diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 18:24:20 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 18:24:20 +0000 |
commit | 483eb2f56657e8e7f419ab1a4fab8dce9ade8609 (patch) | |
tree | e5d88d25d870d5dedacb6bbdbe2a966086a0a5cf /src/cls/rbd | |
parent | Initial commit. (diff) | |
download | ceph-483eb2f56657e8e7f419ab1a4fab8dce9ade8609.tar.xz ceph-483eb2f56657e8e7f419ab1a4fab8dce9ade8609.zip |
Adding upstream version 14.2.21.upstream/14.2.21upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/cls/rbd')
-rw-r--r-- | src/cls/rbd/cls_rbd.cc | 7964 | ||||
-rw-r--r-- | src/cls/rbd/cls_rbd.h | 243 | ||||
-rw-r--r-- | src/cls/rbd/cls_rbd_client.cc | 2807 | ||||
-rw-r--r-- | src/cls/rbd/cls_rbd_client.h | 610 | ||||
-rw-r--r-- | src/cls/rbd/cls_rbd_types.cc | 909 | ||||
-rw-r--r-- | src/cls/rbd/cls_rbd_types.h | 791 |
6 files changed, 13324 insertions, 0 deletions
diff --git a/src/cls/rbd/cls_rbd.cc b/src/cls/rbd/cls_rbd.cc new file mode 100644 index 00000000..09f2e790 --- /dev/null +++ b/src/cls/rbd/cls_rbd.cc @@ -0,0 +1,7964 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +/** \file + * + * This is an OSD class that implements methods for + * use with rbd. + * + * Most of these deal with the rbd header object. Methods prefixed + * with old_ deal with the original rbd design, in which clients read + * and interpreted the header object directly. + * + * The new format is meant to be opaque to clients - all their + * interactions with non-data objects should go through this + * class. The OSD class interface leaves the class to implement its + * own argument and payload serialization/deserialization, so for ease + * of implementation we use the existing ceph encoding/decoding + * methods. Something like json might be preferable, but the rbd + * kernel module has to be able to understand format as well. The + * datatypes exposed to the clients are strings, unsigned integers, + * and vectors of those types. The on-wire format can be found in + * src/include/encoding.h. + * + * The methods for interacting with the new format document their + * parameters as the client sees them - it would be silly to mention + * in each one that they take an input and an output bufferlist. + */ +#include "include/types.h" + +#include <algorithm> +#include <errno.h> +#include <sstream> + +#include "include/uuid.h" +#include "common/bit_vector.hpp" +#include "common/errno.h" +#include "objclass/objclass.h" +#include "osd/osd_types.h" +#include "include/rbd_types.h" +#include "include/rbd/object_map_types.h" + +#include "cls/rbd/cls_rbd.h" +#include "cls/rbd/cls_rbd_types.h" + + +/* + * Object keys: + * + * <partial list> + * + * stripe_unit: size in bytes of the stripe unit. if not present, + * the stripe unit is assumed to match the object size (1 << order). + * + * stripe_count: number of objects to stripe over before looping back. + * if not present or 1, striping is disabled. this is the default. + * + */ + +CLS_VER(2,0) +CLS_NAME(rbd) + +#define RBD_MAX_KEYS_READ 64 +#define RBD_SNAP_KEY_PREFIX "snapshot_" +#define RBD_SNAP_CHILDREN_KEY_PREFIX "snap_children_" +#define RBD_DIR_ID_KEY_PREFIX "id_" +#define RBD_DIR_NAME_KEY_PREFIX "name_" +#define RBD_METADATA_KEY_PREFIX "metadata_" + +namespace { + +uint64_t get_encode_features(cls_method_context_t hctx) { + uint64_t features = 0; + int8_t require_osd_release = cls_get_required_osd_release(hctx); + if (require_osd_release >= CEPH_RELEASE_NAUTILUS) { + features |= CEPH_FEATURE_SERVER_NAUTILUS; + } + return features; +} + +bool calc_sparse_extent(const bufferptr &bp, size_t sparse_size, + uint64_t length, size_t *write_offset, + size_t *write_length, size_t *offset) { + size_t extent_size; + if (*offset + sparse_size > length) { + extent_size = length - *offset; + } else { + extent_size = sparse_size; + } + + bufferptr extent(bp, *offset, extent_size); + *offset += extent_size; + + bool extent_is_zero = extent.is_zero(); + if (!extent_is_zero) { + *write_length += extent_size; + } + if (extent_is_zero && *write_length == 0) { + *write_offset += extent_size; + } + + if ((extent_is_zero || *offset == length) && *write_length != 0) { + return true; + } + return false; +} + +} // anonymous namespace + +static int snap_read_header(cls_method_context_t hctx, bufferlist& bl) +{ + unsigned snap_count = 0; + uint64_t snap_names_len = 0; + struct rbd_obj_header_ondisk *header; + + CLS_LOG(20, "snapshots_list"); + + while (1) { + int len = sizeof(*header) + + snap_count * sizeof(struct rbd_obj_snap_ondisk) + + snap_names_len; + + int rc = cls_cxx_read(hctx, 0, len, &bl); + if (rc < 0) + return rc; + + if (bl.length() < sizeof(*header)) + return -EINVAL; + + header = (struct rbd_obj_header_ondisk *)bl.c_str(); + ceph_assert(header); + + if ((snap_count != header->snap_count) || + (snap_names_len != header->snap_names_len)) { + snap_count = header->snap_count; + snap_names_len = header->snap_names_len; + bl.clear(); + continue; + } + break; + } + + return 0; +} + +static void key_from_snap_id(snapid_t snap_id, string *out) +{ + ostringstream oss; + oss << RBD_SNAP_KEY_PREFIX + << std::setw(16) << std::setfill('0') << std::hex << snap_id; + *out = oss.str(); +} + +static snapid_t snap_id_from_key(const string &key) { + istringstream iss(key); + uint64_t id; + iss.ignore(strlen(RBD_SNAP_KEY_PREFIX)) >> std::hex >> id; + return id; +} + +template<typename T> +static int read_key(cls_method_context_t hctx, const string &key, T *out) +{ + bufferlist bl; + int r = cls_cxx_map_get_val(hctx, key, &bl); + if (r < 0) { + if (r != -ENOENT) { + CLS_ERR("error reading omap key %s: %s", key.c_str(), cpp_strerror(r).c_str()); + } + return r; + } + + try { + auto it = bl.cbegin(); + decode(*out, it); + } catch (const buffer::error &err) { + CLS_ERR("error decoding %s", key.c_str()); + return -EIO; + } + + return 0; +} + +template <typename T> +static int write_key(cls_method_context_t hctx, const string &key, const T &t) { + bufferlist bl; + encode(t, bl); + + int r = cls_cxx_map_set_val(hctx, key, &bl); + if (r < 0) { + CLS_ERR("failed to set omap key: %s", key.c_str()); + return r; + } + return 0; +} + +template <typename T> +static int write_key(cls_method_context_t hctx, const string &key, const T &t, + uint64_t features) { + bufferlist bl; + encode(t, bl, features); + + int r = cls_cxx_map_set_val(hctx, key, &bl); + if (r < 0) { + CLS_ERR("failed to set omap key: %s", key.c_str()); + return r; + } + return 0; +} + +static int remove_key(cls_method_context_t hctx, const string &key) { + int r = cls_cxx_map_remove_key(hctx, key); + if (r < 0 && r != -ENOENT) { + CLS_ERR("failed to remove key: %s", key.c_str()); + return r; + } + return 0; +} + +static bool is_valid_id(const string &id) { + if (!id.size()) + return false; + for (size_t i = 0; i < id.size(); ++i) { + if (!isalnum(id[i])) { + return false; + } + } + return true; +} + +/** + * verify that the header object exists + * + * @return 0 if the object exists, -ENOENT if it does not, or other error + */ +static int check_exists(cls_method_context_t hctx) +{ + uint64_t size; + time_t mtime; + return cls_cxx_stat(hctx, &size, &mtime); +} + +namespace image { + +/** + * check that given feature(s) are set + * + * @param hctx context + * @param need features needed + * @return 0 if features are set, negative error (like ENOEXEC) otherwise + */ +int require_feature(cls_method_context_t hctx, uint64_t need) +{ + uint64_t features; + int r = read_key(hctx, "features", &features); + if (r == -ENOENT) // this implies it's an old-style image with no features + return -ENOEXEC; + if (r < 0) + return r; + if ((features & need) != need) { + CLS_LOG(10, "require_feature missing feature %llx, have %llx", + (unsigned long long)need, (unsigned long long)features); + return -ENOEXEC; + } + return 0; +} + +std::string snap_children_key_from_snap_id(snapid_t snap_id) +{ + ostringstream oss; + oss << RBD_SNAP_CHILDREN_KEY_PREFIX + << std::setw(16) << std::setfill('0') << std::hex << snap_id; + return oss.str(); +} + +int set_op_features(cls_method_context_t hctx, uint64_t op_features, + uint64_t mask) { + uint64_t orig_features; + int r = read_key(hctx, "features", &orig_features); + if (r < 0) { + CLS_ERR("failed to read features off disk: %s", cpp_strerror(r).c_str()); + return r; + } + + uint64_t orig_op_features = 0; + r = read_key(hctx, "op_features", &orig_op_features); + if (r < 0 && r != -ENOENT) { + CLS_ERR("Could not read op features off disk: %s", cpp_strerror(r).c_str()); + return r; + } + + op_features = (orig_op_features & ~mask) | (op_features & mask); + CLS_LOG(10, "op_features=%" PRIu64 " orig_op_features=%" PRIu64, + op_features, orig_op_features); + if (op_features == orig_op_features) { + return 0; + } + + uint64_t features = orig_features; + if (op_features == 0ULL) { + features &= ~RBD_FEATURE_OPERATIONS; + + r = cls_cxx_map_remove_key(hctx, "op_features"); + if (r == -ENOENT) { + r = 0; + } + } else { + features |= RBD_FEATURE_OPERATIONS; + + bufferlist bl; + encode(op_features, bl); + r = cls_cxx_map_set_val(hctx, "op_features", &bl); + } + + if (r < 0) { + CLS_ERR("error updating op features: %s", cpp_strerror(r).c_str()); + return r; + } + + if (features != orig_features) { + bufferlist bl; + encode(features, bl); + r = cls_cxx_map_set_val(hctx, "features", &bl); + if (r < 0) { + CLS_ERR("error updating features: %s", cpp_strerror(r).c_str()); + return r; + } + } + + return 0; +} + +int set_migration(cls_method_context_t hctx, + const cls::rbd::MigrationSpec &migration_spec, bool init) { + if (init) { + bufferlist bl; + int r = cls_cxx_map_get_val(hctx, "migration", &bl); + if (r != -ENOENT) { + if (r == 0) { + CLS_LOG(10, "migration already set"); + return -EEXIST; + } + CLS_ERR("failed to read migration off disk: %s", cpp_strerror(r).c_str()); + return r; + } + + uint64_t features = 0; + r = read_key(hctx, "features", &features); + if (r == -ENOENT) { + CLS_LOG(20, "no features, assuming v1 format"); + bufferlist header; + r = cls_cxx_read(hctx, 0, sizeof(RBD_HEADER_TEXT), &header); + if (r < 0) { + CLS_ERR("failed to read v1 header: %s", cpp_strerror(r).c_str()); + return r; + } + if (header.length() != sizeof(RBD_HEADER_TEXT)) { + CLS_ERR("unrecognized v1 header format"); + return -ENXIO; + } + if (memcmp(RBD_HEADER_TEXT, header.c_str(), header.length()) != 0) { + if (memcmp(RBD_MIGRATE_HEADER_TEXT, header.c_str(), + header.length()) == 0) { + CLS_LOG(10, "migration already set"); + return -EEXIST; + } else { + CLS_ERR("unrecognized v1 header format"); + return -ENXIO; + } + } + if (migration_spec.header_type != cls::rbd::MIGRATION_HEADER_TYPE_SRC) { + CLS_LOG(10, "v1 format image can only be migration source"); + return -EINVAL; + } + + header.clear(); + header.append(RBD_MIGRATE_HEADER_TEXT); + r = cls_cxx_write(hctx, 0, header.length(), &header); + if (r < 0) { + CLS_ERR("error updating v1 header: %s", cpp_strerror(r).c_str()); + return r; + } + } else if (r < 0) { + CLS_ERR("failed to read features off disk: %s", cpp_strerror(r).c_str()); + return r; + } else if ((features & RBD_FEATURE_MIGRATING) != 0ULL) { + if (migration_spec.header_type != cls::rbd::MIGRATION_HEADER_TYPE_DST) { + CLS_LOG(10, "migrating feature already set"); + return -EEXIST; + } + } else { + features |= RBD_FEATURE_MIGRATING; + bl.clear(); + encode(features, bl); + r = cls_cxx_map_set_val(hctx, "features", &bl); + if (r < 0) { + CLS_ERR("error updating features: %s", cpp_strerror(r).c_str()); + return r; + } + } + } + + bufferlist bl; + encode(migration_spec, bl); + int r = cls_cxx_map_set_val(hctx, "migration", &bl); + if (r < 0) { + CLS_ERR("error setting migration: %s", cpp_strerror(r).c_str()); + return r; + } + + return 0; +} + +int read_migration(cls_method_context_t hctx, + cls::rbd::MigrationSpec *migration_spec) { + uint64_t features = 0; + int r = read_key(hctx, "features", &features); + if (r == -ENOENT) { + CLS_LOG(20, "no features, assuming v1 format"); + bufferlist header; + r = cls_cxx_read(hctx, 0, sizeof(RBD_HEADER_TEXT), &header); + if (r < 0) { + CLS_ERR("failed to read v1 header: %s", cpp_strerror(r).c_str()); + return r; + } + if (header.length() != sizeof(RBD_HEADER_TEXT)) { + CLS_ERR("unrecognized v1 header format"); + return -ENXIO; + } + if (memcmp(RBD_MIGRATE_HEADER_TEXT, header.c_str(), header.length()) != 0) { + if (memcmp(RBD_HEADER_TEXT, header.c_str(), header.length()) == 0) { + CLS_LOG(10, "migration feature not set"); + return -EINVAL; + } else { + CLS_ERR("unrecognized v1 header format"); + return -ENXIO; + } + } + if (migration_spec->header_type != cls::rbd::MIGRATION_HEADER_TYPE_SRC) { + CLS_LOG(10, "v1 format image can only be migration source"); + return -EINVAL; + } + } else if (r < 0) { + CLS_ERR("failed to read features off disk: %s", cpp_strerror(r).c_str()); + return r; + } else if ((features & RBD_FEATURE_MIGRATING) == 0ULL) { + CLS_LOG(10, "migration feature not set"); + return -EINVAL; + } + + r = read_key(hctx, "migration", migration_spec); + if (r < 0) { + CLS_ERR("failed to read migration off disk: %s", cpp_strerror(r).c_str()); + return r; + } + + return 0; +} + +int remove_migration(cls_method_context_t hctx) { + int r = remove_key(hctx, "migration"); + if (r < 0) { + return r; + } + + uint64_t features = 0; + r = read_key(hctx, "features", &features); + if (r == -ENOENT) { + CLS_LOG(20, "no features, assuming v1 format"); + bufferlist header; + r = cls_cxx_read(hctx, 0, sizeof(RBD_MIGRATE_HEADER_TEXT), &header); + if (header.length() != sizeof(RBD_MIGRATE_HEADER_TEXT)) { + CLS_ERR("unrecognized v1 header format"); + return -ENXIO; + } + if (memcmp(RBD_MIGRATE_HEADER_TEXT, header.c_str(), header.length()) != 0) { + if (memcmp(RBD_HEADER_TEXT, header.c_str(), header.length()) == 0) { + CLS_LOG(10, "migration feature not set"); + return -EINVAL; + } else { + CLS_ERR("unrecognized v1 header format"); + return -ENXIO; + } + } + header.clear(); + header.append(RBD_HEADER_TEXT); + r = cls_cxx_write(hctx, 0, header.length(), &header); + if (r < 0) { + CLS_ERR("error updating v1 header: %s", cpp_strerror(r).c_str()); + return r; + } + } else if (r < 0) { + CLS_ERR("failed to read features off disk: %s", cpp_strerror(r).c_str()); + return r; + } else if ((features & RBD_FEATURE_MIGRATING) == 0ULL) { + CLS_LOG(10, "migrating feature not set"); + } else { + features &= ~RBD_FEATURE_MIGRATING; + bufferlist bl; + encode(features, bl); + r = cls_cxx_map_set_val(hctx, "features", &bl); + if (r < 0) { + CLS_ERR("error updating features: %s", cpp_strerror(r).c_str()); + return r; + } + } + + return 0; +} + +namespace snapshot { + +template<typename L> +int iterate(cls_method_context_t hctx, L& lambda) { + int max_read = RBD_MAX_KEYS_READ; + string last_read = RBD_SNAP_KEY_PREFIX; + bool more = false; + do { + map<string, bufferlist> vals; + int r = cls_cxx_map_get_vals(hctx, last_read, RBD_SNAP_KEY_PREFIX, + max_read, &vals, &more); + if (r < 0) { + return r; + } + + cls_rbd_snap snap_meta; + for (auto& val : vals) { + auto iter = val.second.cbegin(); + try { + decode(snap_meta, iter); + } catch (const buffer::error &err) { + CLS_ERR("error decoding snapshot metadata for snap : %s", + val.first.c_str()); + return -EIO; + } + + r = lambda(snap_meta); + if (r < 0) { + return r; + } + } + + if (!vals.empty()) { + last_read = vals.rbegin()->first; + } + } while (more); + + return 0; +} + +int write(cls_method_context_t hctx, const std::string& snap_key, + cls_rbd_snap&& snap) { + int r; + uint64_t encode_features = get_encode_features(hctx); + if (snap.migrate_parent_format(encode_features)) { + // ensure the normalized parent link exists before removing it from the + // snapshot record + cls_rbd_parent on_disk_parent; + r = read_key(hctx, "parent", &on_disk_parent); + if (r < 0 && r != -ENOENT) { + return r; + } + + if (!on_disk_parent.exists()) { + on_disk_parent = snap.parent; + on_disk_parent.head_overlap = std::nullopt; + + r = write_key(hctx, "parent", on_disk_parent, encode_features); + if (r < 0) { + return r; + } + } + + // only store the parent overlap in the snapshot + snap.parent_overlap = snap.parent.head_overlap; + snap.parent = {}; + } + + r = write_key(hctx, snap_key, snap, encode_features); + if (r < 0) { + return r; + } + return 0; +} + +} // namespace snapshot + +namespace parent { + +int attach(cls_method_context_t hctx, cls_rbd_parent parent, + bool reattach) { + int r = check_exists(hctx); + if (r < 0) { + CLS_LOG(20, "cls_rbd::image::parent::attach: child doesn't exist"); + return r; + } + + r = image::require_feature(hctx, RBD_FEATURE_LAYERING); + if (r < 0) { + CLS_LOG(20, "cls_rbd::image::parent::attach: child does not support " + "layering"); + return r; + } + + CLS_LOG(20, "cls_rbd::image::parent::attach: pool=%" PRIi64 ", ns=%s, id=%s, " + "snapid=%" PRIu64 ", size=%" PRIu64, + parent.pool_id, parent.pool_namespace.c_str(), + parent.image_id.c_str(), parent.snap_id.val, + parent.head_overlap.value_or(0ULL)); + if (!parent.exists() || parent.head_overlap.value_or(0ULL) == 0ULL) { + return -EINVAL; + } + + // make sure there isn't already a parent + cls_rbd_parent on_disk_parent; + r = read_key(hctx, "parent", &on_disk_parent); + if (r < 0 && r != -ENOENT) { + return r; + } + + auto on_disk_parent_without_overlap{on_disk_parent}; + on_disk_parent_without_overlap.head_overlap = parent.head_overlap; + + if (r == 0 && + (on_disk_parent.head_overlap || + on_disk_parent_without_overlap != parent) && + !reattach) { + CLS_LOG(20, "cls_rbd::parent::attach: existing legacy parent " + "pool=%" PRIi64 ", ns=%s, id=%s, snapid=%" PRIu64 ", " + "overlap=%" PRIu64, + on_disk_parent.pool_id, on_disk_parent.pool_namespace.c_str(), + on_disk_parent.image_id.c_str(), on_disk_parent.snap_id.val, + on_disk_parent.head_overlap.value_or(0ULL)); + return -EEXIST; + } + + // our overlap is the min of our size and the parent's size. + uint64_t our_size; + r = read_key(hctx, "size", &our_size); + if (r < 0) { + return r; + } + + parent.head_overlap = std::min(*parent.head_overlap, our_size); + + r = write_key(hctx, "parent", parent, get_encode_features(hctx)); + if (r < 0) { + return r; + } + + return 0; +} + +int detach(cls_method_context_t hctx, bool legacy_api) { + int r = check_exists(hctx); + if (r < 0) { + CLS_LOG(20, "cls_rbd::parent::detach: child doesn't exist"); + return r; + } + + uint64_t features; + r = read_key(hctx, "features", &features); + if (r == -ENOENT || ((features & RBD_FEATURE_LAYERING) == 0)) { + CLS_LOG(20, "cls_rbd::image::parent::detach: child does not support " + "layering"); + return -ENOEXEC; + } else if (r < 0) { + return r; + } + + cls_rbd_parent on_disk_parent; + r = read_key(hctx, "parent", &on_disk_parent); + if (r < 0) { + return r; + } else if (legacy_api && !on_disk_parent.pool_namespace.empty()) { + return -EXDEV; + } else if (!on_disk_parent.head_overlap) { + return -ENOENT; + } + + auto detach_lambda = [hctx, features](const cls_rbd_snap& snap_meta) { + if (snap_meta.parent.pool_id != -1 || snap_meta.parent_overlap) { + if ((features & RBD_FEATURE_DEEP_FLATTEN) != 0ULL) { + // remove parent reference from snapshot + cls_rbd_snap snap_meta_copy = snap_meta; + snap_meta_copy.parent = {}; + snap_meta_copy.parent_overlap = std::nullopt; + + std::string snap_key; + key_from_snap_id(snap_meta_copy.id, &snap_key); + int r = snapshot::write(hctx, snap_key, std::move(snap_meta_copy)); + if (r < 0) { + return r; + } + } else { + return -EEXIST; + } + } + return 0; + }; + + r = snapshot::iterate(hctx, detach_lambda); + bool has_child_snaps = (r == -EEXIST); + if (r < 0 && r != -EEXIST) { + return r; + } + + int8_t require_osd_release = cls_get_required_osd_release(hctx); + if (has_child_snaps && require_osd_release >= CEPH_RELEASE_NAUTILUS) { + // remove overlap from HEAD revision but keep spec for snapshots + on_disk_parent.head_overlap = std::nullopt; + r = write_key(hctx, "parent", on_disk_parent, get_encode_features(hctx)); + if (r < 0) { + return r; + } + } else { + r = remove_key(hctx, "parent"); + if (r < 0 && r != -ENOENT) { + return r; + } + } + + if (!has_child_snaps) { + // disable clone child op feature if no longer associated + r = set_op_features(hctx, 0, RBD_OPERATION_FEATURE_CLONE_CHILD); + if (r < 0) { + return r; + } + } + return 0; +} + +} // namespace parent +} // namespace image + +/** + * Initialize the header with basic metadata. + * Extra features may initialize more fields in the future. + * Everything is stored as key/value pairs as omaps in the header object. + * + * If features the OSD does not understand are requested, -ENOSYS is + * returned. + * + * Input: + * @param size number of bytes in the image (uint64_t) + * @param order bits to shift to determine the size of data objects (uint8_t) + * @param features what optional things this image will use (uint64_t) + * @param object_prefix a prefix for all the data objects + * @param data_pool_id pool id where data objects is stored (int64_t) + * + * Output: + * @return 0 on success, negative error code on failure + */ +int create(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + string object_prefix; + uint64_t features, size; + uint8_t order; + int64_t data_pool_id = -1; + + try { + auto iter = in->cbegin(); + decode(size, iter); + decode(order, iter); + decode(features, iter); + decode(object_prefix, iter); + if (!iter.end()) { + decode(data_pool_id, iter); + } + } catch (const buffer::error &err) { + return -EINVAL; + } + + CLS_LOG(20, "create object_prefix=%s size=%llu order=%u features=%llu", + object_prefix.c_str(), (unsigned long long)size, order, + (unsigned long long)features); + + if (features & ~RBD_FEATURES_ALL) { + return -ENOSYS; + } + + if (!object_prefix.size()) { + return -EINVAL; + } + + bufferlist stored_prefixbl; + int r = cls_cxx_map_get_val(hctx, "object_prefix", &stored_prefixbl); + if (r != -ENOENT) { + CLS_ERR("reading object_prefix returned %d", r); + return -EEXIST; + } + + bufferlist sizebl; + bufferlist orderbl; + bufferlist featuresbl; + bufferlist object_prefixbl; + bufferlist snap_seqbl; + bufferlist timestampbl; + uint64_t snap_seq = 0; + utime_t timestamp = ceph_clock_now(); + encode(size, sizebl); + encode(order, orderbl); + encode(features, featuresbl); + encode(object_prefix, object_prefixbl); + encode(snap_seq, snap_seqbl); + encode(timestamp, timestampbl); + + map<string, bufferlist> omap_vals; + omap_vals["size"] = sizebl; + omap_vals["order"] = orderbl; + omap_vals["features"] = featuresbl; + omap_vals["object_prefix"] = object_prefixbl; + omap_vals["snap_seq"] = snap_seqbl; + omap_vals["create_timestamp"] = timestampbl; + omap_vals["access_timestamp"] = timestampbl; + omap_vals["modify_timestamp"] = timestampbl; + + if ((features & RBD_FEATURE_OPERATIONS) != 0ULL) { + CLS_ERR("Attempting to set internal feature: operations"); + return -EINVAL; + } + + if (features & RBD_FEATURE_DATA_POOL) { + if (data_pool_id == -1) { + CLS_ERR("data pool not provided with feature enabled"); + return -EINVAL; + } + + bufferlist data_pool_id_bl; + encode(data_pool_id, data_pool_id_bl); + omap_vals["data_pool_id"] = data_pool_id_bl; + } else if (data_pool_id != -1) { + CLS_ERR("data pool provided with feature disabled"); + return -EINVAL; + } + + r = cls_cxx_map_set_vals(hctx, &omap_vals); + if (r < 0) + return r; + + return 0; +} + +/** + * Input: + * @param snap_id which snapshot to query, or CEPH_NOSNAP (uint64_t) (deprecated) + * @param read_only true if the image will be used read-only (bool) + * + * Output: + * @param features list of enabled features for the given snapshot (uint64_t) + * @param incompatible incompatible feature bits + * @returns 0 on success, negative error code on failure + */ +int get_features(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + bool read_only = false; + + auto iter = in->cbegin(); + try { + uint64_t snap_id; + decode(snap_id, iter); + if (!iter.end()) { + decode(read_only, iter); + } + } catch (const buffer::error &err) { + return -EINVAL; + } + + CLS_LOG(20, "get_features read_only=%d", read_only); + + uint64_t features; + int r = read_key(hctx, "features", &features); + if (r < 0) { + CLS_ERR("failed to read features off disk: %s", cpp_strerror(r).c_str()); + return r; + } + + uint64_t incompatible = (read_only ? features & RBD_FEATURES_INCOMPATIBLE : + features & RBD_FEATURES_RW_INCOMPATIBLE); + encode(features, *out); + encode(incompatible, *out); + return 0; +} + +/** + * set the image features + * + * Input: + * @param features image features + * @param mask image feature mask + * + * Output: + * none + * + * @returns 0 on success, negative error code upon failure + */ +int set_features(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + uint64_t features; + uint64_t mask; + auto iter = in->cbegin(); + try { + decode(features, iter); + decode(mask, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + // check that features exists to make sure this is a header object + // that was created correctly + uint64_t orig_features = 0; + int r = read_key(hctx, "features", &orig_features); + if (r < 0 && r != -ENOENT) { + CLS_ERR("Could not read image's features off disk: %s", + cpp_strerror(r).c_str()); + return r; + } + + if ((mask & RBD_FEATURES_INTERNAL) != 0ULL) { + CLS_ERR("Attempting to set internal feature: %" PRIu64, + static_cast<uint64_t>(mask & RBD_FEATURES_INTERNAL)); + return -EINVAL; + } + + // newer clients might attempt to mask off features we don't support + mask &= RBD_FEATURES_ALL; + + uint64_t enabled_features = features & mask; + if ((enabled_features & RBD_FEATURES_MUTABLE) != enabled_features) { + CLS_ERR("Attempting to enable immutable feature: %" PRIu64, + static_cast<uint64_t>(enabled_features & ~RBD_FEATURES_MUTABLE)); + return -EINVAL; + } + + uint64_t disabled_features = ~features & mask; + uint64_t disable_mask = (RBD_FEATURES_MUTABLE | RBD_FEATURES_DISABLE_ONLY); + if ((disabled_features & disable_mask) != disabled_features) { + CLS_ERR("Attempting to disable immutable feature: %" PRIu64, + enabled_features & ~disable_mask); + return -EINVAL; + } + + features = (orig_features & ~mask) | (features & mask); + CLS_LOG(10, "set_features features=%" PRIu64 " orig_features=%" PRIu64, + features, orig_features); + + bufferlist bl; + encode(features, bl); + r = cls_cxx_map_set_val(hctx, "features", &bl); + if (r < 0) { + CLS_ERR("error updating features: %s", cpp_strerror(r).c_str()); + return r; + } + return 0; +} + +/** + * Input: + * @param snap_id which snapshot to query, or CEPH_NOSNAP (uint64_t) + * + * Output: + * @param order bits to shift to get the size of data objects (uint8_t) + * @param size size of the image in bytes for the given snapshot (uint64_t) + * @returns 0 on success, negative error code on failure + */ +int get_size(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + uint64_t snap_id, size; + uint8_t order; + + auto iter = in->cbegin(); + try { + decode(snap_id, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + CLS_LOG(20, "get_size snap_id=%llu", (unsigned long long)snap_id); + + int r = read_key(hctx, "order", &order); + if (r < 0) { + CLS_ERR("failed to read the order off of disk: %s", cpp_strerror(r).c_str()); + return r; + } + + if (snap_id == CEPH_NOSNAP) { + r = read_key(hctx, "size", &size); + if (r < 0) { + CLS_ERR("failed to read the image's size off of disk: %s", cpp_strerror(r).c_str()); + return r; + } + } else { + cls_rbd_snap snap; + string snapshot_key; + key_from_snap_id(snap_id, &snapshot_key); + int r = read_key(hctx, snapshot_key, &snap); + if (r < 0) + return r; + + size = snap.image_size; + } + + encode(order, *out); + encode(size, *out); + + return 0; +} + +/** + * Input: + * @param size new capacity of the image in bytes (uint64_t) + * + * Output: + * @returns 0 on success, negative error code on failure + */ +int set_size(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + uint64_t size; + + auto iter = in->cbegin(); + try { + decode(size, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + // check that size exists to make sure this is a header object + // that was created correctly + uint64_t orig_size; + int r = read_key(hctx, "size", &orig_size); + if (r < 0) { + CLS_ERR("Could not read image's size off disk: %s", cpp_strerror(r).c_str()); + return r; + } + + CLS_LOG(20, "set_size size=%llu orig_size=%llu", (unsigned long long)size, + (unsigned long long)orig_size); + + bufferlist sizebl; + encode(size, sizebl); + r = cls_cxx_map_set_val(hctx, "size", &sizebl); + if (r < 0) { + CLS_ERR("error writing snapshot metadata: %s", cpp_strerror(r).c_str()); + return r; + } + + // if we are shrinking, and have a parent, shrink our overlap with + // the parent, too. + if (size < orig_size) { + cls_rbd_parent parent; + r = read_key(hctx, "parent", &parent); + if (r == -ENOENT) + r = 0; + if (r < 0) + return r; + if (parent.exists() && parent.head_overlap.value_or(0ULL) > size) { + parent.head_overlap = size; + r = write_key(hctx, "parent", parent, get_encode_features(hctx)); + if (r < 0) { + return r; + } + } + } + + return 0; +} + +/** + * get the current protection status of the specified snapshot + * + * Input: + * @param snap_id (uint64_t) which snapshot to get the status of + * + * Output: + * @param status (uint8_t) one of: + * RBD_PROTECTION_STATUS_{PROTECTED, UNPROTECTED, UNPROTECTING} + * + * @returns 0 on success, negative error code on failure + * @returns -EINVAL if snapid is CEPH_NOSNAP + */ +int get_protection_status(cls_method_context_t hctx, bufferlist *in, + bufferlist *out) +{ + snapid_t snap_id; + + auto iter = in->cbegin(); + try { + decode(snap_id, iter); + } catch (const buffer::error &err) { + CLS_LOG(20, "get_protection_status: invalid decode"); + return -EINVAL; + } + + int r = check_exists(hctx); + if (r < 0) + return r; + + CLS_LOG(20, "get_protection_status snap_id=%llu", + (unsigned long long)snap_id.val); + + if (snap_id == CEPH_NOSNAP) + return -EINVAL; + + cls_rbd_snap snap; + string snapshot_key; + key_from_snap_id(snap_id.val, &snapshot_key); + r = read_key(hctx, snapshot_key, &snap); + if (r < 0) { + CLS_ERR("could not read key for snapshot id %" PRIu64, snap_id.val); + return r; + } + + if (snap.protection_status >= RBD_PROTECTION_STATUS_LAST) { + CLS_ERR("invalid protection status for snap id %llu: %u", + (unsigned long long)snap_id.val, snap.protection_status); + return -EIO; + } + + encode(snap.protection_status, *out); + return 0; +} + +/** + * set the proctection status of a snapshot + * + * Input: + * @param snapid (uint64_t) which snapshot to set the status of + * @param status (uint8_t) one of: + * RBD_PROTECTION_STATUS_{PROTECTED, UNPROTECTED, UNPROTECTING} + * + * @returns 0 on success, negative error code on failure + * @returns -EINVAL if snapid is CEPH_NOSNAP + */ +int set_protection_status(cls_method_context_t hctx, bufferlist *in, + bufferlist *out) +{ + snapid_t snap_id; + uint8_t status; + + auto iter = in->cbegin(); + try { + decode(snap_id, iter); + decode(status, iter); + } catch (const buffer::error &err) { + CLS_LOG(20, "set_protection_status: invalid decode"); + return -EINVAL; + } + + int r = check_exists(hctx); + if (r < 0) + return r; + + r = image::require_feature(hctx, RBD_FEATURE_LAYERING); + if (r < 0) { + CLS_LOG(20, "image does not support layering"); + return r; + } + + CLS_LOG(20, "set_protection_status snapid=%llu status=%u", + (unsigned long long)snap_id.val, status); + + if (snap_id == CEPH_NOSNAP) + return -EINVAL; + + if (status >= RBD_PROTECTION_STATUS_LAST) { + CLS_LOG(10, "invalid protection status for snap id %llu: %u", + (unsigned long long)snap_id.val, status); + return -EINVAL; + } + + cls_rbd_snap snap; + string snapshot_key; + key_from_snap_id(snap_id.val, &snapshot_key); + r = read_key(hctx, snapshot_key, &snap); + if (r < 0) { + CLS_ERR("could not read key for snapshot id %" PRIu64, snap_id.val); + return r; + } + + snap.protection_status = status; + r = image::snapshot::write(hctx, snapshot_key, std::move(snap)); + if (r < 0) { + return r; + } + + return 0; +} + +/** + * get striping parameters + * + * Input: + * none + * + * Output: + * @param stripe unit (bytes) + * @param stripe count (num objects) + * + * @returns 0 on success + */ +int get_stripe_unit_count(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + int r = check_exists(hctx); + if (r < 0) + return r; + + CLS_LOG(20, "get_stripe_unit_count"); + + r = image::require_feature(hctx, RBD_FEATURE_STRIPINGV2); + if (r < 0) + return r; + + uint64_t stripe_unit = 0, stripe_count = 0; + r = read_key(hctx, "stripe_unit", &stripe_unit); + if (r == -ENOENT) { + // default to object size + uint8_t order; + r = read_key(hctx, "order", &order); + if (r < 0) { + CLS_ERR("failed to read the order off of disk: %s", cpp_strerror(r).c_str()); + return -EIO; + } + stripe_unit = 1ull << order; + } + if (r < 0) + return r; + r = read_key(hctx, "stripe_count", &stripe_count); + if (r == -ENOENT) { + // default to 1 + stripe_count = 1; + r = 0; + } + if (r < 0) + return r; + + encode(stripe_unit, *out); + encode(stripe_count, *out); + return 0; +} + +/** + * set striping parameters + * + * Input: + * @param stripe unit (bytes) + * @param stripe count (num objects) + * + * @returns 0 on success + */ +int set_stripe_unit_count(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + uint64_t stripe_unit, stripe_count; + + auto iter = in->cbegin(); + try { + decode(stripe_unit, iter); + decode(stripe_count, iter); + } catch (const buffer::error &err) { + CLS_LOG(20, "set_stripe_unit_count: invalid decode"); + return -EINVAL; + } + + if (!stripe_count || !stripe_unit) + return -EINVAL; + + int r = check_exists(hctx); + if (r < 0) + return r; + + CLS_LOG(20, "set_stripe_unit_count"); + + r = image::require_feature(hctx, RBD_FEATURE_STRIPINGV2); + if (r < 0) + return r; + + uint8_t order; + r = read_key(hctx, "order", &order); + if (r < 0) { + CLS_ERR("failed to read the order off of disk: %s", cpp_strerror(r).c_str()); + return r; + } + if ((1ull << order) % stripe_unit || stripe_unit > (1ull << order)) { + CLS_ERR("stripe unit %llu is not a factor of the object size %llu", + (unsigned long long)stripe_unit, 1ull << order); + return -EINVAL; + } + + bufferlist bl, bl2; + encode(stripe_unit, bl); + r = cls_cxx_map_set_val(hctx, "stripe_unit", &bl); + if (r < 0) { + CLS_ERR("error writing stripe_unit metadata: %s", cpp_strerror(r).c_str()); + return r; + } + + encode(stripe_count, bl2); + r = cls_cxx_map_set_val(hctx, "stripe_count", &bl2); + if (r < 0) { + CLS_ERR("error writing stripe_count metadata: %s", cpp_strerror(r).c_str()); + return r; + } + + return 0; +} + +int get_create_timestamp(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + CLS_LOG(20, "get_create_timestamp"); + + utime_t timestamp; + bufferlist bl; + int r = cls_cxx_map_get_val(hctx, "create_timestamp", &bl); + if (r < 0) { + if (r != -ENOENT) { + CLS_ERR("error reading create_timestamp: %s", cpp_strerror(r).c_str()); + return r; + } + } else { + try { + auto it = bl.cbegin(); + decode(timestamp, it); + } catch (const buffer::error &err) { + CLS_ERR("could not decode create_timestamp"); + return -EIO; + } + } + + encode(timestamp, *out); + return 0; +} + +/** + * get the image access timestamp + * + * Input: + * @param none + * + * Output: + * @param timestamp the image access timestamp + * + * @returns 0 on success, negative error code upon failure + */ +int get_access_timestamp(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + CLS_LOG(20, "get_access_timestamp"); + + utime_t timestamp; + bufferlist bl; + int r = cls_cxx_map_get_val(hctx, "access_timestamp", &bl); + if (r < 0) { + if (r != -ENOENT) { + CLS_ERR("error reading access_timestamp: %s", cpp_strerror(r).c_str()); + return r; + } + } else { + try { + auto it = bl.cbegin(); + decode(timestamp, it); + } catch (const buffer::error &err) { + CLS_ERR("could not decode access_timestamp"); + return -EIO; + } + } + + encode(timestamp, *out); + return 0; +} + +/** + * get the image modify timestamp + * + * Input: + * @param none + * + * Output: + * @param timestamp the image modify timestamp + * + * @returns 0 on success, negative error code upon failure + */ +int get_modify_timestamp(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + CLS_LOG(20, "get_modify_timestamp"); + + utime_t timestamp; + bufferlist bl; + int r = cls_cxx_map_get_val(hctx, "modify_timestamp", &bl); + if (r < 0) { + if (r != -ENOENT) { + CLS_ERR("error reading modify_timestamp: %s", cpp_strerror(r).c_str()); + return r; + } + } else { + try { + auto it = bl.cbegin(); + decode(timestamp, it); + } catch (const buffer::error &err) { + CLS_ERR("could not decode modify_timestamp"); + return -EIO; + } + } + + encode(timestamp, *out); + return 0; +} + + +/** + * get the image flags + * + * Input: + * @param snap_id which snapshot to query, to CEPH_NOSNAP (uint64_t) + * + * Output: + * @param flags image flags + * + * @returns 0 on success, negative error code upon failure + */ +int get_flags(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + uint64_t snap_id; + auto iter = in->cbegin(); + try { + decode(snap_id, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + CLS_LOG(20, "get_flags snap_id=%llu", (unsigned long long)snap_id); + + uint64_t flags = 0; + if (snap_id == CEPH_NOSNAP) { + int r = read_key(hctx, "flags", &flags); + if (r < 0 && r != -ENOENT) { + CLS_ERR("failed to read flags off disk: %s", cpp_strerror(r).c_str()); + return r; + } + } else { + cls_rbd_snap snap; + string snapshot_key; + key_from_snap_id(snap_id, &snapshot_key); + int r = read_key(hctx, snapshot_key, &snap); + if (r < 0) { + return r; + } + flags = snap.flags; + } + + encode(flags, *out); + return 0; +} + +/** + * set the image flags + * + * Input: + * @param flags image flags + * @param mask image flag mask + * @param snap_id which snapshot to update, or CEPH_NOSNAP (uint64_t) + * + * Output: + * none + * + * @returns 0 on success, negative error code upon failure + */ +int set_flags(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + uint64_t flags; + uint64_t mask; + uint64_t snap_id = CEPH_NOSNAP; + auto iter = in->cbegin(); + try { + decode(flags, iter); + decode(mask, iter); + if (!iter.end()) { + decode(snap_id, iter); + } + } catch (const buffer::error &err) { + return -EINVAL; + } + + // check that size exists to make sure this is a header object + // that was created correctly + int r; + uint64_t orig_flags = 0; + cls_rbd_snap snap_meta; + string snap_meta_key; + if (snap_id == CEPH_NOSNAP) { + r = read_key(hctx, "flags", &orig_flags); + if (r < 0 && r != -ENOENT) { + CLS_ERR("Could not read image's flags off disk: %s", + cpp_strerror(r).c_str()); + return r; + } + } else { + key_from_snap_id(snap_id, &snap_meta_key); + r = read_key(hctx, snap_meta_key, &snap_meta); + if (r < 0) { + CLS_ERR("Could not read snapshot: snap_id=%" PRIu64 ": %s", + snap_id, cpp_strerror(r).c_str()); + return r; + } + orig_flags = snap_meta.flags; + } + + flags = (orig_flags & ~mask) | (flags & mask); + CLS_LOG(20, "set_flags snap_id=%" PRIu64 ", orig_flags=%" PRIu64 ", " + "new_flags=%" PRIu64 ", mask=%" PRIu64, snap_id, orig_flags, + flags, mask); + + if (snap_id == CEPH_NOSNAP) { + r = write_key(hctx, "flags", flags); + } else { + snap_meta.flags = flags; + r = image::snapshot::write(hctx, snap_meta_key, std::move(snap_meta)); + } + + if (r < 0) { + return r; + } + return 0; +} + +/** + * Get the operation-based image features + * + * Input: + * + * Output: + * @param bitmask of enabled op features (uint64_t) + * @returns 0 on success, negative error code on failure + */ +int op_features_get(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + CLS_LOG(20, "op_features_get"); + + uint64_t op_features = 0; + int r = read_key(hctx, "op_features", &op_features); + if (r < 0 && r != -ENOENT) { + CLS_ERR("failed to read op features off disk: %s", cpp_strerror(r).c_str()); + return r; + } + + encode(op_features, *out); + return 0; +} + +/** + * Set the operation-based image features + * + * Input: + * @param op_features image op features + * @param mask image op feature mask + * + * Output: + * none + * + * @returns 0 on success, negative error code upon failure + */ +int op_features_set(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + uint64_t op_features; + uint64_t mask; + auto iter = in->cbegin(); + try { + decode(op_features, iter); + decode(mask, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + uint64_t unsupported_op_features = (mask & ~RBD_OPERATION_FEATURES_ALL); + if (unsupported_op_features != 0ULL) { + CLS_ERR("unsupported op features: %" PRIu64, unsupported_op_features); + return -EINVAL; + } + + return image::set_op_features(hctx, op_features, mask); +} + +/** + * get the current parent, if any + * + * Input: + * @param snap_id which snapshot to query, or CEPH_NOSNAP (uint64_t) + * + * Output: + * @param pool parent pool id (-1 if parent does not exist) + * @param image parent image id + * @param snapid parent snapid + * @param size portion of parent mapped under the child + * + * @returns 0 on success or parent does not exist, negative error code on failure + */ +int get_parent(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + uint64_t snap_id; + + auto iter = in->cbegin(); + try { + decode(snap_id, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + int r = check_exists(hctx); + if (r < 0) { + return r; + } + + CLS_LOG(20, "get_parent snap_id=%" PRIu64, snap_id); + + cls_rbd_parent parent; + r = image::require_feature(hctx, RBD_FEATURE_LAYERING); + if (r == 0) { + r = read_key(hctx, "parent", &parent); + if (r < 0 && r != -ENOENT) { + return r; + } else if (!parent.pool_namespace.empty()) { + return -EXDEV; + } + + if (snap_id != CEPH_NOSNAP) { + cls_rbd_snap snap; + std::string snapshot_key; + key_from_snap_id(snap_id, &snapshot_key); + r = read_key(hctx, snapshot_key, &snap); + if (r < 0 && r != -ENOENT) { + return r; + } + + if (snap.parent.exists()) { + // legacy format where full parent spec is written within + // each snapshot record + parent = snap.parent; + } else if (snap.parent_overlap) { + // normalized parent reference + if (!parent.exists()) { + CLS_ERR("get_parent: snap_id=%" PRIu64 ": invalid parent spec", + snap_id); + return -EINVAL; + } + parent.head_overlap = *snap.parent_overlap; + } else { + // snapshot doesn't have associated parent + parent = {}; + } + } + } + + encode(parent.pool_id, *out); + encode(parent.image_id, *out); + encode(parent.snap_id, *out); + encode(parent.head_overlap.value_or(0ULL), *out); + return 0; +} + +/** + * set the image parent + * + * Input: + * @param pool parent pool + * @param id parent image id + * @param snapid parent snapid + * @param size parent size + * + * @returns 0 on success, or negative error code + */ +int set_parent(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + cls_rbd_parent parent; + auto iter = in->cbegin(); + try { + decode(parent.pool_id, iter); + decode(parent.image_id, iter); + decode(parent.snap_id, iter); + + uint64_t overlap; + decode(overlap, iter); + parent.head_overlap = overlap; + } catch (const buffer::error &err) { + CLS_LOG(20, "cls_rbd::set_parent: invalid decode"); + return -EINVAL; + } + + int r = image::parent::attach(hctx, parent, false); + if (r < 0) { + return r; + } + + return 0; +} + + +/** + * remove the parent pointer + * + * This can only happen on the head, not on a snapshot. No arguments. + * + * @returns 0 on success, negative error code on failure. + */ +int remove_parent(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + int r = image::parent::detach(hctx, true); + if (r < 0) { + return r; + } + + return 0; +} + +/** + * Input: + * none + * + * Output: + * @param parent spec (cls::rbd::ParentImageSpec) + * @returns 0 on success, negative error code on failure + */ +int parent_get(cls_method_context_t hctx, bufferlist *in, bufferlist *out) { + int r = check_exists(hctx); + if (r < 0) { + return r; + } + + CLS_LOG(20, "parent_get"); + + cls_rbd_parent parent; + r = image::require_feature(hctx, RBD_FEATURE_LAYERING); + if (r == 0) { + r = read_key(hctx, "parent", &parent); + if (r < 0 && r != -ENOENT) { + return r; + } else if (r == -ENOENT) { + // examine oldest snapshot to see if it has a denormalized parent + auto parent_lambda = [hctx, &parent](const cls_rbd_snap& snap_meta) { + if (snap_meta.parent.exists()) { + parent = snap_meta.parent; + } + return 0; + }; + + r = image::snapshot::iterate(hctx, parent_lambda); + if (r < 0) { + return r; + } + } + } + + cls::rbd::ParentImageSpec parent_image_spec{ + parent.pool_id, parent.pool_namespace, parent.image_id, + parent.snap_id}; + encode(parent_image_spec, *out); + return 0; +} + +/** + * Input: + * @param snap id (uint64_t) parent snapshot id + * + * Output: + * @param byte overlap of parent image (std::optional<uint64_t>) + * @returns 0 on success, negative error code on failure + */ +int parent_overlap_get(cls_method_context_t hctx, bufferlist *in, + bufferlist *out) { + uint64_t snap_id; + auto iter = in->cbegin(); + try { + decode(snap_id, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + int r = check_exists(hctx); + CLS_LOG(20, "parent_overlap_get"); + + std::optional<uint64_t> parent_overlap = std::nullopt; + r = image::require_feature(hctx, RBD_FEATURE_LAYERING); + if (r == 0) { + if (snap_id == CEPH_NOSNAP) { + cls_rbd_parent parent; + r = read_key(hctx, "parent", &parent); + if (r < 0 && r != -ENOENT) { + return r; + } else if (r == 0) { + parent_overlap = parent.head_overlap; + } + } else { + cls_rbd_snap snap; + std::string snapshot_key; + key_from_snap_id(snap_id, &snapshot_key); + r = read_key(hctx, snapshot_key, &snap); + if (r < 0) { + return r; + } + + if (snap.parent_overlap) { + parent_overlap = snap.parent_overlap; + } else if (snap.parent.exists()) { + // legacy format where full parent spec is written within + // each snapshot record + parent_overlap = snap.parent.head_overlap; + } + } + }; + + encode(parent_overlap, *out); + return 0; +} + +/** + * Input: + * @param parent spec (cls::rbd::ParentImageSpec) + * @param size parent size (uint64_t) + * + * Output: + * @returns 0 on success, negative error code on failure + */ +int parent_attach(cls_method_context_t hctx, bufferlist *in, bufferlist *out) { + cls::rbd::ParentImageSpec parent_image_spec; + uint64_t parent_overlap; + bool reattach = false; + + auto iter = in->cbegin(); + try { + decode(parent_image_spec, iter); + decode(parent_overlap, iter); + if (!iter.end()) { + decode(reattach, iter); + } + } catch (const buffer::error &err) { + CLS_LOG(20, "cls_rbd::parent_attach: invalid decode"); + return -EINVAL; + } + + int r = image::parent::attach(hctx, {parent_image_spec, parent_overlap}, + reattach); + if (r < 0) { + return r; + } + + return 0; +} + +/** + * Input: + * none + * + * Output: + * @returns 0 on success, negative error code on failure + */ +int parent_detach(cls_method_context_t hctx, bufferlist *in, bufferlist *out) { + int r = image::parent::detach(hctx, false); + if (r < 0) { + return r; + } + + return 0; +} + + +/** + * methods for dealing with rbd_children object + */ + +static int decode_parent_common(bufferlist::const_iterator& it, uint64_t *pool_id, + string *image_id, snapid_t *snap_id) +{ + try { + decode(*pool_id, it); + decode(*image_id, it); + decode(*snap_id, it); + } catch (const buffer::error &err) { + CLS_ERR("error decoding parent spec"); + return -EINVAL; + } + return 0; +} + +static int decode_parent(bufferlist *in, uint64_t *pool_id, + string *image_id, snapid_t *snap_id) +{ + auto it = in->cbegin(); + return decode_parent_common(it, pool_id, image_id, snap_id); +} + +static int decode_parent_and_child(bufferlist *in, uint64_t *pool_id, + string *image_id, snapid_t *snap_id, + string *c_image_id) +{ + auto it = in->cbegin(); + int r = decode_parent_common(it, pool_id, image_id, snap_id); + if (r < 0) + return r; + try { + decode(*c_image_id, it); + } catch (const buffer::error &err) { + CLS_ERR("error decoding child image id"); + return -EINVAL; + } + return 0; +} + +static string parent_key(uint64_t pool_id, string image_id, snapid_t snap_id) +{ + bufferlist key_bl; + encode(pool_id, key_bl); + encode(image_id, key_bl); + encode(snap_id, key_bl); + return string(key_bl.c_str(), key_bl.length()); +} + +/** + * add child to rbd_children directory object + * + * rbd_children is a map of (p_pool_id, p_image_id, p_snap_id) to + * [c_image_id, [c_image_id ... ]] + * + * Input: + * @param p_pool_id parent pool id + * @param p_image_id parent image oid + * @param p_snap_id parent snapshot id + * @param c_image_id new child image oid to add + * + * @returns 0 on success, negative error on failure + */ + +int add_child(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + int r; + + uint64_t p_pool_id; + snapid_t p_snap_id; + string p_image_id, c_image_id; + // Use set for ease of erase() for remove_child() + std::set<string> children; + + r = decode_parent_and_child(in, &p_pool_id, &p_image_id, &p_snap_id, + &c_image_id); + if (r < 0) + return r; + + CLS_LOG(20, "add_child %s to (%" PRIu64 ", %s, %" PRIu64 ")", c_image_id.c_str(), + p_pool_id, p_image_id.c_str(), p_snap_id.val); + + string key = parent_key(p_pool_id, p_image_id, p_snap_id); + + // get current child list for parent, if any + r = read_key(hctx, key, &children); + if ((r < 0) && (r != -ENOENT)) { + CLS_LOG(20, "add_child: omap read failed: %s", cpp_strerror(r).c_str()); + return r; + } + + if (children.find(c_image_id) != children.end()) { + CLS_LOG(20, "add_child: child already exists: %s", c_image_id.c_str()); + return -EEXIST; + } + // add new child + children.insert(c_image_id); + + // write back + bufferlist childbl; + encode(children, childbl); + r = cls_cxx_map_set_val(hctx, key, &childbl); + if (r < 0) + CLS_LOG(20, "add_child: omap write failed: %s", cpp_strerror(r).c_str()); + return r; +} + +/** + * remove child from rbd_children directory object + * + * Input: + * @param p_pool_id parent pool id + * @param p_image_id parent image oid + * @param p_snap_id parent snapshot id + * @param c_image_id new child image oid to add + * + * @returns 0 on success, negative error on failure + */ + +int remove_child(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + int r; + + uint64_t p_pool_id; + snapid_t p_snap_id; + string p_image_id, c_image_id; + std::set<string> children; + + r = decode_parent_and_child(in, &p_pool_id, &p_image_id, &p_snap_id, + &c_image_id); + if (r < 0) + return r; + + CLS_LOG(20, "remove_child %s from (%" PRIu64 ", %s, %" PRIu64 ")", + c_image_id.c_str(), p_pool_id, p_image_id.c_str(), + p_snap_id.val); + + string key = parent_key(p_pool_id, p_image_id, p_snap_id); + + // get current child list for parent. Unlike add_child(), an empty list + // is an error (how can we remove something that doesn't exist?) + r = read_key(hctx, key, &children); + if (r < 0) { + CLS_LOG(20, "remove_child: read omap failed: %s", cpp_strerror(r).c_str()); + return r; + } + + if (children.find(c_image_id) == children.end()) { + CLS_LOG(20, "remove_child: child not found: %s", c_image_id.c_str()); + return -ENOENT; + } + // find and remove child + children.erase(c_image_id); + + // now empty? remove key altogether + if (children.empty()) { + r = cls_cxx_map_remove_key(hctx, key); + if (r < 0) + CLS_LOG(20, "remove_child: remove key failed: %s", cpp_strerror(r).c_str()); + } else { + // write back shortened children list + bufferlist childbl; + encode(children, childbl); + r = cls_cxx_map_set_val(hctx, key, &childbl); + if (r < 0) + CLS_LOG(20, "remove_child: write omap failed: %s", cpp_strerror(r).c_str()); + } + return r; +} + +/** + * Input: + * @param p_pool_id parent pool id + * @param p_image_id parent image oid + * @param p_snap_id parent snapshot id + * @param c_image_id new child image oid to add + * + * Output: + * @param children set<string> of children + * + * @returns 0 on success, negative error on failure + */ +int get_children(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + int r; + uint64_t p_pool_id; + snapid_t p_snap_id; + string p_image_id; + std::set<string> children; + + r = decode_parent(in, &p_pool_id, &p_image_id, &p_snap_id); + if (r < 0) + return r; + + CLS_LOG(20, "get_children of (%" PRIu64 ", %s, %" PRIu64 ")", + p_pool_id, p_image_id.c_str(), p_snap_id.val); + + string key = parent_key(p_pool_id, p_image_id, p_snap_id); + + r = read_key(hctx, key, &children); + if (r < 0) { + if (r != -ENOENT) + CLS_LOG(20, "get_children: read omap failed: %s", cpp_strerror(r).c_str()); + return r; + } + encode(children, *out); + return 0; +} + + +/** + * Get the information needed to create a rados snap context for doing + * I/O to the data objects. This must include all snapshots. + * + * Output: + * @param snap_seq the highest snapshot id ever associated with the image (uint64_t) + * @param snap_ids existing snapshot ids in descending order (vector<uint64_t>) + * @returns 0 on success, negative error code on failure + */ +int get_snapcontext(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + CLS_LOG(20, "get_snapcontext"); + + int r; + int max_read = RBD_MAX_KEYS_READ; + vector<snapid_t> snap_ids; + string last_read = RBD_SNAP_KEY_PREFIX; + bool more; + + do { + set<string> keys; + r = cls_cxx_map_get_keys(hctx, last_read, max_read, &keys, &more); + if (r < 0) + return r; + + for (set<string>::const_iterator it = keys.begin(); + it != keys.end(); ++it) { + if ((*it).find(RBD_SNAP_KEY_PREFIX) != 0) + break; + snapid_t snap_id = snap_id_from_key(*it); + snap_ids.push_back(snap_id); + } + if (!keys.empty()) + last_read = *(keys.rbegin()); + } while (more); + + uint64_t snap_seq; + r = read_key(hctx, "snap_seq", &snap_seq); + if (r < 0) { + CLS_ERR("could not read the image's snap_seq off disk: %s", cpp_strerror(r).c_str()); + return r; + } + + // snap_ids must be descending in a snap context + std::reverse(snap_ids.begin(), snap_ids.end()); + + encode(snap_seq, *out); + encode(snap_ids, *out); + + return 0; +} + +/** + * Output: + * @param object_prefix prefix for data object names (string) + * @returns 0 on success, negative error code on failure + */ +int get_object_prefix(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + CLS_LOG(20, "get_object_prefix"); + + string object_prefix; + int r = read_key(hctx, "object_prefix", &object_prefix); + if (r < 0) { + CLS_ERR("failed to read the image's object prefix off of disk: %s", + cpp_strerror(r).c_str()); + return r; + } + + encode(object_prefix, *out); + + return 0; +} + +/** + * Input: + * none + * + * Output: + * @param pool_id (int64_t) of data pool or -1 if none + * @returns 0 on success, negative error code on failure + */ +int get_data_pool(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + CLS_LOG(20, "get_data_pool"); + + int64_t data_pool_id = -1; + int r = read_key(hctx, "data_pool_id", &data_pool_id); + if (r == -ENOENT) { + data_pool_id = -1; + } else if (r < 0) { + CLS_ERR("error reading image data pool id: %s", cpp_strerror(r).c_str()); + return r; + } + + encode(data_pool_id, *out); + return 0; +} + +/** + * Input: + * @param snap_id which snapshot to query + * + * Output: + * @param name (string) of the snapshot + * @returns 0 on success, negative error code on failure + */ +int get_snapshot_name(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + uint64_t snap_id; + + auto iter = in->cbegin(); + try { + decode(snap_id, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + CLS_LOG(20, "get_snapshot_name snap_id=%llu", (unsigned long long)snap_id); + + if (snap_id == CEPH_NOSNAP) + return -EINVAL; + + cls_rbd_snap snap; + string snapshot_key; + key_from_snap_id(snap_id, &snapshot_key); + int r = read_key(hctx, snapshot_key, &snap); + if (r < 0) + return r; + + encode(snap.name, *out); + + return 0; +} + +/** + * Input: + * @param snap_id which snapshot to query + * + * Output: + * @param timestamp (utime_t) of the snapshot + * @returns 0 on success, negative error code on failure + * + * NOTE: deprecated - remove this method after Luminous is unsupported + */ +int get_snapshot_timestamp(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + uint64_t snap_id; + + auto iter = in->cbegin(); + try { + decode(snap_id, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + CLS_LOG(20, "get_snapshot_timestamp snap_id=%llu", (unsigned long long)snap_id); + + if (snap_id == CEPH_NOSNAP) { + return -EINVAL; + } + + cls_rbd_snap snap; + string snapshot_key; + key_from_snap_id(snap_id, &snapshot_key); + int r = read_key(hctx, snapshot_key, &snap); + if (r < 0) { + return r; + } + + encode(snap.timestamp, *out); + return 0; +} + +/** + * Input: + * @param snap_id which snapshot to query + * + * Output: + * @param snapshot (cls::rbd::SnapshotInfo) + * @returns 0 on success, negative error code on failure + */ +int snapshot_get(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + uint64_t snap_id; + + auto iter = in->cbegin(); + try { + decode(snap_id, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + CLS_LOG(20, "snapshot_get snap_id=%llu", (unsigned long long)snap_id); + if (snap_id == CEPH_NOSNAP) { + return -EINVAL; + } + + cls_rbd_snap snap; + string snapshot_key; + key_from_snap_id(snap_id, &snapshot_key); + int r = read_key(hctx, snapshot_key, &snap); + if (r < 0) { + return r; + } + + cls::rbd::SnapshotInfo snapshot_info{snap.id, snap.snapshot_namespace, + snap.name, snap.image_size, + snap.timestamp, snap.child_count}; + encode(snapshot_info, *out); + return 0; +} + +/** + * Adds a snapshot to an rbd header. Ensures the id and name are unique. + * + * Input: + * @param snap_name name of the snapshot (string) + * @param snap_id id of the snapshot (uint64_t) + * @param snap_namespace namespace of the snapshot (cls::rbd::SnapshotNamespace) + * + * Output: + * @returns 0 on success, negative error code on failure. + * @returns -ESTALE if the input snap_id is less than the image's snap_seq + * @returns -EEXIST if the id or name are already used by another snapshot + */ +int snapshot_add(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + bufferlist snap_namebl, snap_idbl; + cls_rbd_snap snap_meta; + uint64_t snap_limit; + + try { + auto iter = in->cbegin(); + decode(snap_meta.name, iter); + decode(snap_meta.id, iter); + if (!iter.end()) { + decode(snap_meta.snapshot_namespace, iter); + } + } catch (const buffer::error &err) { + return -EINVAL; + } + + if (boost::get<cls::rbd::UnknownSnapshotNamespace>( + &snap_meta.snapshot_namespace) != nullptr) { + CLS_ERR("Unknown snapshot namespace provided"); + return -EINVAL; + } + + CLS_LOG(20, "snapshot_add name=%s id=%llu", snap_meta.name.c_str(), + (unsigned long long)snap_meta.id.val); + + if (snap_meta.id > CEPH_MAXSNAP) + return -EINVAL; + + uint64_t cur_snap_seq; + int r = read_key(hctx, "snap_seq", &cur_snap_seq); + if (r < 0) { + CLS_ERR("Could not read image's snap_seq off disk: %s", cpp_strerror(r).c_str()); + return r; + } + + // client lost a race with another snapshot creation. + // snap_seq must be monotonically increasing. + if (snap_meta.id < cur_snap_seq) + return -ESTALE; + + r = read_key(hctx, "size", &snap_meta.image_size); + if (r < 0) { + CLS_ERR("Could not read image's size off disk: %s", cpp_strerror(r).c_str()); + return r; + } + r = read_key(hctx, "flags", &snap_meta.flags); + if (r < 0 && r != -ENOENT) { + CLS_ERR("Could not read image's flags off disk: %s", cpp_strerror(r).c_str()); + return r; + } + + r = read_key(hctx, "snap_limit", &snap_limit); + if (r == -ENOENT) { + snap_limit = UINT64_MAX; + } else if (r < 0) { + CLS_ERR("Could not read snapshot limit off disk: %s", cpp_strerror(r).c_str()); + return r; + } + + snap_meta.timestamp = ceph_clock_now(); + + uint64_t total_read = 0; + auto pre_check_lambda = + [&snap_meta, &total_read, snap_limit](const cls_rbd_snap& old_meta) { + ++total_read; + if (total_read >= snap_limit) { + CLS_ERR("Attempt to create snapshot over limit of %" PRIu64, + snap_limit); + return -EDQUOT; + } + + if ((snap_meta.name == old_meta.name && + snap_meta.snapshot_namespace == old_meta.snapshot_namespace) || + snap_meta.id == old_meta.id) { + CLS_LOG(20, "snap_name %s or snap_id %" PRIu64 " matches existing snap " + "%s %" PRIu64, snap_meta.name.c_str(), snap_meta.id.val, + old_meta.name.c_str(), old_meta.id.val); + return -EEXIST; + } + return 0; + }; + + r = image::snapshot::iterate(hctx, pre_check_lambda); + if (r < 0) { + return r; + } + + // snapshot inherits parent, if any + cls_rbd_parent parent; + r = read_key(hctx, "parent", &parent); + if (r < 0 && r != -ENOENT) { + return r; + } + if (r == 0) { + // write helper method will convert to normalized format if required + snap_meta.parent = parent; + } + + if (cls::rbd::get_snap_namespace_type(snap_meta.snapshot_namespace) == + cls::rbd::SNAPSHOT_NAMESPACE_TYPE_TRASH) { + // add snap_trash feature bit if not already enabled + r = image::set_op_features(hctx, RBD_OPERATION_FEATURE_SNAP_TRASH, + RBD_OPERATION_FEATURE_SNAP_TRASH); + if (r < 0) { + return r; + } + } + + r = write_key(hctx, "snap_seq", snap_meta.id); + if (r < 0) { + return r; + } + + std::string snapshot_key; + key_from_snap_id(snap_meta.id, &snapshot_key); + r = image::snapshot::write(hctx, snapshot_key, std::move(snap_meta)); + if (r < 0) { + return r; + } + + return 0; +} + +/** + * rename snapshot . + * + * Input: + * @param src_snap_id old snap id of the snapshot (snapid_t) + * @param dst_snap_name new name of the snapshot (string) + * + * Output: + * @returns 0 on success, negative error code on failure. + */ +int snapshot_rename(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + bufferlist snap_namebl, snap_idbl; + snapid_t src_snap_id; + string dst_snap_name; + cls_rbd_snap snap_meta; + int r; + + try { + auto iter = in->cbegin(); + decode(src_snap_id, iter); + decode(dst_snap_name, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + CLS_LOG(20, "snapshot_rename id=%" PRIu64 ", dst_name=%s", + src_snap_id.val, dst_snap_name.c_str()); + + auto duplicate_name_lambda = [&dst_snap_name](const cls_rbd_snap& snap_meta) { + if (cls::rbd::get_snap_namespace_type(snap_meta.snapshot_namespace) == + cls::rbd::SNAPSHOT_NAMESPACE_TYPE_USER && + snap_meta.name == dst_snap_name) { + CLS_LOG(20, "snap_name %s matches existing snap with snap id %" PRIu64, + dst_snap_name.c_str(), snap_meta.id.val); + return -EEXIST; + } + return 0; + }; + r = image::snapshot::iterate(hctx, duplicate_name_lambda); + if (r < 0) { + return r; + } + + std::string src_snap_key; + key_from_snap_id(src_snap_id, &src_snap_key); + r = read_key(hctx, src_snap_key, &snap_meta); + if (r == -ENOENT) { + CLS_LOG(20, "cannot find existing snap with snap id = %" PRIu64, + src_snap_id.val); + return r; + } + + if (cls::rbd::get_snap_namespace_type(snap_meta.snapshot_namespace) != + cls::rbd::SNAPSHOT_NAMESPACE_TYPE_USER) { + // can only rename user snapshots + return -EINVAL; + } + + snap_meta.name = dst_snap_name; + r = image::snapshot::write(hctx, src_snap_key, std::move(snap_meta)); + if (r < 0) { + return r; + } + + return 0; +} + +/** + * Removes a snapshot from an rbd header. + * + * Input: + * @param snap_id the id of the snapshot to remove (uint64_t) + * + * Output: + * @returns 0 on success, negative error code on failure + */ +int snapshot_remove(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + snapid_t snap_id; + + try { + auto iter = in->cbegin(); + decode(snap_id, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + CLS_LOG(20, "snapshot_remove id=%llu", (unsigned long long)snap_id.val); + + // check if the key exists. we can't rely on remove_key doing this for + // us, since OMAPRMKEYS returns success if the key is not there. + // bug or feature? sounds like a bug, since tmap did not have this + // behavior, but cls_rgw may rely on it... + cls_rbd_snap snap; + string snapshot_key; + key_from_snap_id(snap_id, &snapshot_key); + int r = read_key(hctx, snapshot_key, &snap); + if (r == -ENOENT) { + return -ENOENT; + } + + if (snap.protection_status != RBD_PROTECTION_STATUS_UNPROTECTED) { + return -EBUSY; + } + + // snapshot is in-use by clone v2 child + if (snap.child_count > 0) { + return -EBUSY; + } + + r = remove_key(hctx, snapshot_key); + if (r < 0) { + return r; + } + + bool has_child_snaps = false; + bool has_trash_snaps = false; + auto remove_lambda = [snap_id, &has_child_snaps, &has_trash_snaps]( + const cls_rbd_snap& snap_meta) { + if (snap_meta.id != snap_id) { + if (snap_meta.parent.pool_id != -1 || snap_meta.parent_overlap) { + has_child_snaps = true; + } + + if (cls::rbd::get_snap_namespace_type(snap_meta.snapshot_namespace) == + cls::rbd::SNAPSHOT_NAMESPACE_TYPE_TRASH) { + has_trash_snaps = true; + } + } + return 0; + }; + + r = image::snapshot::iterate(hctx, remove_lambda); + if (r < 0) { + return r; + } + + cls_rbd_parent parent; + r = read_key(hctx, "parent", &parent); + if (r < 0 && r != -ENOENT) { + return r; + } + + bool has_parent = (r >= 0 && parent.exists()); + bool is_head_child = (has_parent && parent.head_overlap); + int8_t require_osd_release = cls_get_required_osd_release(hctx); + if (has_parent && !is_head_child && !has_child_snaps && + require_osd_release >= CEPH_RELEASE_NAUTILUS) { + // remove the unused parent image spec + r = remove_key(hctx, "parent"); + if (r < 0 && r != -ENOENT) { + return r; + } + } + + uint64_t op_features_mask = 0ULL; + if (!has_child_snaps && !is_head_child) { + // disable clone child op feature if no longer associated + op_features_mask |= RBD_OPERATION_FEATURE_CLONE_CHILD; + } + if (!has_trash_snaps) { + // remove the snap_trash op feature if not in-use by any other snapshots + op_features_mask |= RBD_OPERATION_FEATURE_SNAP_TRASH; + } + + if (op_features_mask != 0ULL) { + r = image::set_op_features(hctx, 0, op_features_mask); + if (r < 0) { + return r; + } + } + + return 0; +} + +/** + * Moves a snapshot to the trash namespace. + * + * Input: + * @param snap_id the id of the snapshot to move to the trash (uint64_t) + * + * Output: + * @returns 0 on success, negative error code on failure + */ +int snapshot_trash_add(cls_method_context_t hctx, bufferlist *in, + bufferlist *out) +{ + snapid_t snap_id; + + try { + auto iter = in->cbegin(); + decode(snap_id, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + CLS_LOG(20, "snapshot_trash_add id=%" PRIu64, snap_id.val); + + cls_rbd_snap snap; + std::string snapshot_key; + key_from_snap_id(snap_id, &snapshot_key); + int r = read_key(hctx, snapshot_key, &snap); + if (r == -ENOENT) { + return r; + } + + if (snap.protection_status != RBD_PROTECTION_STATUS_UNPROTECTED) { + return -EBUSY; + } + + auto snap_type = cls::rbd::get_snap_namespace_type(snap.snapshot_namespace); + if (snap_type == cls::rbd::SNAPSHOT_NAMESPACE_TYPE_TRASH) { + return -EEXIST; + } + + // add snap_trash feature bit if not already enabled + r = image::set_op_features(hctx, RBD_OPERATION_FEATURE_SNAP_TRASH, + RBD_OPERATION_FEATURE_SNAP_TRASH); + if (r < 0) { + return r; + } + + snap.snapshot_namespace = cls::rbd::TrashSnapshotNamespace{snap_type, + snap.name}; + uuid_d uuid_gen; + uuid_gen.generate_random(); + snap.name = uuid_gen.to_string(); + + r = image::snapshot::write(hctx, snapshot_key, std::move(snap)); + if (r < 0) { + return r; + } + + return 0; +} + +/** + * Returns a uint64_t of all the features supported by this class. + */ +int get_all_features(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + uint64_t all_features = RBD_FEATURES_ALL; + encode(all_features, *out); + return 0; +} + +/** + * "Copy up" data from the parent of a clone to the clone's object(s). + * Used for implementing copy-on-write for a clone image. Client + * will pass down a chunk of data that fits completely within one + * clone block (one object), and is aligned (starts at beginning of block), + * but may be shorter (for non-full parent blocks). The class method + * can't know the object size to validate the requested length, + * so it just writes the data as given if the child object doesn't + * already exist, and returns success if it does. + * + * Input: + * @param in bufferlist of data to write + * + * Output: + * @returns 0 on success, or if block already exists in child + * negative error code on other error + */ + +int copyup(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + // check for existence; if child object exists, just return success + if (cls_cxx_stat(hctx, NULL, NULL) == 0) + return 0; + CLS_LOG(20, "copyup: writing length %d\n", in->length()); + return cls_cxx_write(hctx, 0, in->length(), in); +} + + +/************************ rbd_id object methods **************************/ + +/** + * Input: + * @param in ignored + * + * Output: + * @param id the id stored in the object + * @returns 0 on success, negative error code on failure + */ +int get_id(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + uint64_t size; + int r = cls_cxx_stat(hctx, &size, NULL); + if (r < 0) + return r; + + if (size == 0) + return -ENOENT; + + bufferlist read_bl; + r = cls_cxx_read(hctx, 0, size, &read_bl); + if (r < 0) { + CLS_ERR("get_id: could not read id: %s", cpp_strerror(r).c_str()); + return r; + } + + string id; + try { + auto iter = read_bl.cbegin(); + decode(id, iter); + } catch (const buffer::error &err) { + return -EIO; + } + + encode(id, *out); + return 0; +} + +/** + * Set the id of an image. The object must already exist. + * + * Input: + * @param id the id of the image, as an alpha-numeric string + * + * Output: + * @returns 0 on success, -EEXIST if the atomic create fails, + * negative error code on other error + */ +int set_id(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + int r = check_exists(hctx); + if (r < 0) + return r; + + string id; + try { + auto iter = in->cbegin(); + decode(id, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + if (!is_valid_id(id)) { + CLS_ERR("set_id: invalid id '%s'", id.c_str()); + return -EINVAL; + } + + uint64_t size; + r = cls_cxx_stat(hctx, &size, NULL); + if (r < 0) + return r; + if (size != 0) + return -EEXIST; + + CLS_LOG(20, "set_id: id=%s", id.c_str()); + + bufferlist write_bl; + encode(id, write_bl); + return cls_cxx_write(hctx, 0, write_bl.length(), &write_bl); +} + +/** + * Update the access timestamp of an image + * + * Input: + * @param none + * + * Output: + * @returns 0 on success, negative error code on other error + */ +int set_access_timestamp(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + int r = check_exists(hctx); + if(r < 0) + return r; + + utime_t timestamp = ceph_clock_now(); + r = write_key(hctx, "access_timestamp", timestamp); + if(r < 0) { + CLS_ERR("error setting access_timestamp"); + return r; + } + + return 0; +} + +/** + * Update the modify timestamp of an image + * + * Input: + * @param none + * + * Output: + * @returns 0 on success, negative error code on other error + */ + +int set_modify_timestamp(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + int r = check_exists(hctx); + if(r < 0) + return r; + + utime_t timestamp = ceph_clock_now(); + r = write_key(hctx, "modify_timestamp", timestamp); + if(r < 0) { + CLS_ERR("error setting modify_timestamp"); + return r; + } + + return 0; +} + + + +/*********************** methods for rbd_directory ***********************/ + +static const string dir_key_for_id(const string &id) +{ + return RBD_DIR_ID_KEY_PREFIX + id; +} + +static const string dir_key_for_name(const string &name) +{ + return RBD_DIR_NAME_KEY_PREFIX + name; +} + +static const string dir_name_from_key(const string &key) +{ + return key.substr(strlen(RBD_DIR_NAME_KEY_PREFIX)); +} + +static int dir_add_image_helper(cls_method_context_t hctx, + const string &name, const string &id, + bool check_for_unique_id) +{ + if (!name.size() || !is_valid_id(id)) { + CLS_ERR("dir_add_image_helper: invalid name '%s' or id '%s'", + name.c_str(), id.c_str()); + return -EINVAL; + } + + CLS_LOG(20, "dir_add_image_helper name=%s id=%s", name.c_str(), id.c_str()); + + string tmp; + string name_key = dir_key_for_name(name); + string id_key = dir_key_for_id(id); + int r = read_key(hctx, name_key, &tmp); + if (r != -ENOENT) { + CLS_LOG(10, "name already exists"); + return -EEXIST; + } + r = read_key(hctx, id_key, &tmp); + if (r != -ENOENT && check_for_unique_id) { + CLS_LOG(10, "id already exists"); + return -EBADF; + } + bufferlist id_bl, name_bl; + encode(id, id_bl); + encode(name, name_bl); + map<string, bufferlist> omap_vals; + omap_vals[name_key] = id_bl; + omap_vals[id_key] = name_bl; + return cls_cxx_map_set_vals(hctx, &omap_vals); +} + +static int dir_remove_image_helper(cls_method_context_t hctx, + const string &name, const string &id) +{ + CLS_LOG(20, "dir_remove_image_helper name=%s id=%s", + name.c_str(), id.c_str()); + + string stored_name, stored_id; + string name_key = dir_key_for_name(name); + string id_key = dir_key_for_id(id); + int r = read_key(hctx, name_key, &stored_id); + if (r < 0) { + if (r != -ENOENT) + CLS_ERR("error reading name to id mapping: %s", cpp_strerror(r).c_str()); + return r; + } + r = read_key(hctx, id_key, &stored_name); + if (r < 0) { + CLS_ERR("error reading id to name mapping: %s", cpp_strerror(r).c_str()); + return r; + } + + // check if this op raced with a rename + if (stored_name != name || stored_id != id) { + CLS_ERR("stored name '%s' and id '%s' do not match args '%s' and '%s'", + stored_name.c_str(), stored_id.c_str(), name.c_str(), id.c_str()); + return -ESTALE; + } + + r = cls_cxx_map_remove_key(hctx, name_key); + if (r < 0) { + CLS_ERR("error removing name: %s", cpp_strerror(r).c_str()); + return r; + } + + r = cls_cxx_map_remove_key(hctx, id_key); + if (r < 0) { + CLS_ERR("error removing id: %s", cpp_strerror(r).c_str()); + return r; + } + + return 0; +} + +/** + * Rename an image in the directory, updating both indexes + * atomically. This can't be done from the client calling + * dir_add_image and dir_remove_image in one transaction because the + * results of the first method are not visibale to later steps. + * + * Input: + * @param src original name of the image + * @param dest new name of the image + * @param id the id of the image + * + * Output: + * @returns -ESTALE if src and id do not map to each other + * @returns -ENOENT if src or id are not in the directory + * @returns -EEXIST if dest already exists + * @returns 0 on success, negative error code on failure + */ +int dir_rename_image(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + string src, dest, id; + try { + auto iter = in->cbegin(); + decode(src, iter); + decode(dest, iter); + decode(id, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + int r = dir_remove_image_helper(hctx, src, id); + if (r < 0) + return r; + // ignore duplicate id because the result of + // remove_image_helper is not visible yet + return dir_add_image_helper(hctx, dest, id, false); +} + +/** + * Get the id of an image given its name. + * + * Input: + * @param name the name of the image + * + * Output: + * @param id the id of the image + * @returns 0 on success, negative error code on failure + */ +int dir_get_id(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + string name; + + try { + auto iter = in->cbegin(); + decode(name, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + CLS_LOG(20, "dir_get_id: name=%s", name.c_str()); + + string id; + int r = read_key(hctx, dir_key_for_name(name), &id); + if (r < 0) { + if (r != -ENOENT) + CLS_ERR("error reading id for name '%s': %s", name.c_str(), cpp_strerror(r).c_str()); + return r; + } + encode(id, *out); + return 0; +} + +/** + * Get the name of an image given its id. + * + * Input: + * @param id the id of the image + * + * Output: + * @param name the name of the image + * @returns 0 on success, negative error code on failure + */ +int dir_get_name(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + string id; + + try { + auto iter = in->cbegin(); + decode(id, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + CLS_LOG(20, "dir_get_name: id=%s", id.c_str()); + + string name; + int r = read_key(hctx, dir_key_for_id(id), &name); + if (r < 0) { + if (r != -ENOENT) { + CLS_ERR("error reading name for id '%s': %s", id.c_str(), + cpp_strerror(r).c_str()); + } + return r; + } + encode(name, *out); + return 0; +} + +/** + * List the names and ids of the images in the directory, sorted by + * name. + * + * Input: + * @param start_after which name to begin listing after + * (use the empty string to start at the beginning) + * @param max_return the maximum number of names to list + * + * Output: + * @param images map from name to id of up to max_return images + * @returns 0 on success, negative error code on failure + */ +int dir_list(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + string start_after; + uint64_t max_return; + + try { + auto iter = in->cbegin(); + decode(start_after, iter); + decode(max_return, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + int max_read = RBD_MAX_KEYS_READ; + map<string, string> images; + string last_read = dir_key_for_name(start_after); + bool more = true; + + while (more && images.size() < max_return) { + map<string, bufferlist> vals; + CLS_LOG(20, "last_read = '%s'", last_read.c_str()); + int r = cls_cxx_map_get_vals(hctx, last_read, RBD_DIR_NAME_KEY_PREFIX, + max_read, &vals, &more); + if (r < 0) { + if (r != -ENOENT) { + CLS_ERR("error reading directory by name: %s", cpp_strerror(r).c_str()); + } + return r; + } + + for (map<string, bufferlist>::iterator it = vals.begin(); + it != vals.end(); ++it) { + string id; + auto iter = it->second.cbegin(); + try { + decode(id, iter); + } catch (const buffer::error &err) { + CLS_ERR("could not decode id of image '%s'", it->first.c_str()); + return -EIO; + } + CLS_LOG(20, "adding '%s' -> '%s'", dir_name_from_key(it->first).c_str(), id.c_str()); + images[dir_name_from_key(it->first)] = id; + if (images.size() >= max_return) + break; + } + if (!vals.empty()) { + last_read = dir_key_for_name(images.rbegin()->first); + } + } + + encode(images, *out); + + return 0; +} + +/** + * Add an image to the rbd directory. Creates the directory object if + * needed, and updates the index from id to name and name to id. + * + * Input: + * @param name the name of the image + * @param id the id of the image + * + * Output: + * @returns -EEXIST if the image name is already in the directory + * @returns -EBADF if the image id is already in the directory + * @returns 0 on success, negative error code on failure + */ +int dir_add_image(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + int r = cls_cxx_create(hctx, false); + if (r < 0) { + CLS_ERR("could not create directory: %s", cpp_strerror(r).c_str()); + return r; + } + + string name, id; + try { + auto iter = in->cbegin(); + decode(name, iter); + decode(id, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + return dir_add_image_helper(hctx, name, id, true); +} + +/** + * Remove an image from the rbd directory. + * + * Input: + * @param name the name of the image + * @param id the id of the image + * + * Output: + * @returns -ESTALE if the name and id do not map to each other + * @returns 0 on success, negative error code on failure + */ +int dir_remove_image(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + string name, id; + try { + auto iter = in->cbegin(); + decode(name, iter); + decode(id, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + return dir_remove_image_helper(hctx, name, id); +} + +/** + * Verify the current state of the directory + * + * Input: + * @param state the DirectoryState of the directory + * + * Output: + * @returns -ENOENT if the state does not match + * @returns 0 on success, negative error code on failure + */ +int dir_state_assert(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + cls::rbd::DirectoryState directory_state = cls::rbd::DIRECTORY_STATE_READY; + try { + auto iter = in->cbegin(); + decode(directory_state, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + cls::rbd::DirectoryState on_disk_directory_state = directory_state; + int r = read_key(hctx, "state", &on_disk_directory_state); + if (r < 0) { + return r; + } + + if (directory_state != on_disk_directory_state) { + return -ENOENT; + } + return 0; +} + +/** + * Set the current state of the directory + * + * Input: + * @param state the DirectoryState of the directory + * + * Output: + * @returns -ENOENT if the state does not match + * @returns 0 on success, negative error code on failure + */ +int dir_state_set(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + cls::rbd::DirectoryState directory_state; + try { + auto iter = in->cbegin(); + decode(directory_state, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + int r = check_exists(hctx); + if (r < 0 && r != -ENOENT) { + return r; + } + + switch (directory_state) { + case cls::rbd::DIRECTORY_STATE_READY: + break; + case cls::rbd::DIRECTORY_STATE_ADD_DISABLED: + { + if (r == -ENOENT) { + return r; + } + + // verify that the directory is empty + std::map<std::string, bufferlist> vals; + bool more; + r = cls_cxx_map_get_vals(hctx, RBD_DIR_NAME_KEY_PREFIX, + RBD_DIR_NAME_KEY_PREFIX, 1, &vals, &more); + if (r < 0) { + return r; + } else if (!vals.empty()) { + return -EBUSY; + } + } + break; + default: + return -EINVAL; + } + + r = write_key(hctx, "state", directory_state); + if (r < 0) { + return r; + } + + return 0; +} + +int object_map_read(cls_method_context_t hctx, BitVector<2> &object_map) +{ + uint64_t size; + int r = cls_cxx_stat(hctx, &size, NULL); + if (r < 0) { + return r; + } + if (size == 0) { + return -ENOENT; + } + + bufferlist bl; + r = cls_cxx_read(hctx, 0, size, &bl); + if (r < 0) { + return r; + } + + try { + auto iter = bl.cbegin(); + decode(object_map, iter); + } catch (const buffer::error &err) { + CLS_ERR("failed to decode object map: %s", err.what()); + return -EINVAL; + } + return 0; +} + +/** + * Load an rbd image's object map + * + * Input: + * none + * + * Output: + * @param object map bit vector + * @returns 0 on success, negative error code on failure + */ +int object_map_load(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + BitVector<2> object_map; + int r = object_map_read(hctx, object_map); + if (r < 0) { + return r; + } + + object_map.set_crc_enabled(false); + encode(object_map, *out); + return 0; +} + +/** + * Save an rbd image's object map + * + * Input: + * @param object map bit vector + * + * Output: + * @returns 0 on success, negative error code on failure + */ +int object_map_save(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + BitVector<2> object_map; + try { + auto iter = in->cbegin(); + decode(object_map, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + object_map.set_crc_enabled(true); + + bufferlist bl; + encode(object_map, bl); + CLS_LOG(20, "object_map_save: object size=%" PRIu64 ", byte size=%u", + object_map.size(), bl.length()); + return cls_cxx_write_full(hctx, &bl); +} + +/** + * Resize an rbd image's object map + * + * Input: + * @param object_count the max number of objects in the image + * @param default_state the default state of newly created objects + * + * Output: + * @returns 0 on success, negative error code on failure + */ +int object_map_resize(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + uint64_t object_count; + uint8_t default_state; + try { + auto iter = in->cbegin(); + decode(object_count, iter); + decode(default_state, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + // protect against excessive memory requirements + if (object_count > cls::rbd::MAX_OBJECT_MAP_OBJECT_COUNT) { + CLS_ERR("object map too large: %" PRIu64, object_count); + return -EINVAL; + } + + BitVector<2> object_map; + int r = object_map_read(hctx, object_map); + if ((r < 0) && (r != -ENOENT)) { + return r; + } + + size_t orig_object_map_size = object_map.size(); + if (object_count < orig_object_map_size) { + auto it = object_map.begin() + object_count; + auto end_it = object_map.end() ; + uint64_t i = object_count; + for (; it != end_it; ++it, ++i) { + if (*it != default_state) { + CLS_ERR("object map indicates object still exists: %" PRIu64, i); + return -ESTALE; + } + } + object_map.resize(object_count); + } else if (object_count > orig_object_map_size) { + object_map.resize(object_count); + auto it = object_map.begin() + orig_object_map_size; + auto end_it = object_map.end(); + for (; it != end_it; ++it) { + *it = default_state; + } + } + + bufferlist map; + encode(object_map, map); + CLS_LOG(20, "object_map_resize: object size=%" PRIu64 ", byte size=%u", + object_count, map.length()); + return cls_cxx_write_full(hctx, &map); +} + +/** + * Update an rbd image's object map + * + * Input: + * @param start_object_no the start object iterator + * @param end_object_no the end object iterator + * @param new_object_state the new object state + * @param current_object_state optional current object state filter + * + * Output: + * @returns 0 on success, negative error code on failure + */ +int object_map_update(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + uint64_t start_object_no; + uint64_t end_object_no; + uint8_t new_object_state; + boost::optional<uint8_t> current_object_state; + try { + auto iter = in->cbegin(); + decode(start_object_no, iter); + decode(end_object_no, iter); + decode(new_object_state, iter); + decode(current_object_state, iter); + } catch (const buffer::error &err) { + CLS_ERR("failed to decode message"); + return -EINVAL; + } + + uint64_t size; + int r = cls_cxx_stat(hctx, &size, NULL); + if (r < 0) { + return r; + } + + BitVector<2> object_map; + bufferlist header_bl; + r = cls_cxx_read2(hctx, 0, object_map.get_header_length(), &header_bl, + CEPH_OSD_OP_FLAG_FADVISE_WILLNEED); + if (r < 0) { + CLS_ERR("object map header read failed"); + return r; + } + + try { + auto it = header_bl.cbegin(); + object_map.decode_header(it); + } catch (const buffer::error &err) { + CLS_ERR("failed to decode object map header: %s", err.what()); + return -EINVAL; + } + + uint64_t object_byte_offset; + uint64_t byte_length; + object_map.get_header_crc_extents(&object_byte_offset, &byte_length); + + bufferlist footer_bl; + r = cls_cxx_read2(hctx, object_byte_offset, byte_length, &footer_bl, + CEPH_OSD_OP_FLAG_FADVISE_WILLNEED); + if (r < 0) { + CLS_ERR("object map footer read header CRC failed"); + return r; + } + + try { + auto it = footer_bl.cbegin(); + object_map.decode_header_crc(it); + } catch (const buffer::error &err) { + CLS_ERR("failed to decode object map header CRC: %s", err.what()); + } + + if (start_object_no >= end_object_no || end_object_no > object_map.size()) { + return -ERANGE; + } + + uint64_t object_count = end_object_no - start_object_no; + object_map.get_data_crcs_extents(start_object_no, object_count, + &object_byte_offset, &byte_length); + const auto footer_object_offset = object_byte_offset; + + footer_bl.clear(); + r = cls_cxx_read2(hctx, object_byte_offset, byte_length, &footer_bl, + CEPH_OSD_OP_FLAG_FADVISE_WILLNEED); + if (r < 0) { + CLS_ERR("object map footer read data CRCs failed"); + return r; + } + + try { + auto it = footer_bl.cbegin(); + object_map.decode_data_crcs(it, start_object_no); + } catch (const buffer::error &err) { + CLS_ERR("failed to decode object map data CRCs: %s", err.what()); + } + + uint64_t data_byte_offset; + object_map.get_data_extents(start_object_no, object_count, + &data_byte_offset, &object_byte_offset, + &byte_length); + + bufferlist data_bl; + r = cls_cxx_read2(hctx, object_byte_offset, byte_length, &data_bl, + CEPH_OSD_OP_FLAG_FADVISE_WILLNEED); + if (r < 0) { + CLS_ERR("object map data read failed"); + return r; + } + + try { + auto it = data_bl.cbegin(); + object_map.decode_data(it, data_byte_offset); + } catch (const buffer::error &err) { + CLS_ERR("failed to decode data chunk [%" PRIu64 "]: %s", + data_byte_offset, err.what()); + return -EINVAL; + } + + bool updated = false; + auto it = object_map.begin() + start_object_no; + auto end_it = object_map.begin() + end_object_no; + for (; it != end_it; ++it) { + uint8_t state = *it; + if ((!current_object_state || state == *current_object_state || + (*current_object_state == OBJECT_EXISTS && + state == OBJECT_EXISTS_CLEAN)) && state != new_object_state) { + *it = new_object_state; + updated = true; + } + } + + if (updated) { + CLS_LOG(20, "object_map_update: %" PRIu64 "~%" PRIu64 " -> %" PRIu64, + data_byte_offset, byte_length, object_byte_offset); + + bufferlist data_bl; + object_map.encode_data(data_bl, data_byte_offset, byte_length); + r = cls_cxx_write2(hctx, object_byte_offset, data_bl.length(), &data_bl, + CEPH_OSD_OP_FLAG_FADVISE_WILLNEED); + if (r < 0) { + CLS_ERR("failed to write object map header: %s", cpp_strerror(r).c_str()); + return r; + } + + footer_bl.clear(); + object_map.encode_data_crcs(footer_bl, start_object_no, object_count); + r = cls_cxx_write2(hctx, footer_object_offset, footer_bl.length(), + &footer_bl, CEPH_OSD_OP_FLAG_FADVISE_WILLNEED); + if (r < 0) { + CLS_ERR("failed to write object map footer: %s", cpp_strerror(r).c_str()); + return r; + } + } else { + CLS_LOG(20, "object_map_update: no update necessary"); + } + + return 0; +} + +/** + * Mark all _EXISTS objects as _EXISTS_CLEAN so future writes to the + * image HEAD can be tracked. + * + * Input: + * none + * + * Output: + * @returns 0 on success, negative error code on failure + */ +int object_map_snap_add(cls_method_context_t hctx, bufferlist *in, + bufferlist *out) +{ + BitVector<2> object_map; + int r = object_map_read(hctx, object_map); + if (r < 0) { + return r; + } + + bool updated = false; + auto it = object_map.begin(); + auto end_it = object_map.end(); + for (; it != end_it; ++it) { + if (*it == OBJECT_EXISTS) { + *it = OBJECT_EXISTS_CLEAN; + updated = true; + } + } + + if (updated) { + bufferlist bl; + encode(object_map, bl); + r = cls_cxx_write_full(hctx, &bl); + } + return r; +} + +/** + * Mark all _EXISTS_CLEAN objects as _EXISTS in the current object map + * if the provided snapshot object map object is marked as _EXISTS. + * + * Input: + * @param snapshot object map bit vector + * + * Output: + * @returns 0 on success, negative error code on failure + */ +int object_map_snap_remove(cls_method_context_t hctx, bufferlist *in, + bufferlist *out) +{ + BitVector<2> src_object_map; + try { + auto iter = in->cbegin(); + decode(src_object_map, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + BitVector<2> dst_object_map; + int r = object_map_read(hctx, dst_object_map); + if (r < 0) { + return r; + } + + bool updated = false; + auto src_it = src_object_map.begin(); + auto dst_it = dst_object_map.begin(); + auto dst_it_end = dst_object_map.end(); + uint64_t i = 0; + for (; dst_it != dst_it_end; ++dst_it) { + if (*dst_it == OBJECT_EXISTS_CLEAN && + (i >= src_object_map.size() || *src_it == OBJECT_EXISTS)) { + *dst_it = OBJECT_EXISTS; + updated = true; + } + if (i < src_object_map.size()) + ++src_it; + ++i; + } + + if (updated) { + bufferlist bl; + encode(dst_object_map, bl); + r = cls_cxx_write_full(hctx, &bl); + } + return r; +} + +static const string metadata_key_for_name(const string &name) +{ + return RBD_METADATA_KEY_PREFIX + name; +} + +static const string metadata_name_from_key(const string &key) +{ + return key.substr(strlen(RBD_METADATA_KEY_PREFIX)); +} + +/** + * Input: + * @param start_after which name to begin listing after + * (use the empty string to start at the beginning) + * @param max_return the maximum number of names to list + + * Output: + * @param value + * @returns 0 on success, negative error code on failure + */ +int metadata_list(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + string start_after; + uint64_t max_return; + + try { + auto iter = in->cbegin(); + decode(start_after, iter); + decode(max_return, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + // TODO remove implicit support for zero during the N-release + if (max_return == 0) { + max_return = RBD_MAX_KEYS_READ; + } + + map<string, bufferlist> data; + string last_read = metadata_key_for_name(start_after); + bool more = true; + + while (more && data.size() < max_return) { + map<string, bufferlist> raw_data; + int max_read = std::min<uint64_t>(RBD_MAX_KEYS_READ, max_return - data.size()); + int r = cls_cxx_map_get_vals(hctx, last_read, RBD_METADATA_KEY_PREFIX, + max_read, &raw_data, &more); + if (r < 0) { + if (r != -ENOENT) { + CLS_ERR("failed to read the vals off of disk: %s", + cpp_strerror(r).c_str()); + } + return r; + } + + for (auto& kv : raw_data) { + data[metadata_name_from_key(kv.first)].swap(kv.second); + } + + if (!raw_data.empty()) { + last_read = raw_data.rbegin()->first; + } + } + + encode(data, *out); + return 0; +} + +/** + * Input: + * @param data <map(key, value)> + * + * Output: + * @returns 0 on success, negative error code on failure + */ +int metadata_set(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + map<string, bufferlist> data, raw_data; + + auto iter = in->cbegin(); + try { + decode(data, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + for (map<string, bufferlist>::iterator it = data.begin(); + it != data.end(); ++it) { + CLS_LOG(20, "metadata_set key=%s value=%.*s", it->first.c_str(), + it->second.length(), it->second.c_str()); + raw_data[metadata_key_for_name(it->first)].swap(it->second); + } + int r = cls_cxx_map_set_vals(hctx, &raw_data); + if (r < 0) { + CLS_ERR("error writing metadata: %s", cpp_strerror(r).c_str()); + return r; + } + + return 0; +} + +/** + * Input: + * @param key + * + * Output: + * @returns 0 on success, negative error code on failure + */ +int metadata_remove(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + string key; + + auto iter = in->cbegin(); + try { + decode(key, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + CLS_LOG(20, "metadata_remove key=%s", key.c_str()); + + int r = cls_cxx_map_remove_key(hctx, metadata_key_for_name(key)); + if (r < 0) { + CLS_ERR("error removing metadata: %s", cpp_strerror(r).c_str()); + return r; + } + + return 0; +} + +/** + * Input: + * @param key + * + * Output: + * @param metadata value associated with the key + * @returns 0 on success, negative error code on failure + */ +int metadata_get(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + string key; + bufferlist value; + + auto iter = in->cbegin(); + try { + decode(key, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + CLS_LOG(20, "metadata_get key=%s", key.c_str()); + + int r = cls_cxx_map_get_val(hctx, metadata_key_for_name(key), &value); + if (r < 0) { + if (r != -ENOENT) + CLS_ERR("error getting metadata: %s", cpp_strerror(r).c_str()); + return r; + } + + encode(value, *out); + return 0; +} + +int snapshot_get_limit(cls_method_context_t hctx, bufferlist *in, + bufferlist *out) +{ + uint64_t snap_limit; + int r = read_key(hctx, "snap_limit", &snap_limit); + if (r == -ENOENT) { + snap_limit = UINT64_MAX; + } else if (r < 0) { + CLS_ERR("error retrieving snapshot limit: %s", cpp_strerror(r).c_str()); + return r; + } + + CLS_LOG(20, "read snapshot limit %" PRIu64, snap_limit); + encode(snap_limit, *out); + + return 0; +} + +int snapshot_set_limit(cls_method_context_t hctx, bufferlist *in, + bufferlist *out) +{ + int rc; + uint64_t new_limit; + bufferlist bl; + size_t snap_count = 0; + + try { + auto iter = in->cbegin(); + decode(new_limit, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + if (new_limit == UINT64_MAX) { + CLS_LOG(20, "remove snapshot limit\n"); + rc = cls_cxx_map_remove_key(hctx, "snap_limit"); + return rc; + } + + //try to read header as v1 format + rc = snap_read_header(hctx, bl); + + // error when reading header + if (rc < 0 && rc != -EINVAL) { + return rc; + } else if (rc >= 0) { + // success, the image is v1 format + struct rbd_obj_header_ondisk *header; + header = (struct rbd_obj_header_ondisk *)bl.c_str(); + snap_count = header->snap_count; + } else { + // else, the image is v2 format + int max_read = RBD_MAX_KEYS_READ; + string last_read = RBD_SNAP_KEY_PREFIX; + bool more; + + do { + set<string> keys; + rc = cls_cxx_map_get_keys(hctx, last_read, max_read, &keys, &more); + if (rc < 0) { + CLS_ERR("error retrieving snapshots: %s", cpp_strerror(rc).c_str()); + return rc; + } + for (auto& key : keys) { + if (key.find(RBD_SNAP_KEY_PREFIX) != 0) + break; + snap_count++; + } + if (!keys.empty()) + last_read = *(keys.rbegin()); + } while (more); + } + + if (new_limit < snap_count) { + rc = -ERANGE; + CLS_LOG(10, "snapshot limit is less than the number of snapshots.\n"); + } else { + CLS_LOG(20, "set snapshot limit to %" PRIu64 "\n", new_limit); + bl.clear(); + encode(new_limit, bl); + rc = cls_cxx_map_set_val(hctx, "snap_limit", &bl); + } + + return rc; +} + + +/** + * Input: + * @param snap id (uint64_t) parent snapshot id + * @param child spec (cls::rbd::ChildImageSpec) child image + * + * Output: + * @returns 0 on success, negative error code on failure + */ +int child_attach(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + uint64_t snap_id; + cls::rbd::ChildImageSpec child_image; + try { + auto it = in->cbegin(); + decode(snap_id, it); + decode(child_image, it); + } catch (const buffer::error &err) { + return -EINVAL; + } + + CLS_LOG(20, "child_attach snap_id=%" PRIu64 ", child_pool_id=%" PRIi64 ", " + "child_image_id=%s", snap_id, child_image.pool_id, + child_image.image_id.c_str()); + + cls_rbd_snap snap; + std::string snapshot_key; + key_from_snap_id(snap_id, &snapshot_key); + int r = read_key(hctx, snapshot_key, &snap); + if (r < 0) { + return r; + } + + if (cls::rbd::get_snap_namespace_type(snap.snapshot_namespace) == + cls::rbd::SNAPSHOT_NAMESPACE_TYPE_TRASH) { + // cannot attach to a deleted snapshot + return -ENOENT; + } + + auto children_key = image::snap_children_key_from_snap_id(snap_id); + cls::rbd::ChildImageSpecs child_images; + r = read_key(hctx, children_key, &child_images); + if (r < 0 && r != -ENOENT) { + CLS_ERR("error reading snapshot children: %s", cpp_strerror(r).c_str()); + return r; + } + + auto it = child_images.insert(child_image); + if (!it.second) { + // child already attached to the snapshot + return -EEXIST; + } + + r = write_key(hctx, children_key, child_images); + if (r < 0) { + CLS_ERR("error writing snapshot children: %s", cpp_strerror(r).c_str()); + return r; + } + + ++snap.child_count; + r = image::snapshot::write(hctx, snapshot_key, std::move(snap)); + if (r < 0) { + return r; + } + + r = image::set_op_features(hctx, RBD_OPERATION_FEATURE_CLONE_PARENT, + RBD_OPERATION_FEATURE_CLONE_PARENT); + if (r < 0) { + return r; + } + + return 0; +} + +/** + * Input: + * @param snap id (uint64_t) parent snapshot id + * @param child spec (cls::rbd::ChildImageSpec) child image + * + * Output: + * @returns 0 on success, negative error code on failure + */ +int child_detach(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + uint64_t snap_id; + cls::rbd::ChildImageSpec child_image; + try { + auto it = in->cbegin(); + decode(snap_id, it); + decode(child_image, it); + } catch (const buffer::error &err) { + return -EINVAL; + } + + CLS_LOG(20, "child_detach snap_id=%" PRIu64 ", child_pool_id=%" PRIi64 ", " + "child_image_id=%s", snap_id, child_image.pool_id, + child_image.image_id.c_str()); + + cls_rbd_snap snap; + std::string snapshot_key; + key_from_snap_id(snap_id, &snapshot_key); + int r = read_key(hctx, snapshot_key, &snap); + if (r < 0) { + return r; + } + + auto children_key = image::snap_children_key_from_snap_id(snap_id); + cls::rbd::ChildImageSpecs child_images; + r = read_key(hctx, children_key, &child_images); + if (r < 0 && r != -ENOENT) { + CLS_ERR("error reading snapshot children: %s", cpp_strerror(r).c_str()); + return r; + } + + if (snap.child_count != child_images.size()) { + // children and reference count don't match + CLS_ERR("children reference count mismatch: %" PRIu64, snap_id); + return -EINVAL; + } + + if (child_images.erase(child_image) == 0) { + // child not attached to the snapshot + return -ENOENT; + } + + if (child_images.empty()) { + r = remove_key(hctx, children_key); + } else { + r = write_key(hctx, children_key, child_images); + if (r < 0) { + CLS_ERR("error writing snapshot children: %s", cpp_strerror(r).c_str()); + return r; + } + } + + --snap.child_count; + r = image::snapshot::write(hctx, snapshot_key, std::move(snap)); + if (r < 0) { + return r; + } + + if (snap.child_count == 0) { + auto clone_in_use_lambda = [snap_id](const cls_rbd_snap& snap_meta) { + if (snap_meta.id != snap_id && snap_meta.child_count > 0) { + return -EEXIST; + } + return 0; + }; + + r = image::snapshot::iterate(hctx, clone_in_use_lambda); + if (r < 0 && r != -EEXIST) { + return r; + } + + if (r != -EEXIST) { + // remove the clone_v2 op feature if not in-use by any other snapshots + r = image::set_op_features(hctx, 0, RBD_OPERATION_FEATURE_CLONE_PARENT); + if (r < 0) { + return r; + } + } + } + + return 0; +} + +/** + * Input: + * @param snap id (uint64_t) parent snapshot id + * + * Output: + * @param (cls::rbd::ChildImageSpecs) child images + * @returns 0 on success, negative error code on failure + */ +int children_list(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + uint64_t snap_id; + try { + auto it = in->cbegin(); + decode(snap_id, it); + } catch (const buffer::error &err) { + return -EINVAL; + } + + CLS_LOG(20, "child_detach snap_id=%" PRIu64, snap_id); + + cls_rbd_snap snap; + std::string snapshot_key; + key_from_snap_id(snap_id, &snapshot_key); + int r = read_key(hctx, snapshot_key, &snap); + if (r < 0) { + return r; + } + + auto children_key = image::snap_children_key_from_snap_id(snap_id); + cls::rbd::ChildImageSpecs child_images; + r = read_key(hctx, children_key, &child_images); + if (r == -ENOENT) { + return r; + } else if (r < 0) { + CLS_ERR("error reading snapshot children: %s", cpp_strerror(r).c_str()); + return r; + } + + encode(child_images, *out); + return 0; +} + +/** + * Set image migration. + * + * Input: + * @param migration_spec (cls::rbd::MigrationSpec) image migration spec + * + * Output: + * + * @returns 0 on success, negative error code on failure + */ +int migration_set(cls_method_context_t hctx, bufferlist *in, bufferlist *out) { + cls::rbd::MigrationSpec migration_spec; + try { + auto it = in->cbegin(); + decode(migration_spec, it); + } catch (const buffer::error &err) { + return -EINVAL; + } + + int r = image::set_migration(hctx, migration_spec, true); + if (r < 0) { + return r; + } + + return 0; +} + +/** + * Set image migration state. + * + * Input: + * @param state (cls::rbd::MigrationState) migration state + * @param description (std::string) migration state description + * + * Output: + * + * @returns 0 on success, negative error code on failure + */ +int migration_set_state(cls_method_context_t hctx, bufferlist *in, + bufferlist *out) { + cls::rbd::MigrationState state; + std::string description; + try { + auto it = in->cbegin(); + decode(state, it); + decode(description, it); + } catch (const buffer::error &err) { + return -EINVAL; + } + + cls::rbd::MigrationSpec migration_spec; + int r = image::read_migration(hctx, &migration_spec); + if (r < 0) { + return r; + } + + migration_spec.state = state; + migration_spec.state_description = description; + + r = image::set_migration(hctx, migration_spec, false); + if (r < 0) { + return r; + } + + return 0; +} + +/** + * Get image migration spec. + * + * Input: + * + * Output: + * @param migration_spec (cls::rbd::MigrationSpec) image migration spec + * + * @returns 0 on success, negative error code on failure + */ +int migration_get(cls_method_context_t hctx, bufferlist *in, bufferlist *out) { + cls::rbd::MigrationSpec migration_spec; + int r = image::read_migration(hctx, &migration_spec); + if (r < 0) { + return r; + } + + encode(migration_spec, *out); + + return 0; +} + +/** + * Remove image migration spec. + * + * Input: + * + * Output: + * + * @returns 0 on success, negative error code on failure + */ +int migration_remove(cls_method_context_t hctx, bufferlist *in, + bufferlist *out) { + int r = image::remove_migration(hctx); + if (r < 0) { + return r; + } + + return 0; +} + +/** + * Ensure writer snapc state + * + * Input: + * @param snap id (uint64_t) snap context sequence id + * @param state (cls::rbd::AssertSnapcSeqState) snap context state + * + * Output: + * @returns -ERANGE if assertion fails + * @returns 0 on success, negative error code on failure + */ +int assert_snapc_seq(cls_method_context_t hctx, bufferlist *in, + bufferlist *out) +{ + uint64_t snapc_seq; + cls::rbd::AssertSnapcSeqState state; + try { + auto it = in->cbegin(); + decode(snapc_seq, it); + decode(state, it); + } catch (const buffer::error &err) { + return -EINVAL; + } + + uint64_t snapset_seq; + int r = cls_get_snapset_seq(hctx, &snapset_seq); + if (r < 0 && r != -ENOENT) { + return r; + } + + switch (state) { + case cls::rbd::ASSERT_SNAPC_SEQ_GT_SNAPSET_SEQ: + return (r == -ENOENT || snapc_seq > snapset_seq) ? 0 : -ERANGE; + case cls::rbd::ASSERT_SNAPC_SEQ_LE_SNAPSET_SEQ: + return (r == -ENOENT || snapc_seq > snapset_seq) ? -ERANGE : 0; + default: + return -EOPNOTSUPP; + } +} + +/****************************** Old format *******************************/ + +int old_snapshots_list(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + bufferlist bl; + struct rbd_obj_header_ondisk *header; + int rc = snap_read_header(hctx, bl); + if (rc < 0) + return rc; + + header = (struct rbd_obj_header_ondisk *)bl.c_str(); + bufferptr p(header->snap_names_len); + char *buf = (char *)header; + char *name = buf + sizeof(*header) + header->snap_count * sizeof(struct rbd_obj_snap_ondisk); + char *end = name + header->snap_names_len; + memcpy(p.c_str(), + buf + sizeof(*header) + header->snap_count * sizeof(struct rbd_obj_snap_ondisk), + header->snap_names_len); + + encode(header->snap_seq, *out); + encode(header->snap_count, *out); + + for (unsigned i = 0; i < header->snap_count; i++) { + string s = name; + encode(header->snaps[i].id, *out); + encode(header->snaps[i].image_size, *out); + encode(s, *out); + + name += strlen(name) + 1; + if (name > end) + return -EIO; + } + + return 0; +} + +int old_snapshot_add(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + bufferlist bl; + struct rbd_obj_header_ondisk *header; + bufferlist newbl; + bufferptr header_bp(sizeof(*header)); + struct rbd_obj_snap_ondisk *new_snaps; + + int rc = snap_read_header(hctx, bl); + if (rc < 0) + return rc; + + header = (struct rbd_obj_header_ondisk *)bl.c_str(); + + int snaps_id_ofs = sizeof(*header); + int names_ofs = snaps_id_ofs + sizeof(*new_snaps) * header->snap_count; + const char *snap_name; + const char *snap_names = ((char *)header) + names_ofs; + const char *end = snap_names + header->snap_names_len; + auto iter = in->cbegin(); + string s; + uint64_t snap_id; + + try { + decode(s, iter); + decode(snap_id, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + snap_name = s.c_str(); + + if (header->snap_seq > snap_id) + return -ESTALE; + + uint64_t snap_limit; + rc = read_key(hctx, "snap_limit", &snap_limit); + if (rc == -ENOENT) { + snap_limit = UINT64_MAX; + } else if (rc < 0) { + return rc; + } + + if (header->snap_count >= snap_limit) + return -EDQUOT; + + const char *cur_snap_name; + for (cur_snap_name = snap_names; cur_snap_name < end; cur_snap_name += strlen(cur_snap_name) + 1) { + if (strncmp(cur_snap_name, snap_name, end - cur_snap_name) == 0) + return -EEXIST; + } + if (cur_snap_name > end) + return -EIO; + + int snap_name_len = strlen(snap_name); + + bufferptr new_names_bp(header->snap_names_len + snap_name_len + 1); + bufferptr new_snaps_bp(sizeof(*new_snaps) * (header->snap_count + 1)); + + /* copy snap names and append to new snap name */ + char *new_snap_names = new_names_bp.c_str(); + strcpy(new_snap_names, snap_name); + memcpy(new_snap_names + snap_name_len + 1, snap_names, header->snap_names_len); + + /* append new snap id */ + new_snaps = (struct rbd_obj_snap_ondisk *)new_snaps_bp.c_str(); + memcpy(new_snaps + 1, header->snaps, sizeof(*new_snaps) * header->snap_count); + + header->snap_count = header->snap_count + 1; + header->snap_names_len = header->snap_names_len + snap_name_len + 1; + header->snap_seq = snap_id; + + new_snaps[0].id = snap_id; + new_snaps[0].image_size = header->image_size; + + memcpy(header_bp.c_str(), header, sizeof(*header)); + + newbl.push_back(header_bp); + newbl.push_back(new_snaps_bp); + newbl.push_back(new_names_bp); + + rc = cls_cxx_write_full(hctx, &newbl); + if (rc < 0) + return rc; + + return 0; +} + +int old_snapshot_remove(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + bufferlist bl; + struct rbd_obj_header_ondisk *header; + bufferlist newbl; + bufferptr header_bp(sizeof(*header)); + + int rc = snap_read_header(hctx, bl); + if (rc < 0) + return rc; + + header = (struct rbd_obj_header_ondisk *)bl.c_str(); + + int snaps_id_ofs = sizeof(*header); + int names_ofs = snaps_id_ofs + sizeof(struct rbd_obj_snap_ondisk) * header->snap_count; + const char *snap_name; + const char *snap_names = ((char *)header) + names_ofs; + const char *orig_names = snap_names; + const char *end = snap_names + header->snap_names_len; + auto iter = in->cbegin(); + string s; + unsigned i; + bool found = false; + struct rbd_obj_snap_ondisk snap; + + try { + decode(s, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + snap_name = s.c_str(); + + for (i = 0; snap_names < end; i++) { + if (strcmp(snap_names, snap_name) == 0) { + snap = header->snaps[i]; + found = true; + break; + } + snap_names += strlen(snap_names) + 1; + } + if (!found) { + CLS_ERR("couldn't find snap %s\n", snap_name); + return -ENOENT; + } + + header->snap_names_len = header->snap_names_len - (s.length() + 1); + header->snap_count = header->snap_count - 1; + + bufferptr new_names_bp(header->snap_names_len); + bufferptr new_snaps_bp(sizeof(header->snaps[0]) * header->snap_count); + + memcpy(header_bp.c_str(), header, sizeof(*header)); + newbl.push_back(header_bp); + + if (header->snap_count) { + int snaps_len = 0; + int names_len = 0; + CLS_LOG(20, "i=%u\n", i); + if (i > 0) { + snaps_len = sizeof(header->snaps[0]) * i; + names_len = snap_names - orig_names; + memcpy(new_snaps_bp.c_str(), header->snaps, snaps_len); + memcpy(new_names_bp.c_str(), orig_names, names_len); + } + snap_names += s.length() + 1; + + if (i < header->snap_count) { + memcpy(new_snaps_bp.c_str() + snaps_len, + header->snaps + i + 1, + sizeof(header->snaps[0]) * (header->snap_count - i)); + memcpy(new_names_bp.c_str() + names_len, snap_names , end - snap_names); + } + newbl.push_back(new_snaps_bp); + newbl.push_back(new_names_bp); + } + + rc = cls_cxx_write_full(hctx, &newbl); + if (rc < 0) + return rc; + + return 0; +} + +/** + * rename snapshot of old format. + * + * Input: + * @param src_snap_id old snap id of the snapshot (snapid_t) + * @param dst_snap_name new name of the snapshot (string) + * + * Output: + * @returns 0 on success, negative error code on failure. +*/ +int old_snapshot_rename(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + bufferlist bl; + struct rbd_obj_header_ondisk *header; + bufferlist newbl; + bufferptr header_bp(sizeof(*header)); + snapid_t src_snap_id; + const char *dst_snap_name; + string dst; + + int rc = snap_read_header(hctx, bl); + if (rc < 0) + return rc; + + header = (struct rbd_obj_header_ondisk *)bl.c_str(); + + int snaps_id_ofs = sizeof(*header); + int names_ofs = snaps_id_ofs + sizeof(rbd_obj_snap_ondisk) * header->snap_count; + const char *snap_names = ((char *)header) + names_ofs; + const char *orig_names = snap_names; + const char *end = snap_names + header->snap_names_len; + auto iter = in->cbegin(); + unsigned i; + bool found = false; + + try { + decode(src_snap_id, iter); + decode(dst, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + dst_snap_name = dst.c_str(); + + const char *cur_snap_name; + for (cur_snap_name = snap_names; cur_snap_name < end; + cur_snap_name += strlen(cur_snap_name) + 1) { + if (strcmp(cur_snap_name, dst_snap_name) == 0) + return -EEXIST; + } + if (cur_snap_name > end) + return -EIO; + for (i = 0; i < header->snap_count; i++) { + if (src_snap_id == header->snaps[i].id) { + found = true; + break; + } + snap_names += strlen(snap_names) + 1; + } + if (!found) { + CLS_ERR("couldn't find snap %llu\n", (unsigned long long)src_snap_id.val); + return -ENOENT; + } + + CLS_LOG(20, "rename snap with snap id %llu to dest name %s", (unsigned long long)src_snap_id.val, dst_snap_name); + header->snap_names_len = header->snap_names_len - strlen(snap_names) + dst.length(); + + bufferptr new_names_bp(header->snap_names_len); + bufferptr new_snaps_bp(sizeof(header->snaps[0]) * header->snap_count); + + if (header->snap_count) { + int names_len = 0; + CLS_LOG(20, "i=%u\n", i); + if (i > 0) { + names_len = snap_names - orig_names; + memcpy(new_names_bp.c_str(), orig_names, names_len); + } + strcpy(new_names_bp.c_str() + names_len, dst_snap_name); + names_len += strlen(dst_snap_name) + 1; + snap_names += strlen(snap_names) + 1; + if (i < header->snap_count) { + memcpy(new_names_bp.c_str() + names_len, snap_names , end - snap_names); + } + memcpy(new_snaps_bp.c_str(), header->snaps, sizeof(header->snaps[0]) * header->snap_count); + } + + memcpy(header_bp.c_str(), header, sizeof(*header)); + newbl.push_back(header_bp); + newbl.push_back(new_snaps_bp); + newbl.push_back(new_names_bp); + + rc = cls_cxx_write_full(hctx, &newbl); + if (rc < 0) + return rc; + return 0; +} + + +namespace mirror { + +static const std::string UUID("mirror_uuid"); +static const std::string MODE("mirror_mode"); +static const std::string PEER_KEY_PREFIX("mirror_peer_"); +static const std::string IMAGE_KEY_PREFIX("image_"); +static const std::string GLOBAL_KEY_PREFIX("global_"); +static const std::string STATUS_GLOBAL_KEY_PREFIX("status_global_"); +static const std::string INSTANCE_KEY_PREFIX("instance_"); +static const std::string MIRROR_IMAGE_MAP_KEY_PREFIX("image_map_"); + +std::string peer_key(const std::string &uuid) { + return PEER_KEY_PREFIX + uuid; +} + +std::string image_key(const string &image_id) { + return IMAGE_KEY_PREFIX + image_id; +} + +std::string global_key(const string &global_id) { + return GLOBAL_KEY_PREFIX + global_id; +} + +std::string status_global_key(const string &global_id) { + return STATUS_GLOBAL_KEY_PREFIX + global_id; +} + +std::string instance_key(const string &instance_id) { + return INSTANCE_KEY_PREFIX + instance_id; +} + +std::string mirror_image_map_key(const string& global_image_id) { + return MIRROR_IMAGE_MAP_KEY_PREFIX + global_image_id; +} + +int uuid_get(cls_method_context_t hctx, std::string *mirror_uuid) { + bufferlist mirror_uuid_bl; + int r = cls_cxx_map_get_val(hctx, mirror::UUID, &mirror_uuid_bl); + if (r < 0) { + if (r != -ENOENT) { + CLS_ERR("error reading mirror uuid: %s", cpp_strerror(r).c_str()); + } + return r; + } + + *mirror_uuid = std::string(mirror_uuid_bl.c_str(), mirror_uuid_bl.length()); + return 0; +} + +void sanitize_entity_inst(entity_inst_t* entity_inst) { + // make all addrs of type ANY because the type isn't what uniquely + // identifies them and clients and on-disk formats can be encoded + // with different backwards compatibility settings. + entity_inst->addr.set_type(entity_addr_t::TYPE_ANY); +} + +int list_watchers(cls_method_context_t hctx, + std::set<entity_inst_t> *entities) { + obj_list_watch_response_t watchers; + int r = cls_cxx_list_watchers(hctx, &watchers); + if (r < 0 && r != -ENOENT) { + CLS_ERR("error listing watchers: '%s'", cpp_strerror(r).c_str()); + return r; + } + + entities->clear(); + for (auto &w : watchers.entries) { + entity_inst_t entity_inst{w.name, w.addr}; + sanitize_entity_inst(&entity_inst); + + entities->insert(entity_inst); + } + return 0; +} + +int read_peers(cls_method_context_t hctx, + std::vector<cls::rbd::MirrorPeer> *peers) { + std::string last_read = PEER_KEY_PREFIX; + int max_read = RBD_MAX_KEYS_READ; + bool more = true; + while (more) { + std::map<std::string, bufferlist> vals; + int r = cls_cxx_map_get_vals(hctx, last_read, PEER_KEY_PREFIX.c_str(), + max_read, &vals, &more); + if (r < 0) { + if (r != -ENOENT) { + CLS_ERR("error reading peers: %s", cpp_strerror(r).c_str()); + } + return r; + } + + for (auto &it : vals) { + try { + auto bl_it = it.second.cbegin(); + cls::rbd::MirrorPeer peer; + decode(peer, bl_it); + peers->push_back(peer); + } catch (const buffer::error &err) { + CLS_ERR("could not decode peer '%s'", it.first.c_str()); + return -EIO; + } + } + + if (!vals.empty()) { + last_read = vals.rbegin()->first; + } + } + return 0; +} + +int read_peer(cls_method_context_t hctx, const std::string &id, + cls::rbd::MirrorPeer *peer) { + bufferlist bl; + int r = cls_cxx_map_get_val(hctx, peer_key(id), &bl); + if (r < 0) { + CLS_ERR("error reading peer '%s': %s", id.c_str(), + cpp_strerror(r).c_str()); + return r; + } + + try { + auto bl_it = bl.cbegin(); + decode(*peer, bl_it); + } catch (const buffer::error &err) { + CLS_ERR("could not decode peer '%s'", id.c_str()); + return -EIO; + } + return 0; +} + +int write_peer(cls_method_context_t hctx, const std::string &id, + const cls::rbd::MirrorPeer &peer) { + bufferlist bl; + encode(peer, bl); + + int r = cls_cxx_map_set_val(hctx, peer_key(id), &bl); + if (r < 0) { + CLS_ERR("error writing peer '%s': %s", id.c_str(), + cpp_strerror(r).c_str()); + return r; + } + return 0; +} + +int image_get(cls_method_context_t hctx, const string &image_id, + cls::rbd::MirrorImage *mirror_image) { + bufferlist bl; + int r = cls_cxx_map_get_val(hctx, image_key(image_id), &bl); + if (r < 0) { + if (r != -ENOENT) { + CLS_ERR("error reading mirrored image '%s': '%s'", image_id.c_str(), + cpp_strerror(r).c_str()); + } + return r; + } + + try { + auto it = bl.cbegin(); + decode(*mirror_image, it); + } catch (const buffer::error &err) { + CLS_ERR("could not decode mirrored image '%s'", image_id.c_str()); + return -EIO; + } + + return 0; +} + +int image_set(cls_method_context_t hctx, const string &image_id, + const cls::rbd::MirrorImage &mirror_image) { + bufferlist bl; + encode(mirror_image, bl); + + cls::rbd::MirrorImage existing_mirror_image; + int r = image_get(hctx, image_id, &existing_mirror_image); + if (r == -ENOENT) { + // make sure global id doesn't already exist + std::string global_id_key = global_key(mirror_image.global_image_id); + std::string image_id; + r = read_key(hctx, global_id_key, &image_id); + if (r >= 0) { + return -EEXIST; + } else if (r != -ENOENT) { + CLS_ERR("error reading global image id: '%s': '%s'", image_id.c_str(), + cpp_strerror(r).c_str()); + return r; + } + + // make sure this was not a race for disabling + if (mirror_image.state == cls::rbd::MIRROR_IMAGE_STATE_DISABLING) { + CLS_ERR("image '%s' is already disabled", image_id.c_str()); + return r; + } + } else if (r < 0) { + CLS_ERR("error reading mirrored image '%s': '%s'", image_id.c_str(), + cpp_strerror(r).c_str()); + return r; + } else if (existing_mirror_image.global_image_id != + mirror_image.global_image_id) { + // cannot change the global id + return -EINVAL; + } + + r = cls_cxx_map_set_val(hctx, image_key(image_id), &bl); + if (r < 0) { + CLS_ERR("error adding mirrored image '%s': %s", image_id.c_str(), + cpp_strerror(r).c_str()); + return r; + } + + bufferlist image_id_bl; + encode(image_id, image_id_bl); + r = cls_cxx_map_set_val(hctx, global_key(mirror_image.global_image_id), + &image_id_bl); + if (r < 0) { + CLS_ERR("error adding global id for image '%s': %s", image_id.c_str(), + cpp_strerror(r).c_str()); + return r; + } + return 0; +} + +int image_remove(cls_method_context_t hctx, const string &image_id) { + bufferlist bl; + cls::rbd::MirrorImage mirror_image; + int r = image_get(hctx, image_id, &mirror_image); + if (r < 0) { + if (r != -ENOENT) { + CLS_ERR("error reading mirrored image '%s': '%s'", image_id.c_str(), + cpp_strerror(r).c_str()); + } + return r; + } + + if (mirror_image.state != cls::rbd::MIRROR_IMAGE_STATE_DISABLING) { + return -EBUSY; + } + + r = cls_cxx_map_remove_key(hctx, image_key(image_id)); + if (r < 0) { + CLS_ERR("error removing mirrored image '%s': %s", image_id.c_str(), + cpp_strerror(r).c_str()); + return r; + } + + r = cls_cxx_map_remove_key(hctx, global_key(mirror_image.global_image_id)); + if (r < 0 && r != -ENOENT) { + CLS_ERR("error removing global id for image '%s': %s", image_id.c_str(), + cpp_strerror(r).c_str()); + return r; + } + + r = cls_cxx_map_remove_key(hctx, + status_global_key(mirror_image.global_image_id)); + if (r < 0 && r != -ENOENT) { + CLS_ERR("error removing global status for image '%s': %s", image_id.c_str(), + cpp_strerror(r).c_str()); + return r; + } + + return 0; +} + +struct MirrorImageStatusOnDisk : cls::rbd::MirrorImageStatus { + entity_inst_t origin; + + MirrorImageStatusOnDisk() { + } + MirrorImageStatusOnDisk(const cls::rbd::MirrorImageStatus &status) : + cls::rbd::MirrorImageStatus(status) { + } + + void encode_meta(bufferlist &bl, uint64_t features) const { + ENCODE_START(1, 1, bl); + auto sanitized_origin = origin; + sanitize_entity_inst(&sanitized_origin); + encode(sanitized_origin, bl, features); + ENCODE_FINISH(bl); + } + + void encode(bufferlist &bl, uint64_t features) const { + encode_meta(bl, features); + cls::rbd::MirrorImageStatus::encode(bl); + } + + void decode_meta(bufferlist::const_iterator &it) { + DECODE_START(1, it); + decode(origin, it); + sanitize_entity_inst(&origin); + DECODE_FINISH(it); + } + + void decode(bufferlist::const_iterator &it) { + decode_meta(it); + cls::rbd::MirrorImageStatus::decode(it); + } +}; +WRITE_CLASS_ENCODER_FEATURES(MirrorImageStatusOnDisk) + +int image_status_set(cls_method_context_t hctx, const string &global_image_id, + const cls::rbd::MirrorImageStatus &status) { + MirrorImageStatusOnDisk ondisk_status(status); + ondisk_status.up = false; + ondisk_status.last_update = ceph_clock_now(); + + int r = cls_get_request_origin(hctx, &ondisk_status.origin); + ceph_assert(r == 0); + + bufferlist bl; + encode(ondisk_status, bl, cls_get_features(hctx)); + + r = cls_cxx_map_set_val(hctx, status_global_key(global_image_id), &bl); + if (r < 0) { + CLS_ERR("error setting status for mirrored image, global id '%s': %s", + global_image_id.c_str(), cpp_strerror(r).c_str()); + return r; + } + return 0; +} + +int image_status_remove(cls_method_context_t hctx, + const string &global_image_id) { + + int r = cls_cxx_map_remove_key(hctx, status_global_key(global_image_id)); + if (r < 0) { + CLS_ERR("error removing status for mirrored image, global id '%s': %s", + global_image_id.c_str(), cpp_strerror(r).c_str()); + return r; + } + return 0; +} + +int image_status_get(cls_method_context_t hctx, const string &global_image_id, + const std::set<entity_inst_t> &watchers, + cls::rbd::MirrorImageStatus *status) { + + bufferlist bl; + int r = cls_cxx_map_get_val(hctx, status_global_key(global_image_id), &bl); + if (r < 0) { + if (r != -ENOENT) { + CLS_ERR("error reading status for mirrored image, global id '%s': '%s'", + global_image_id.c_str(), cpp_strerror(r).c_str()); + } + return r; + } + + MirrorImageStatusOnDisk ondisk_status; + try { + auto it = bl.cbegin(); + decode(ondisk_status, it); + } catch (const buffer::error &err) { + CLS_ERR("could not decode status for mirrored image, global id '%s'", + global_image_id.c_str()); + return -EIO; + } + + + *status = static_cast<cls::rbd::MirrorImageStatus>(ondisk_status); + status->up = (watchers.find(ondisk_status.origin) != watchers.end()); + return 0; +} + +int image_status_list(cls_method_context_t hctx, + const std::string &start_after, uint64_t max_return, + map<std::string, cls::rbd::MirrorImage> *mirror_images, + map<std::string, cls::rbd::MirrorImageStatus> *mirror_statuses) { + std::string last_read = image_key(start_after); + int max_read = RBD_MAX_KEYS_READ; + bool more = true; + + std::set<entity_inst_t> watchers; + int r = list_watchers(hctx, &watchers); + if (r < 0) { + return r; + } + + while (more && mirror_images->size() < max_return) { + std::map<std::string, bufferlist> vals; + CLS_LOG(20, "last_read = '%s'", last_read.c_str()); + r = cls_cxx_map_get_vals(hctx, last_read, IMAGE_KEY_PREFIX, max_read, &vals, + &more); + if (r < 0) { + if (r != -ENOENT) { + CLS_ERR("error reading mirror image directory by name: %s", + cpp_strerror(r).c_str()); + } + return r; + } + + for (auto it = vals.begin(); it != vals.end() && + mirror_images->size() < max_return; ++it) { + const std::string &image_id = it->first.substr(IMAGE_KEY_PREFIX.size()); + cls::rbd::MirrorImage mirror_image; + auto iter = it->second.cbegin(); + try { + decode(mirror_image, iter); + } catch (const buffer::error &err) { + CLS_ERR("could not decode mirror image payload of image '%s'", + image_id.c_str()); + return -EIO; + } + + (*mirror_images)[image_id] = mirror_image; + + cls::rbd::MirrorImageStatus status; + int r1 = image_status_get(hctx, mirror_image.global_image_id, watchers, + &status); + if (r1 < 0) { + continue; + } + + (*mirror_statuses)[image_id] = status; + } + if (!vals.empty()) { + last_read = image_key(mirror_images->rbegin()->first); + } + } + + return 0; +} + +int image_status_get_summary( + cls_method_context_t hctx, + std::map<cls::rbd::MirrorImageStatusState, int> *states) { + std::set<entity_inst_t> watchers; + int r = list_watchers(hctx, &watchers); + if (r < 0) { + return r; + } + + states->clear(); + + string last_read = IMAGE_KEY_PREFIX; + int max_read = RBD_MAX_KEYS_READ; + bool more = true; + while (more) { + map<string, bufferlist> vals; + r = cls_cxx_map_get_vals(hctx, last_read, IMAGE_KEY_PREFIX, + max_read, &vals, &more); + if (r < 0) { + if (r != -ENOENT) { + CLS_ERR("error reading mirrored images: %s", cpp_strerror(r).c_str()); + } + return r; + } + + for (auto &list_it : vals) { + const string &key = list_it.first; + + if (0 != key.compare(0, IMAGE_KEY_PREFIX.size(), IMAGE_KEY_PREFIX)) { + break; + } + + cls::rbd::MirrorImage mirror_image; + auto iter = list_it.second.cbegin(); + try { + decode(mirror_image, iter); + } catch (const buffer::error &err) { + CLS_ERR("could not decode mirror image payload for key '%s'", + key.c_str()); + return -EIO; + } + + cls::rbd::MirrorImageStatus status; + image_status_get(hctx, mirror_image.global_image_id, watchers, &status); + + cls::rbd::MirrorImageStatusState state = status.up ? status.state : + cls::rbd::MIRROR_IMAGE_STATUS_STATE_UNKNOWN; + (*states)[state]++; + } + + if (!vals.empty()) { + last_read = vals.rbegin()->first; + } + } + + return 0; +} + +int image_status_remove_down(cls_method_context_t hctx) { + std::set<entity_inst_t> watchers; + int r = list_watchers(hctx, &watchers); + if (r < 0) { + return r; + } + + string last_read = STATUS_GLOBAL_KEY_PREFIX; + int max_read = RBD_MAX_KEYS_READ; + bool more = true; + while (more) { + map<string, bufferlist> vals; + r = cls_cxx_map_get_vals(hctx, last_read, STATUS_GLOBAL_KEY_PREFIX, + max_read, &vals, &more); + if (r < 0) { + if (r != -ENOENT) { + CLS_ERR("error reading mirrored images: %s", cpp_strerror(r).c_str()); + } + return r; + } + + for (auto &list_it : vals) { + const string &key = list_it.first; + + if (0 != key.compare(0, STATUS_GLOBAL_KEY_PREFIX.size(), + STATUS_GLOBAL_KEY_PREFIX)) { + break; + } + + MirrorImageStatusOnDisk status; + try { + auto it = list_it.second.cbegin(); + status.decode_meta(it); + } catch (const buffer::error &err) { + CLS_ERR("could not decode status metadata for mirrored image '%s'", + key.c_str()); + return -EIO; + } + + if (watchers.find(status.origin) == watchers.end()) { + CLS_LOG(20, "removing stale status object for key %s", + key.c_str()); + int r1 = cls_cxx_map_remove_key(hctx, key); + if (r1 < 0) { + CLS_ERR("error removing stale status for key '%s': %s", + key.c_str(), cpp_strerror(r1).c_str()); + return r1; + } + } + } + + if (!vals.empty()) { + last_read = vals.rbegin()->first; + } + } + + return 0; +} + +int image_instance_get(cls_method_context_t hctx, + const string &global_image_id, + const std::set<entity_inst_t> &watchers, + entity_inst_t *instance) { + bufferlist bl; + int r = cls_cxx_map_get_val(hctx, status_global_key(global_image_id), &bl); + if (r < 0) { + if (r != -ENOENT) { + CLS_ERR("error reading status for mirrored image, global id '%s': '%s'", + global_image_id.c_str(), cpp_strerror(r).c_str()); + } + return r; + } + + MirrorImageStatusOnDisk ondisk_status; + try { + auto it = bl.cbegin(); + decode(ondisk_status, it); + } catch (const buffer::error &err) { + CLS_ERR("could not decode status for mirrored image, global id '%s'", + global_image_id.c_str()); + return -EIO; + } + + if (watchers.find(ondisk_status.origin) == watchers.end()) { + return -ESTALE; + } + + *instance = ondisk_status.origin; + return 0; +} + +int image_instance_list(cls_method_context_t hctx, + const std::string &start_after, + uint64_t max_return, + map<std::string, entity_inst_t> *instances) { + std::string last_read = image_key(start_after); + int max_read = RBD_MAX_KEYS_READ; + bool more = true; + + std::set<entity_inst_t> watchers; + int r = list_watchers(hctx, &watchers); + if (r < 0) { + return r; + } + + while (more && instances->size() < max_return) { + std::map<std::string, bufferlist> vals; + CLS_LOG(20, "last_read = '%s'", last_read.c_str()); + r = cls_cxx_map_get_vals(hctx, last_read, IMAGE_KEY_PREFIX, max_read, &vals, + &more); + if (r < 0) { + if (r != -ENOENT) { + CLS_ERR("error reading mirror image directory by name: %s", + cpp_strerror(r).c_str()); + } + return r; + } + + for (auto it = vals.begin(); it != vals.end() && + instances->size() < max_return; ++it) { + const std::string &image_id = it->first.substr(IMAGE_KEY_PREFIX.size()); + cls::rbd::MirrorImage mirror_image; + auto iter = it->second.cbegin(); + try { + decode(mirror_image, iter); + } catch (const buffer::error &err) { + CLS_ERR("could not decode mirror image payload of image '%s'", + image_id.c_str()); + return -EIO; + } + + entity_inst_t instance; + r = image_instance_get(hctx, mirror_image.global_image_id, watchers, + &instance); + if (r < 0) { + continue; + } + + (*instances)[image_id] = instance; + } + if (!vals.empty()) { + last_read = vals.rbegin()->first; + } + } + + return 0; +} + +int instances_list(cls_method_context_t hctx, + std::vector<std::string> *instance_ids) { + std::string last_read = INSTANCE_KEY_PREFIX; + int max_read = RBD_MAX_KEYS_READ; + bool more = true; + while (more) { + std::map<std::string, bufferlist> vals; + int r = cls_cxx_map_get_vals(hctx, last_read, INSTANCE_KEY_PREFIX.c_str(), + max_read, &vals, &more); + if (r < 0) { + if (r != -ENOENT) { + CLS_ERR("error reading mirror instances: %s", cpp_strerror(r).c_str()); + } + return r; + } + + for (auto &it : vals) { + instance_ids->push_back(it.first.substr(INSTANCE_KEY_PREFIX.size())); + } + + if (!vals.empty()) { + last_read = vals.rbegin()->first; + } + } + return 0; +} + +int instances_add(cls_method_context_t hctx, const string &instance_id) { + bufferlist bl; + + int r = cls_cxx_map_set_val(hctx, instance_key(instance_id), &bl); + if (r < 0) { + CLS_ERR("error setting mirror instance %s: %s", instance_id.c_str(), + cpp_strerror(r).c_str()); + return r; + } + return 0; +} + +int instances_remove(cls_method_context_t hctx, const string &instance_id) { + + int r = cls_cxx_map_remove_key(hctx, instance_key(instance_id)); + if (r < 0) { + CLS_ERR("error removing mirror instance %s: %s", instance_id.c_str(), + cpp_strerror(r).c_str()); + return r; + } + return 0; +} + +int mirror_image_map_list(cls_method_context_t hctx, + const std::string &start_after, + uint64_t max_return, + std::map<std::string, cls::rbd::MirrorImageMap> *image_mapping) { + bool more = true; + std::string last_read = mirror_image_map_key(start_after); + + while (more && image_mapping->size() < max_return) { + std::map<std::string, bufferlist> vals; + CLS_LOG(20, "last read: '%s'", last_read.c_str()); + + int max_read = std::min<uint64_t>(RBD_MAX_KEYS_READ, max_return - image_mapping->size()); + int r = cls_cxx_map_get_vals(hctx, last_read, MIRROR_IMAGE_MAP_KEY_PREFIX, + max_read, &vals, &more); + if (r < 0) { + CLS_ERR("error reading image map: %s", cpp_strerror(r).c_str()); + return r; + } + + if (vals.empty()) { + return 0; + } + + for (auto it = vals.begin(); it != vals.end(); ++it) { + const std::string &global_image_id = + it->first.substr(MIRROR_IMAGE_MAP_KEY_PREFIX.size()); + + cls::rbd::MirrorImageMap mirror_image_map; + auto iter = it->second.cbegin(); + try { + decode(mirror_image_map, iter); + } catch (const buffer::error &err) { + CLS_ERR("could not decode image map payload: %s", + cpp_strerror(r).c_str()); + return -EINVAL; + } + + image_mapping->insert(std::make_pair(global_image_id, mirror_image_map)); + } + + if (!vals.empty()) { + last_read = vals.rbegin()->first; + } + } + + return 0; +} + +} // namespace mirror + +/** + * Input: + * none + * + * Output: + * @param uuid (std::string) + * @returns 0 on success, negative error code on failure + */ +int mirror_uuid_get(cls_method_context_t hctx, bufferlist *in, + bufferlist *out) { + std::string mirror_uuid; + int r = mirror::uuid_get(hctx, &mirror_uuid); + if (r < 0) { + return r; + } + + encode(mirror_uuid, *out); + return 0; +} + +/** + * Input: + * @param mirror_uuid (std::string) + * + * Output: + * @returns 0 on success, negative error code on failure + */ +int mirror_uuid_set(cls_method_context_t hctx, bufferlist *in, + bufferlist *out) { + std::string mirror_uuid; + try { + auto bl_it = in->cbegin(); + decode(mirror_uuid, bl_it); + } catch (const buffer::error &err) { + return -EINVAL; + } + + if (mirror_uuid.empty()) { + CLS_ERR("cannot set empty mirror uuid"); + return -EINVAL; + } + + uint32_t mirror_mode; + int r = read_key(hctx, mirror::MODE, &mirror_mode); + if (r < 0 && r != -ENOENT) { + return r; + } else if (r == 0 && mirror_mode != cls::rbd::MIRROR_MODE_DISABLED) { + CLS_ERR("cannot set mirror uuid while mirroring enabled"); + return -EINVAL; + } + + bufferlist mirror_uuid_bl; + mirror_uuid_bl.append(mirror_uuid); + r = cls_cxx_map_set_val(hctx, mirror::UUID, &mirror_uuid_bl); + if (r < 0) { + CLS_ERR("failed to set mirror uuid"); + return r; + } + return 0; +} + +/** + * Input: + * none + * + * Output: + * @param cls::rbd::MirrorMode (uint32_t) + * @returns 0 on success, negative error code on failure + */ +int mirror_mode_get(cls_method_context_t hctx, bufferlist *in, + bufferlist *out) { + uint32_t mirror_mode_decode; + int r = read_key(hctx, mirror::MODE, &mirror_mode_decode); + if (r < 0) { + return r; + } + + encode(mirror_mode_decode, *out); + return 0; +} + +/** + * Input: + * @param mirror_mode (cls::rbd::MirrorMode) (uint32_t) + * + * Output: + * @returns 0 on success, negative error code on failure + */ +int mirror_mode_set(cls_method_context_t hctx, bufferlist *in, + bufferlist *out) { + uint32_t mirror_mode_decode; + try { + auto bl_it = in->cbegin(); + decode(mirror_mode_decode, bl_it); + } catch (const buffer::error &err) { + return -EINVAL; + } + + bool enabled; + switch (static_cast<cls::rbd::MirrorMode>(mirror_mode_decode)) { + case cls::rbd::MIRROR_MODE_DISABLED: + enabled = false; + break; + case cls::rbd::MIRROR_MODE_IMAGE: + case cls::rbd::MIRROR_MODE_POOL: + enabled = true; + break; + default: + CLS_ERR("invalid mirror mode: %d", mirror_mode_decode); + return -EINVAL; + } + + int r; + if (enabled) { + std::string mirror_uuid; + r = mirror::uuid_get(hctx, &mirror_uuid); + if (r == -ENOENT) { + return -EINVAL; + } else if (r < 0) { + return r; + } + + bufferlist bl; + encode(mirror_mode_decode, bl); + + r = cls_cxx_map_set_val(hctx, mirror::MODE, &bl); + if (r < 0) { + CLS_ERR("error enabling mirroring: %s", cpp_strerror(r).c_str()); + return r; + } + } else { + std::vector<cls::rbd::MirrorPeer> peers; + r = mirror::read_peers(hctx, &peers); + if (r < 0 && r != -ENOENT) { + return r; + } + + if (!peers.empty()) { + CLS_ERR("mirroring peers still registered"); + return -EBUSY; + } + + r = remove_key(hctx, mirror::MODE); + if (r < 0) { + return r; + } + + r = remove_key(hctx, mirror::UUID); + if (r < 0) { + return r; + } + } + return 0; +} + +/** + * Input: + * none + * + * Output: + * @param std::vector<cls::rbd::MirrorPeer>: collection of peers + * @returns 0 on success, negative error code on failure + */ +int mirror_peer_list(cls_method_context_t hctx, bufferlist *in, + bufferlist *out) { + std::vector<cls::rbd::MirrorPeer> peers; + int r = mirror::read_peers(hctx, &peers); + if (r < 0 && r != -ENOENT) { + return r; + } + + encode(peers, *out); + return 0; +} + +/** + * Input: + * @param mirror_peer (cls::rbd::MirrorPeer) + * + * Output: + * @returns 0 on success, negative error code on failure + */ +int mirror_peer_add(cls_method_context_t hctx, bufferlist *in, + bufferlist *out) { + cls::rbd::MirrorPeer mirror_peer; + try { + auto it = in->cbegin(); + decode(mirror_peer, it); + } catch (const buffer::error &err) { + return -EINVAL; + } + + uint32_t mirror_mode_decode; + int r = read_key(hctx, mirror::MODE, &mirror_mode_decode); + if (r < 0 && r != -ENOENT) { + return r; + } else if (r == -ENOENT || + mirror_mode_decode == cls::rbd::MIRROR_MODE_DISABLED) { + CLS_ERR("mirroring must be enabled on the pool"); + return -EINVAL; + } else if (!mirror_peer.is_valid()) { + CLS_ERR("mirror peer is not valid"); + return -EINVAL; + } + + std::string mirror_uuid; + r = mirror::uuid_get(hctx, &mirror_uuid); + if (r < 0) { + CLS_ERR("error retrieving mirroring uuid: %s", cpp_strerror(r).c_str()); + return r; + } else if (mirror_peer.uuid == mirror_uuid) { + CLS_ERR("peer uuid '%s' matches pool mirroring uuid", + mirror_uuid.c_str()); + return -EINVAL; + } + + std::vector<cls::rbd::MirrorPeer> peers; + r = mirror::read_peers(hctx, &peers); + if (r < 0 && r != -ENOENT) { + return r; + } + + for (auto const &peer : peers) { + if (peer.uuid == mirror_peer.uuid) { + CLS_ERR("peer uuid '%s' already exists", + peer.uuid.c_str()); + return -ESTALE; + } else if (peer.cluster_name == mirror_peer.cluster_name && + (peer.pool_id == -1 || mirror_peer.pool_id == -1 || + peer.pool_id == mirror_peer.pool_id)) { + CLS_ERR("peer cluster name '%s' already exists", + peer.cluster_name.c_str()); + return -EEXIST; + } + } + + bufferlist bl; + encode(mirror_peer, bl); + r = cls_cxx_map_set_val(hctx, mirror::peer_key(mirror_peer.uuid), + &bl); + if (r < 0) { + CLS_ERR("error adding peer: %s", cpp_strerror(r).c_str()); + return r; + } + return 0; +} + +/** + * Input: + * @param uuid (std::string) + * + * Output: + * @returns 0 on success, negative error code on failure + */ +int mirror_peer_remove(cls_method_context_t hctx, bufferlist *in, + bufferlist *out) { + std::string uuid; + try { + auto it = in->cbegin(); + decode(uuid, it); + } catch (const buffer::error &err) { + return -EINVAL; + } + + int r = cls_cxx_map_remove_key(hctx, mirror::peer_key(uuid)); + if (r < 0 && r != -ENOENT) { + CLS_ERR("error removing peer: %s", cpp_strerror(r).c_str()); + return r; + } + return 0; +} + +/** + * Input: + * @param uuid (std::string) + * @param client_name (std::string) + * + * Output: + * @returns 0 on success, negative error code on failure + */ +int mirror_peer_set_client(cls_method_context_t hctx, bufferlist *in, + bufferlist *out) { + std::string uuid; + std::string client_name; + try { + auto it = in->cbegin(); + decode(uuid, it); + decode(client_name, it); + } catch (const buffer::error &err) { + return -EINVAL; + } + + cls::rbd::MirrorPeer peer; + int r = mirror::read_peer(hctx, uuid, &peer); + if (r < 0) { + return r; + } + + peer.client_name = client_name; + r = mirror::write_peer(hctx, uuid, peer); + if (r < 0) { + return r; + } + return 0; +} + +/** + * Input: + * @param uuid (std::string) + * @param cluster_name (std::string) + * + * Output: + * @returns 0 on success, negative error code on failure + */ +int mirror_peer_set_cluster(cls_method_context_t hctx, bufferlist *in, + bufferlist *out) { + std::string uuid; + std::string cluster_name; + try { + auto it = in->cbegin(); + decode(uuid, it); + decode(cluster_name, it); + } catch (const buffer::error &err) { + return -EINVAL; + } + + cls::rbd::MirrorPeer peer; + int r = mirror::read_peer(hctx, uuid, &peer); + if (r < 0) { + return r; + } + + peer.cluster_name = cluster_name; + r = mirror::write_peer(hctx, uuid, peer); + if (r < 0) { + return r; + } + return 0; +} + +/** + * Input: + * @param start_after which name to begin listing after + * (use the empty string to start at the beginning) + * @param max_return the maximum number of names to list + * + * Output: + * @param std::map<std::string, std::string>: local id to global id map + * @returns 0 on success, negative error code on failure + */ +int mirror_image_list(cls_method_context_t hctx, bufferlist *in, + bufferlist *out) { + std::string start_after; + uint64_t max_return; + try { + auto iter = in->cbegin(); + decode(start_after, iter); + decode(max_return, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + int max_read = RBD_MAX_KEYS_READ; + bool more = true; + std::map<std::string, std::string> mirror_images; + std::string last_read = mirror::image_key(start_after); + + while (more && mirror_images.size() < max_return) { + std::map<std::string, bufferlist> vals; + CLS_LOG(20, "last_read = '%s'", last_read.c_str()); + int r = cls_cxx_map_get_vals(hctx, last_read, mirror::IMAGE_KEY_PREFIX, + max_read, &vals, &more); + if (r < 0) { + if (r != -ENOENT) { + CLS_ERR("error reading mirror image directory by name: %s", + cpp_strerror(r).c_str()); + } + return r; + } + + for (auto it = vals.begin(); it != vals.end(); ++it) { + const std::string &image_id = + it->first.substr(mirror::IMAGE_KEY_PREFIX.size()); + cls::rbd::MirrorImage mirror_image; + auto iter = it->second.cbegin(); + try { + decode(mirror_image, iter); + } catch (const buffer::error &err) { + CLS_ERR("could not decode mirror image payload of image '%s'", + image_id.c_str()); + return -EIO; + } + + mirror_images[image_id] = mirror_image.global_image_id; + if (mirror_images.size() >= max_return) { + break; + } + } + if (!vals.empty()) { + last_read = mirror::image_key(mirror_images.rbegin()->first); + } + } + + encode(mirror_images, *out); + return 0; +} + +/** + * Input: + * @param global_id (std::string) + * + * Output: + * @param std::string - image id + * @returns 0 on success, negative error code on failure + */ +int mirror_image_get_image_id(cls_method_context_t hctx, bufferlist *in, + bufferlist *out) { + std::string global_id; + try { + auto it = in->cbegin(); + decode(global_id, it); + } catch (const buffer::error &err) { + return -EINVAL; + } + + std::string image_id; + int r = read_key(hctx, mirror::global_key(global_id), &image_id); + if (r < 0) { + if (r != -ENOENT) { + CLS_ERR("error retrieving image id for global id '%s': %s", + global_id.c_str(), cpp_strerror(r).c_str()); + } + return r; + } + + encode(image_id, *out); + return 0; +} + +/** + * Input: + * @param image_id (std::string) + * + * Output: + * @param cls::rbd::MirrorImage - metadata associated with the image_id + * @returns 0 on success, negative error code on failure + */ +int mirror_image_get(cls_method_context_t hctx, bufferlist *in, + bufferlist *out) { + string image_id; + try { + auto it = in->cbegin(); + decode(image_id, it); + } catch (const buffer::error &err) { + return -EINVAL; + } + + cls::rbd::MirrorImage mirror_image; + int r = mirror::image_get(hctx, image_id, &mirror_image); + if (r < 0) { + return r; + } + + encode(mirror_image, *out); + return 0; +} + +/** + * Input: + * @param image_id (std::string) + * @param mirror_image (cls::rbd::MirrorImage) + * + * Output: + * @returns 0 on success, negative error code on failure + * @returns -EEXIST if there's an existing image_id with a different global_image_id + */ +int mirror_image_set(cls_method_context_t hctx, bufferlist *in, + bufferlist *out) { + string image_id; + cls::rbd::MirrorImage mirror_image; + try { + auto it = in->cbegin(); + decode(image_id, it); + decode(mirror_image, it); + } catch (const buffer::error &err) { + return -EINVAL; + } + + int r = mirror::image_set(hctx, image_id, mirror_image); + if (r < 0) { + return r; + } + return 0; +} + +/** + * Input: + * @param image_id (std::string) + * + * Output: + * @returns 0 on success, negative error code on failure + */ +int mirror_image_remove(cls_method_context_t hctx, bufferlist *in, + bufferlist *out) { + string image_id; + try { + auto it = in->cbegin(); + decode(image_id, it); + } catch (const buffer::error &err) { + return -EINVAL; + } + + int r = mirror::image_remove(hctx, image_id); + if (r < 0) { + return r; + } + return 0; +} + +/** + * Input: + * @param global_image_id (std::string) + * @param status (cls::rbd::MirrorImageStatus) + * + * Output: + * @returns 0 on success, negative error code on failure + */ +int mirror_image_status_set(cls_method_context_t hctx, bufferlist *in, + bufferlist *out) { + string global_image_id; + cls::rbd::MirrorImageStatus status; + try { + auto it = in->cbegin(); + decode(global_image_id, it); + decode(status, it); + } catch (const buffer::error &err) { + return -EINVAL; + } + + int r = mirror::image_status_set(hctx, global_image_id, status); + if (r < 0) { + return r; + } + return 0; +} + +/** + * Input: + * @param global_image_id (std::string) + * + * Output: + * @returns 0 on success, negative error code on failure + */ +int mirror_image_status_remove(cls_method_context_t hctx, bufferlist *in, + bufferlist *out) { + string global_image_id; + try { + auto it = in->cbegin(); + decode(global_image_id, it); + } catch (const buffer::error &err) { + return -EINVAL; + } + + int r = mirror::image_status_remove(hctx, global_image_id); + if (r < 0) { + return r; + } + return 0; +} + +/** + * Input: + * @param global_image_id (std::string) + * + * Output: + * @param cls::rbd::MirrorImageStatus - metadata associated with the global_image_id + * @returns 0 on success, negative error code on failure + */ +int mirror_image_status_get(cls_method_context_t hctx, bufferlist *in, + bufferlist *out) { + string global_image_id; + try { + auto it = in->cbegin(); + decode(global_image_id, it); + } catch (const buffer::error &err) { + return -EINVAL; + } + + std::set<entity_inst_t> watchers; + int r = mirror::list_watchers(hctx, &watchers); + if (r < 0) { + return r; + } + + cls::rbd::MirrorImageStatus status; + r = mirror::image_status_get(hctx, global_image_id, watchers, &status); + if (r < 0) { + return r; + } + + encode(status, *out); + return 0; +} + +/** + * Input: + * @param start_after which name to begin listing after + * (use the empty string to start at the beginning) + * @param max_return the maximum number of names to list + * + * Output: + * @param std::map<std::string, cls::rbd::MirrorImage>: image id to image map + * @param std::map<std::string, cls::rbd::MirrorImageStatus>: image it to status map + * @returns 0 on success, negative error code on failure + */ +int mirror_image_status_list(cls_method_context_t hctx, bufferlist *in, + bufferlist *out) { + std::string start_after; + uint64_t max_return; + try { + auto iter = in->cbegin(); + decode(start_after, iter); + decode(max_return, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + map<std::string, cls::rbd::MirrorImage> images; + map<std::string, cls::rbd::MirrorImageStatus> statuses; + int r = mirror::image_status_list(hctx, start_after, max_return, &images, + &statuses); + if (r < 0) { + return r; + } + + encode(images, *out); + encode(statuses, *out); + return 0; +} + +/** + * Input: + * none + * + * Output: + * @param std::map<cls::rbd::MirrorImageStatusState, int>: states counts + * @returns 0 on success, negative error code on failure + */ +int mirror_image_status_get_summary(cls_method_context_t hctx, bufferlist *in, + bufferlist *out) { + std::map<cls::rbd::MirrorImageStatusState, int> states; + + int r = mirror::image_status_get_summary(hctx, &states); + if (r < 0) { + return r; + } + + encode(states, *out); + return 0; +} + +/** + * Input: + * none + * + * Output: + * @returns 0 on success, negative error code on failure + */ +int mirror_image_status_remove_down(cls_method_context_t hctx, bufferlist *in, + bufferlist *out) { + int r = mirror::image_status_remove_down(hctx); + if (r < 0) { + return r; + } + return 0; +} + +/** + * Input: + * @param global_image_id (std::string) + * + * Output: + * @param entity_inst_t - instance + * @returns 0 on success, negative error code on failure + */ +int mirror_image_instance_get(cls_method_context_t hctx, bufferlist *in, + bufferlist *out) { + string global_image_id; + try { + auto it = in->cbegin(); + decode(global_image_id, it); + } catch (const buffer::error &err) { + return -EINVAL; + } + + std::set<entity_inst_t> watchers; + int r = mirror::list_watchers(hctx, &watchers); + if (r < 0) { + return r; + } + + entity_inst_t instance; + r = mirror::image_instance_get(hctx, global_image_id, watchers, &instance); + if (r < 0) { + return r; + } + + encode(instance, *out, cls_get_features(hctx)); + return 0; +} + +/** + * Input: + * @param start_after which name to begin listing after + * (use the empty string to start at the beginning) + * @param max_return the maximum number of names to list + * + * Output: + * @param std::map<std::string, entity_inst_t>: image id to instance map + * @returns 0 on success, negative error code on failure + */ +int mirror_image_instance_list(cls_method_context_t hctx, bufferlist *in, + bufferlist *out) { + std::string start_after; + uint64_t max_return; + try { + auto iter = in->cbegin(); + decode(start_after, iter); + decode(max_return, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + map<std::string, entity_inst_t> instances; + int r = mirror::image_instance_list(hctx, start_after, max_return, + &instances); + if (r < 0) { + return r; + } + + encode(instances, *out, cls_get_features(hctx)); + return 0; +} + +/** + * Input: + * none + * + * Output: + * @param std::vector<std::string>: instance ids + * @returns 0 on success, negative error code on failure + */ +int mirror_instances_list(cls_method_context_t hctx, bufferlist *in, + bufferlist *out) { + std::vector<std::string> instance_ids; + + int r = mirror::instances_list(hctx, &instance_ids); + if (r < 0) { + return r; + } + + encode(instance_ids, *out); + return 0; +} + +/** + * Input: + * @param instance_id (std::string) + * + * Output: + * @returns 0 on success, negative error code on failure + */ +int mirror_instances_add(cls_method_context_t hctx, bufferlist *in, + bufferlist *out) { + std::string instance_id; + try { + auto iter = in->cbegin(); + decode(instance_id, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + int r = mirror::instances_add(hctx, instance_id); + if (r < 0) { + return r; + } + return 0; +} + +/** + * Input: + * @param instance_id (std::string) + * + * Output: + * @returns 0 on success, negative error code on failure + */ +int mirror_instances_remove(cls_method_context_t hctx, bufferlist *in, + bufferlist *out) { + std::string instance_id; + try { + auto iter = in->cbegin(); + decode(instance_id, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + int r = mirror::instances_remove(hctx, instance_id); + if (r < 0) { + return r; + } + return 0; +} + +/** + * Input: + * @param start_after: key to start after + * @param max_return: max return items + * + * Output: + * @param std::map<std::string, cls::rbd::MirrorImageMap>: image mapping + * @returns 0 on success, negative error code on failure + */ +int mirror_image_map_list(cls_method_context_t hctx, bufferlist *in, + bufferlist *out) { + std::string start_after; + uint64_t max_return; + try { + auto it = in->cbegin(); + decode(start_after, it); + decode(max_return, it); + } catch (const buffer::error &err) { + return -EINVAL; + } + + std::map<std::string, cls::rbd::MirrorImageMap> image_mapping; + int r = mirror::mirror_image_map_list(hctx, start_after, max_return, &image_mapping); + if (r < 0) { + return r; + } + + encode(image_mapping, *out); + return 0; +} + +/** + * Input: + * @param global_image_id: global image id + * @param image_map: image map + * + * Output: + * @returns 0 on success, negative error code on failure + */ +int mirror_image_map_update(cls_method_context_t hctx, bufferlist *in, + bufferlist *out) { + std::string global_image_id; + cls::rbd::MirrorImageMap image_map; + + try { + auto it = in->cbegin(); + decode(global_image_id, it); + decode(image_map, it); + } catch (const buffer::error &err) { + return -EINVAL; + } + + bufferlist bl; + encode(image_map, bl); + + const std::string key = mirror::mirror_image_map_key(global_image_id); + int r = cls_cxx_map_set_val(hctx, key, &bl); + if (r < 0) { + CLS_ERR("error updating image map %s: %s", key.c_str(), + cpp_strerror(r).c_str()); + return r; + } + + return 0; +} + +/** + * Input: + * @param global_image_id: global image id + * + * Output: + * @returns 0 on success, negative error code on failure + */ +int mirror_image_map_remove(cls_method_context_t hctx, bufferlist *in, + bufferlist *out) { + std::string global_image_id; + + try { + auto it = in->cbegin(); + decode(global_image_id, it); + } catch (const buffer::error &err) { + return -EINVAL; + } + + const std::string key = mirror::mirror_image_map_key(global_image_id); + int r = cls_cxx_map_remove_key(hctx, key); + if (r < 0 && r != -ENOENT) { + CLS_ERR("error removing image map %s: %s", key.c_str(), + cpp_strerror(r).c_str()); + return r; + } + + return 0; +} + +namespace group { + +/********************** methods for rbd_group_directory ***********************/ + +int dir_add(cls_method_context_t hctx, + const string &name, const string &id, + bool check_for_unique_id) +{ + if (!name.size() || !is_valid_id(id)) { + CLS_ERR("invalid group name '%s' or id '%s'", + name.c_str(), id.c_str()); + return -EINVAL; + } + + CLS_LOG(20, "dir_add name=%s id=%s", name.c_str(), id.c_str()); + + string name_key = dir_key_for_name(name); + string id_key = dir_key_for_id(id); + string tmp; + int r = read_key(hctx, name_key, &tmp); + if (r != -ENOENT) { + CLS_LOG(10, "name already exists"); + return -EEXIST; + } + r = read_key(hctx, id_key, &tmp); + if (r != -ENOENT && check_for_unique_id) { + CLS_LOG(10, "id already exists"); + return -EBADF; + } + bufferlist id_bl, name_bl; + encode(id, id_bl); + encode(name, name_bl); + map<string, bufferlist> omap_vals; + omap_vals[name_key] = id_bl; + omap_vals[id_key] = name_bl; + return cls_cxx_map_set_vals(hctx, &omap_vals); +} + +int dir_remove(cls_method_context_t hctx, + const string &name, const string &id) +{ + CLS_LOG(20, "dir_remove name=%s id=%s", name.c_str(), id.c_str()); + + string name_key = dir_key_for_name(name); + string id_key = dir_key_for_id(id); + string stored_name, stored_id; + + int r = read_key(hctx, name_key, &stored_id); + if (r < 0) { + if (r != -ENOENT) + CLS_ERR("error reading name to id mapping: %s", cpp_strerror(r).c_str()); + return r; + } + r = read_key(hctx, id_key, &stored_name); + if (r < 0) { + if (r != -ENOENT) + CLS_ERR("error reading id to name mapping: %s", cpp_strerror(r).c_str()); + return r; + } + + // check if this op raced with a rename + if (stored_name != name || stored_id != id) { + CLS_ERR("stored name '%s' and id '%s' do not match args '%s' and '%s'", + stored_name.c_str(), stored_id.c_str(), name.c_str(), id.c_str()); + return -ESTALE; + } + + r = cls_cxx_map_remove_key(hctx, name_key); + if (r < 0) { + CLS_ERR("error removing name: %s", cpp_strerror(r).c_str()); + return r; + } + + r = cls_cxx_map_remove_key(hctx, id_key); + if (r < 0) { + CLS_ERR("error removing id: %s", cpp_strerror(r).c_str()); + return r; + } + + return 0; +} + +static const string RBD_GROUP_SNAP_KEY_PREFIX = "snapshot_"; + +std::string snap_key(const std::string &snap_id) { + ostringstream oss; + oss << RBD_GROUP_SNAP_KEY_PREFIX << snap_id; + return oss.str(); +} + +int snap_list(cls_method_context_t hctx, cls::rbd::GroupSnapshot start_after, + uint64_t max_return, + std::vector<cls::rbd::GroupSnapshot> *group_snaps) +{ + int max_read = RBD_MAX_KEYS_READ; + std::map<string, bufferlist> vals; + string last_read = snap_key(start_after.id); + + group_snaps->clear(); + + bool more; + do { + int r = cls_cxx_map_get_vals(hctx, last_read, + RBD_GROUP_SNAP_KEY_PREFIX, + max_read, &vals, &more); + if (r < 0) + return r; + + for (map<string, bufferlist>::iterator it = vals.begin(); + it != vals.end() && group_snaps->size() < max_return; ++it) { + + auto iter = it->second.cbegin(); + cls::rbd::GroupSnapshot snap; + try { + decode(snap, iter); + } catch (const buffer::error &err) { + CLS_ERR("error decoding snapshot: %s", it->first.c_str()); + return -EIO; + } + CLS_LOG(20, "Discovered snapshot %s %s", + snap.name.c_str(), + snap.id.c_str()); + group_snaps->push_back(snap); + } + + } while (more && (group_snaps->size() < max_return)); + + return 0; +} + +static int check_duplicate_snap_name(cls_method_context_t hctx, + const std::string &snap_name, + const std::string &snap_id) +{ + const int max_read = 1024; + cls::rbd::GroupSnapshot snap_last; + std::vector<cls::rbd::GroupSnapshot> page; + + for (;;) { + int r = snap_list(hctx, snap_last, max_read, &page); + if (r < 0) { + return r; + } + for (auto& snap: page) { + if (snap.name == snap_name && snap.id != snap_id) { + return -EEXIST; + } + } + + if (page.size() < max_read) { + break; + } + + snap_last = *page.rbegin(); + } + + return 0; +} + +} // namespace group + +/** + * List groups from the directory. + * + * Input: + * @param start_after (std::string) + * @param max_return (int64_t) + * + * Output: + * @param map of groups (name, id) + * @return 0 on success, negative error code on failure + */ +int group_dir_list(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + string start_after; + uint64_t max_return; + + try { + auto iter = in->cbegin(); + decode(start_after, iter); + decode(max_return, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + int max_read = RBD_MAX_KEYS_READ; + bool more = true; + map<string, string> groups; + string last_read = dir_key_for_name(start_after); + + while (more && groups.size() < max_return) { + map<string, bufferlist> vals; + CLS_LOG(20, "last_read = '%s'", last_read.c_str()); + int r = cls_cxx_map_get_vals(hctx, last_read, RBD_DIR_NAME_KEY_PREFIX, + max_read, &vals, &more); + if (r < 0) { + if (r != -ENOENT) { + CLS_ERR("error reading directory by name: %s", cpp_strerror(r).c_str()); + } + return r; + } + + for (pair<string, bufferlist> val: vals) { + string id; + auto iter = val.second.cbegin(); + try { + decode(id, iter); + } catch (const buffer::error &err) { + CLS_ERR("could not decode id of group '%s'", val.first.c_str()); + return -EIO; + } + CLS_LOG(20, "adding '%s' -> '%s'", dir_name_from_key(val.first).c_str(), id.c_str()); + groups[dir_name_from_key(val.first)] = id; + if (groups.size() >= max_return) + break; + } + if (!vals.empty()) { + last_read = dir_key_for_name(groups.rbegin()->first); + } + } + + encode(groups, *out); + + return 0; +} + +/** + * Add a group to the directory. + * + * Input: + * @param name (std::string) + * @param id (std::string) + * + * Output: + * @return 0 on success, negative error code on failure + */ +int group_dir_add(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + int r = cls_cxx_create(hctx, false); + + if (r < 0) { + CLS_ERR("could not create group directory: %s", + cpp_strerror(r).c_str()); + return r; + } + + string name, id; + try { + auto iter = in->cbegin(); + decode(name, iter); + decode(id, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + return group::dir_add(hctx, name, id, true); +} + +/** + * Rename a group to the directory. + * + * Input: + * @param src original name of the group (std::string) + * @param dest new name of the group (std::string) + * @param id the id of the group (std::string) + * + * Output: + * @return 0 on success, negative error code on failure + */ +int group_dir_rename(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + string src, dest, id; + try { + auto iter = in->cbegin(); + decode(src, iter); + decode(dest, iter); + decode(id, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + int r = group::dir_remove(hctx, src, id); + if (r < 0) + return r; + + return group::dir_add(hctx, dest, id, false); +} + +/** + * Remove a group from the directory. + * + * Input: + * @param name (std::string) + * @param id (std::string) + * + * Output: + * @return 0 on success, negative error code on failure + */ +int group_dir_remove(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + string name, id; + try { + auto iter = in->cbegin(); + decode(name, iter); + decode(id, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + return group::dir_remove(hctx, name, id); +} + +/** + * Set state of an image in the group. + * + * Input: + * @param image_status (cls::rbd::GroupImageStatus) + * + * Output: + * @return 0 on success, negative error code on failure + */ +int group_image_set(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + CLS_LOG(20, "group_image_set"); + + cls::rbd::GroupImageStatus st; + try { + auto iter = in->cbegin(); + decode(st, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + string image_key = st.spec.image_key(); + + bufferlist image_val_bl; + encode(st.state, image_val_bl); + int r = cls_cxx_map_set_val(hctx, image_key, &image_val_bl); + if (r < 0) { + return r; + } + + return 0; +} + +/** + * Remove reference to an image from the group. + * + * Input: + * @param spec (cls::rbd::GroupImageSpec) + * + * Output: + * @return 0 on success, negative error code on failure + */ +int group_image_remove(cls_method_context_t hctx, + bufferlist *in, bufferlist *out) +{ + CLS_LOG(20, "group_image_remove"); + cls::rbd::GroupImageSpec spec; + try { + auto iter = in->cbegin(); + decode(spec, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + string image_key = spec.image_key(); + + int r = cls_cxx_map_remove_key(hctx, image_key); + if (r < 0) { + CLS_ERR("error removing image from group: %s", cpp_strerror(r).c_str()); + return r; + } + + return 0; +} + +/* + * List images in the group. + * + * Input: + * @param start_after which name to begin listing after + * (use the empty string to start at the beginning) + * @param max_return the maximum number of names to list + * + * Output: + * @param tuples of descriptions of the images: image_id, pool_id, image reference state. + * @return 0 on success, negative error code on failure + */ +int group_image_list(cls_method_context_t hctx, + bufferlist *in, bufferlist *out) +{ + CLS_LOG(20, "group_image_list"); + cls::rbd::GroupImageSpec start_after; + uint64_t max_return; + try { + auto iter = in->cbegin(); + decode(start_after, iter); + decode(max_return, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + int max_read = RBD_MAX_KEYS_READ; + std::map<string, bufferlist> vals; + string last_read = start_after.image_key(); + std::vector<cls::rbd::GroupImageStatus> res; + bool more; + do { + int r = cls_cxx_map_get_vals(hctx, last_read, + cls::rbd::RBD_GROUP_IMAGE_KEY_PREFIX, + max_read, &vals, &more); + if (r < 0) + return r; + + for (map<string, bufferlist>::iterator it = vals.begin(); + it != vals.end() && res.size() < max_return; ++it) { + + auto iter = it->second.cbegin(); + cls::rbd::GroupImageLinkState state; + try { + decode(state, iter); + } catch (const buffer::error &err) { + CLS_ERR("error decoding state for image: %s", it->first.c_str()); + return -EIO; + } + cls::rbd::GroupImageSpec spec; + int r = cls::rbd::GroupImageSpec::from_key(it->first, &spec); + if (r < 0) + return r; + + CLS_LOG(20, "Discovered image %s %" PRId64 " %d", spec.image_id.c_str(), + spec.pool_id, + (int)state); + res.push_back(cls::rbd::GroupImageStatus(spec, state)); + } + if (res.size() > 0) { + last_read = res.rbegin()->spec.image_key(); + } + + } while (more && (res.size() < max_return)); + encode(res, *out); + + return 0; +} + +/** + * Reference the group this image belongs to. + * + * Input: + * @param group_id (std::string) + * @param pool_id (int64_t) + * + * Output: + * @return 0 on success, negative error code on failure + */ +int image_group_add(cls_method_context_t hctx, + bufferlist *in, bufferlist *out) +{ + CLS_LOG(20, "image_group_add"); + cls::rbd::GroupSpec new_group; + try { + auto iter = in->cbegin(); + decode(new_group, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + bufferlist existing_refbl; + + int r = cls_cxx_map_get_val(hctx, RBD_GROUP_REF, &existing_refbl); + if (r == 0) { + // If we are trying to link this image to the same group then return + // success. If this image already belongs to another group then abort. + cls::rbd::GroupSpec old_group; + try { + auto iter = existing_refbl.cbegin(); + decode(old_group, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + if ((old_group.group_id != new_group.group_id) || + (old_group.pool_id != new_group.pool_id)) { + return -EEXIST; + } else { + return 0; // In this case the values are already correct + } + } else if (r < 0 && r != -ENOENT) { + // No entry means this image is not a member of any group. + return r; + } + + r = image::set_op_features(hctx, RBD_OPERATION_FEATURE_GROUP, + RBD_OPERATION_FEATURE_GROUP); + if (r < 0) { + return r; + } + + bufferlist refbl; + encode(new_group, refbl); + r = cls_cxx_map_set_val(hctx, RBD_GROUP_REF, &refbl); + if (r < 0) { + return r; + } + + return 0; +} + +/** + * Remove image's pointer to the group. + * + * Input: + * @param cg_id (std::string) + * @param pool_id (int64_t) + * + * Output: + * @return 0 on success, negative error code on failure + */ +int image_group_remove(cls_method_context_t hctx, + bufferlist *in, + bufferlist *out) +{ + CLS_LOG(20, "image_group_remove"); + cls::rbd::GroupSpec spec; + try { + auto iter = in->cbegin(); + decode(spec, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + bufferlist refbl; + int r = cls_cxx_map_get_val(hctx, RBD_GROUP_REF, &refbl); + if (r < 0) { + return r; + } + + cls::rbd::GroupSpec ref_spec; + auto iter = refbl.cbegin(); + try { + decode(ref_spec, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + if (ref_spec.pool_id != spec.pool_id || ref_spec.group_id != spec.group_id) { + return -EBADF; + } + + r = cls_cxx_map_remove_key(hctx, RBD_GROUP_REF); + if (r < 0) { + return r; + } + + r = image::set_op_features(hctx, 0, RBD_OPERATION_FEATURE_GROUP); + if (r < 0) { + return r; + } + + return 0; +} + +/** + * Retrieve the id and pool of the group this image belongs to. + * + * Input: + * none + * + * Output: + * @param GroupSpec + * @return 0 on success, negative error code on failure + */ +int image_group_get(cls_method_context_t hctx, + bufferlist *in, bufferlist *out) +{ + CLS_LOG(20, "image_group_get"); + bufferlist refbl; + int r = cls_cxx_map_get_val(hctx, RBD_GROUP_REF, &refbl); + if (r < 0 && r != -ENOENT) { + return r; + } + + cls::rbd::GroupSpec spec; + + if (r != -ENOENT) { + auto iter = refbl.cbegin(); + try { + decode(spec, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + } + + encode(spec, *out); + return 0; +} + +/** + * Save initial snapshot record. + * + * Input: + * @param GroupSnapshot + * + * Output: + * @return 0 on success, negative error code on failure + */ +int group_snap_set(cls_method_context_t hctx, + bufferlist *in, bufferlist *out) +{ + CLS_LOG(20, "group_snap_set"); + cls::rbd::GroupSnapshot group_snap; + try { + auto iter = in->cbegin(); + decode(group_snap, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + if (group_snap.name.empty()) { + CLS_ERR("group snapshot name is empty"); + return -EINVAL; + } + if (group_snap.id.empty()) { + CLS_ERR("group snapshot id is empty"); + return -EINVAL; + } + + int r = group::check_duplicate_snap_name(hctx, group_snap.name, + group_snap.id); + if (r < 0) { + return r; + } + + std::string key = group::snap_key(group_snap.id); + if (group_snap.state == cls::rbd::GROUP_SNAPSHOT_STATE_INCOMPLETE) { + bufferlist snap_bl; + r = cls_cxx_map_get_val(hctx, key, &snap_bl); + if (r < 0 && r != -ENOENT) { + return r; + } else if (r >= 0) { + return -EEXIST; + } + } + + bufferlist obl; + encode(group_snap, obl); + r = cls_cxx_map_set_val(hctx, key, &obl); + return r; +} + +/** + * Remove snapshot record. + * + * Input: + * @param id Snapshot id + * + * Output: + * @return 0 on success, negative error code on failure + */ +int group_snap_remove(cls_method_context_t hctx, + bufferlist *in, bufferlist *out) +{ + CLS_LOG(20, "group_snap_remove"); + std::string snap_id; + try { + auto iter = in->cbegin(); + decode(snap_id, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + std::string snap_key = group::snap_key(snap_id); + + CLS_LOG(20, "removing snapshot with key %s", snap_key.c_str()); + int r = cls_cxx_map_remove_key(hctx, snap_key); + return r; +} + +/** + * Get group's snapshot by id. + * + * Input: + * @param snapshot_id the id of the snapshot to look for. + * + * Output: + * @param GroupSnapshot the requested snapshot + * @return 0 on success, negative error code on failure + */ +int group_snap_get_by_id(cls_method_context_t hctx, + bufferlist *in, bufferlist *out) +{ + CLS_LOG(20, "group_snap_get_by_id"); + + std::string snap_id; + try { + auto iter = in->cbegin(); + decode(snap_id, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + bufferlist snapbl; + + int r = cls_cxx_map_get_val(hctx, group::snap_key(snap_id), &snapbl); + if (r < 0) { + return r; + } + + cls::rbd::GroupSnapshot group_snap; + auto iter = snapbl.cbegin(); + try { + decode(group_snap, iter); + } catch (const buffer::error &err) { + CLS_ERR("error decoding snapshot: %s", snap_id.c_str()); + return -EIO; + } + + encode(group_snap, *out); + + return 0; +} + +/** + * List group's snapshots. + * + * Input: + * @param start_after which name to begin listing after + * (use the empty string to start at the beginning) + * @param max_return the maximum number of snapshots to list + * + * Output: + * @param list of snapshots + * @return 0 on success, negative error code on failure + */ +int group_snap_list(cls_method_context_t hctx, + bufferlist *in, bufferlist *out) +{ + CLS_LOG(20, "group_snap_list"); + + cls::rbd::GroupSnapshot start_after; + uint64_t max_return; + try { + auto iter = in->cbegin(); + decode(start_after, iter); + decode(max_return, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + std::vector<cls::rbd::GroupSnapshot> group_snaps; + group::snap_list(hctx, start_after, max_return, &group_snaps); + + encode(group_snaps, *out); + + return 0; +} + +namespace trash { + +static const std::string IMAGE_KEY_PREFIX("id_"); + +std::string image_key(const std::string &image_id) { + return IMAGE_KEY_PREFIX + image_id; +} + +std::string image_id_from_key(const std::string &key) { + return key.substr(IMAGE_KEY_PREFIX.size()); +} + +} // namespace trash + +/** + * Add an image entry to the rbd trash. Creates the trash object if + * needed, and stores the trash spec information of the deleted image. + * + * Input: + * @param id the id of the image + * @param trash_spec the spec info of the deleted image + * + * Output: + * @returns -EEXIST if the image id is already in the trash + * @returns 0 on success, negative error code on failure + */ +int trash_add(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + int r = cls_cxx_create(hctx, false); + if (r < 0) { + CLS_ERR("could not create trash: %s", cpp_strerror(r).c_str()); + return r; + } + + string id; + cls::rbd::TrashImageSpec trash_spec; + try { + auto iter = in->cbegin(); + decode(id, iter); + decode(trash_spec, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + if (!is_valid_id(id)) { + CLS_ERR("trash_add: invalid id '%s'", id.c_str()); + return -EINVAL; + } + + CLS_LOG(20, "trash_add id=%s", id.c_str()); + + string key = trash::image_key(id); + cls::rbd::TrashImageSpec tmp; + r = read_key(hctx, key, &tmp); + if (r < 0 && r != -ENOENT) { + CLS_ERR("could not read key %s entry from trash: %s", key.c_str(), + cpp_strerror(r).c_str()); + return r; + } else if (r == 0) { + CLS_LOG(10, "id already exists"); + return -EEXIST; + } + + map<string, bufferlist> omap_vals; + encode(trash_spec, omap_vals[key]); + return cls_cxx_map_set_vals(hctx, &omap_vals); +} + +/** + * Removes an image entry from the rbd trash object. + * image. + * + * Input: + * @param id the id of the image + * + * Output: + * @returns -ENOENT if the image id does not exist in the trash + * @returns 0 on success, negative error code on failure + */ +int trash_remove(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + string id; + try { + auto iter = in->cbegin(); + decode(id, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + CLS_LOG(20, "trash_remove id=%s", id.c_str()); + + string key = trash::image_key(id); + bufferlist tmp; + int r = cls_cxx_map_get_val(hctx, key, &tmp); + if (r < 0) { + if (r != -ENOENT) { + CLS_ERR("error reading entry key %s: %s", key.c_str(), cpp_strerror(r).c_str()); + } + return r; + } + + r = cls_cxx_map_remove_key(hctx, key); + if (r < 0) { + CLS_ERR("error removing entry: %s", cpp_strerror(r).c_str()); + return r; + } + + return 0; +} + +/** + * Returns the list of trash spec entries registered in the rbd_trash + * object. + * + * Input: + * @param start_after which name to begin listing after + * (use the empty string to start at the beginning) + * @param max_return the maximum number of names to list + * + * Output: + * @param data the map between image id and trash spec info + * + * @returns 0 on success, negative error code on failure + */ +int trash_list(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + string start_after; + uint64_t max_return; + + try { + auto iter = in->cbegin(); + decode(start_after, iter); + decode(max_return, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + map<string, cls::rbd::TrashImageSpec> data; + string last_read = trash::image_key(start_after); + bool more = true; + + CLS_LOG(20, "trash_get_images"); + while (data.size() < max_return) { + map<string, bufferlist> raw_data; + int max_read = std::min<int32_t>(RBD_MAX_KEYS_READ, + max_return - data.size()); + int r = cls_cxx_map_get_vals(hctx, last_read, trash::IMAGE_KEY_PREFIX, + max_read, &raw_data, &more); + if (r < 0) { + if (r != -ENOENT) { + CLS_ERR("failed to read the vals off of disk: %s", + cpp_strerror(r).c_str()); + } + return r; + } + if (raw_data.empty()) { + break; + } + + map<string, bufferlist>::iterator it = raw_data.begin(); + for (; it != raw_data.end(); ++it) { + decode(data[trash::image_id_from_key(it->first)], it->second); + } + + if (!more) { + break; + } + + last_read = raw_data.rbegin()->first; + } + + encode(data, *out); + return 0; +} + +/** + * Returns the trash spec entry of an image registered in the rbd_trash + * object. + * + * Input: + * @param id the id of the image + * + * Output: + * @param out the trash spec entry + * + * @returns 0 on success, negative error code on failure + */ +int trash_get(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + string id; + try { + auto iter = in->cbegin(); + decode(id, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + CLS_LOG(20, "trash_get_image id=%s", id.c_str()); + + + string key = trash::image_key(id); + bufferlist bl; + int r = cls_cxx_map_get_val(hctx, key, out); + if (r < 0 && r != -ENOENT) { + CLS_ERR("error reading image from trash '%s': '%s'", id.c_str(), + cpp_strerror(r).c_str()); + } + return r; +} + +/** + * Set state of an image in the rbd_trash object. + * + * Input: + * @param id the id of the image + * @param trash_state the state of the image to be set + * @param expect_state the expected state of the image + * + * Output: + * @returns 0 on success, negative error code on failure + */ +int trash_state_set(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + string id; + cls::rbd::TrashImageState trash_state; + cls::rbd::TrashImageState expect_state; + try { + bufferlist::const_iterator iter = in->begin(); + decode(id, iter); + decode(trash_state, iter); + decode(expect_state, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + CLS_LOG(20, "trash_state_set id=%s", id.c_str()); + + string key = trash::image_key(id); + cls::rbd::TrashImageSpec trash_spec; + int r = read_key(hctx, key, &trash_spec); + if (r < 0) { + if (r != -ENOENT) { + CLS_ERR("Could not read trash image spec off disk: %s", + cpp_strerror(r).c_str()); + } + return r; + } + + if (trash_spec.state == expect_state) { + trash_spec.state = trash_state; + r = write_key(hctx, key, trash_spec); + if (r < 0) { + CLS_ERR("error setting trash image state: %s", cpp_strerror(r).c_str()); + return r; + } + + return 0; + } else if (trash_spec.state == trash_state) { + return 0; + } else { + CLS_ERR("Current trash state: %d do not match expected: %d or set: %d", + trash_spec.state, expect_state, trash_state); + return -ESTALE; + } +} + +namespace nspace { + +const std::string NAME_KEY_PREFIX("name_"); + +std::string key_for_name(const std::string& name) { + return NAME_KEY_PREFIX + name; +} + +std::string name_from_key(const std::string &key) { + return key.substr(NAME_KEY_PREFIX.size()); +} + +} // namespace nspace + +/** + * Add a namespace to the namespace directory. + * + * Input: + * @param name the name of the namespace + * + * Output: + * @returns -EEXIST if the namespace is already exists + * @returns 0 on success, negative error code on failure + */ +int namespace_add(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + std::string name; + try { + auto iter = in->cbegin(); + decode(name, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + std::string key(nspace::key_for_name(name)); + bufferlist value; + int r = cls_cxx_map_get_val(hctx, key, &value); + if (r < 0 && r != -ENOENT) { + return r; + } else if (r == 0) { + return -EEXIST; + } + + r = cls_cxx_map_set_val(hctx, key, &value); + if (r < 0) { + CLS_ERR("failed to set omap key: %s", key.c_str()); + return r; + } + + return 0; +} + +/** + * Remove a namespace from the namespace directory. + * + * Input: + * @param name the name of the namespace + * + * Output: + * @returns 0 on success, negative error code on failure + */ +int namespace_remove(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + std::string name; + try { + auto iter = in->cbegin(); + decode(name, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + std::string key(nspace::key_for_name(name)); + bufferlist bl; + int r = cls_cxx_map_get_val(hctx, key, &bl); + if (r < 0) { + return r; + } + + r = cls_cxx_map_remove_key(hctx, key); + if (r < 0) { + return r; + } + + return 0; +} + +/** + * Returns the list of namespaces in the rbd_namespace object + * + * Input: + * @param start_after which name to begin listing after + * (use the empty string to start at the beginning) + * @param max_return the maximum number of names to list + * + * Output: + * @param data list of namespace names + * @returns 0 on success, negative error code on failure + */ +int namespace_list(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + string start_after; + uint64_t max_return; + try { + auto iter = in->cbegin(); + decode(start_after, iter); + decode(max_return, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + std::list<std::string> data; + std::string last_read = nspace::key_for_name(start_after); + bool more = true; + + CLS_LOG(20, "namespace_list"); + while (data.size() < max_return) { + std::map<std::string, bufferlist> raw_data; + int max_read = std::min<int32_t>(RBD_MAX_KEYS_READ, + max_return - data.size()); + int r = cls_cxx_map_get_vals(hctx, last_read, nspace::NAME_KEY_PREFIX, + max_read, &raw_data, &more); + if (r < 0) { + if (r != -ENOENT) { + CLS_ERR("failed to read the vals off of disk: %s", + cpp_strerror(r).c_str()); + } + return r; + } + + for (auto& it : raw_data) { + data.push_back(nspace::name_from_key(it.first)); + } + + if (raw_data.empty() || !more) { + break; + } + + last_read = raw_data.rbegin()->first; + } + + encode(data, *out); + return 0; +} + +/** + * Reclaim space for zeroed extents + * + * Input: + * @param sparse_size minimal zeroed block to sparse + * @param remove_empty boolean, true if the object should be removed if empty + * + * Output: + * @returns -ENOENT if the object does not exist or has been removed + * @returns 0 on success, negative error code on failure + */ +int sparsify(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + size_t sparse_size; + bool remove_empty; + try { + auto iter = in->cbegin(); + decode(sparse_size, iter); + decode(remove_empty, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + int r = check_exists(hctx); + if (r < 0) { + return r; + } + + bufferlist bl; + r = cls_cxx_read(hctx, 0, 0, &bl); + if (r < 0) { + CLS_ERR("failed to read data off of disk: %s", cpp_strerror(r).c_str()); + return r; + } + + if (bl.is_zero()) { + if (remove_empty) { + CLS_LOG(20, "remove"); + r = cls_cxx_remove(hctx); + if (r < 0) { + CLS_ERR("remove failed: %s", cpp_strerror(r).c_str()); + return r; + } + } else if (bl.length() > 0) { + CLS_LOG(20, "truncate"); + bufferlist write_bl; + r = cls_cxx_replace(hctx, 0, 0, &write_bl); + if (r < 0) { + CLS_ERR("truncate failed: %s", cpp_strerror(r).c_str()); + return r; + } + } else { + CLS_LOG(20, "skip empty"); + } + return 0; + } + + bl.rebuild(buffer::ptr_node::create(bl.length())); + size_t write_offset = 0; + size_t write_length = 0; + size_t offset = 0; + size_t length = bl.length(); + const auto& ptr = bl.front(); + bool replace = true; + while (offset < length) { + if (calc_sparse_extent(ptr, sparse_size, length, &write_offset, + &write_length, &offset)) { + if (write_offset == 0 && write_length == length) { + CLS_LOG(20, "nothing to do"); + return 0; + } + CLS_LOG(20, "write%s %" PRIu64 "~%" PRIu64, (replace ? "(replace)" : ""), + write_offset, write_length); + bufferlist write_bl; + write_bl.push_back(buffer::ptr_node::create(ptr, write_offset, + write_length)); + if (replace) { + r = cls_cxx_replace(hctx, write_offset, write_length, &write_bl); + replace = false; + } else { + r = cls_cxx_write(hctx, write_offset, write_length, &write_bl); + } + if (r < 0) { + CLS_ERR("write failed: %s", cpp_strerror(r).c_str()); + return r; + } + write_offset = offset; + write_length = 0; + } + } + + return 0; +} + +CLS_INIT(rbd) +{ + CLS_LOG(20, "Loaded rbd class!"); + + cls_handle_t h_class; + cls_method_handle_t h_create; + cls_method_handle_t h_get_features; + cls_method_handle_t h_set_features; + cls_method_handle_t h_get_size; + cls_method_handle_t h_set_size; + cls_method_handle_t h_get_parent; + cls_method_handle_t h_set_parent; + cls_method_handle_t h_remove_parent; + cls_method_handle_t h_parent_get; + cls_method_handle_t h_parent_overlap_get; + cls_method_handle_t h_parent_attach; + cls_method_handle_t h_parent_detach; + cls_method_handle_t h_get_protection_status; + cls_method_handle_t h_set_protection_status; + cls_method_handle_t h_get_stripe_unit_count; + cls_method_handle_t h_set_stripe_unit_count; + cls_method_handle_t h_get_create_timestamp; + cls_method_handle_t h_get_access_timestamp; + cls_method_handle_t h_get_modify_timestamp; + cls_method_handle_t h_get_flags; + cls_method_handle_t h_set_flags; + cls_method_handle_t h_op_features_get; + cls_method_handle_t h_op_features_set; + cls_method_handle_t h_add_child; + cls_method_handle_t h_remove_child; + cls_method_handle_t h_get_children; + cls_method_handle_t h_get_snapcontext; + cls_method_handle_t h_get_object_prefix; + cls_method_handle_t h_get_data_pool; + cls_method_handle_t h_get_snapshot_name; + cls_method_handle_t h_get_snapshot_timestamp; + cls_method_handle_t h_snapshot_get; + cls_method_handle_t h_snapshot_add; + cls_method_handle_t h_snapshot_remove; + cls_method_handle_t h_snapshot_rename; + cls_method_handle_t h_snapshot_trash_add; + cls_method_handle_t h_get_all_features; + cls_method_handle_t h_get_id; + cls_method_handle_t h_set_id; + cls_method_handle_t h_set_modify_timestamp; + cls_method_handle_t h_set_access_timestamp; + cls_method_handle_t h_dir_get_id; + cls_method_handle_t h_dir_get_name; + cls_method_handle_t h_dir_list; + cls_method_handle_t h_dir_add_image; + cls_method_handle_t h_dir_remove_image; + cls_method_handle_t h_dir_rename_image; + cls_method_handle_t h_dir_state_assert; + cls_method_handle_t h_dir_state_set; + cls_method_handle_t h_object_map_load; + cls_method_handle_t h_object_map_save; + cls_method_handle_t h_object_map_resize; + cls_method_handle_t h_object_map_update; + cls_method_handle_t h_object_map_snap_add; + cls_method_handle_t h_object_map_snap_remove; + cls_method_handle_t h_metadata_set; + cls_method_handle_t h_metadata_remove; + cls_method_handle_t h_metadata_list; + cls_method_handle_t h_metadata_get; + cls_method_handle_t h_snapshot_get_limit; + cls_method_handle_t h_snapshot_set_limit; + cls_method_handle_t h_child_attach; + cls_method_handle_t h_child_detach; + cls_method_handle_t h_children_list; + cls_method_handle_t h_migration_set; + cls_method_handle_t h_migration_set_state; + cls_method_handle_t h_migration_get; + cls_method_handle_t h_migration_remove; + cls_method_handle_t h_old_snapshots_list; + cls_method_handle_t h_old_snapshot_add; + cls_method_handle_t h_old_snapshot_remove; + cls_method_handle_t h_old_snapshot_rename; + cls_method_handle_t h_mirror_uuid_get; + cls_method_handle_t h_mirror_uuid_set; + cls_method_handle_t h_mirror_mode_get; + cls_method_handle_t h_mirror_mode_set; + cls_method_handle_t h_mirror_peer_list; + cls_method_handle_t h_mirror_peer_add; + cls_method_handle_t h_mirror_peer_remove; + cls_method_handle_t h_mirror_peer_set_client; + cls_method_handle_t h_mirror_peer_set_cluster; + cls_method_handle_t h_mirror_image_list; + cls_method_handle_t h_mirror_image_get_image_id; + cls_method_handle_t h_mirror_image_get; + cls_method_handle_t h_mirror_image_set; + cls_method_handle_t h_mirror_image_remove; + cls_method_handle_t h_mirror_image_status_set; + cls_method_handle_t h_mirror_image_status_remove; + cls_method_handle_t h_mirror_image_status_get; + cls_method_handle_t h_mirror_image_status_list; + cls_method_handle_t h_mirror_image_status_get_summary; + cls_method_handle_t h_mirror_image_status_remove_down; + cls_method_handle_t h_mirror_image_instance_get; + cls_method_handle_t h_mirror_image_instance_list; + cls_method_handle_t h_mirror_instances_list; + cls_method_handle_t h_mirror_instances_add; + cls_method_handle_t h_mirror_instances_remove; + cls_method_handle_t h_mirror_image_map_list; + cls_method_handle_t h_mirror_image_map_update; + cls_method_handle_t h_mirror_image_map_remove; + cls_method_handle_t h_group_dir_list; + cls_method_handle_t h_group_dir_add; + cls_method_handle_t h_group_dir_remove; + cls_method_handle_t h_group_dir_rename; + cls_method_handle_t h_group_image_remove; + cls_method_handle_t h_group_image_list; + cls_method_handle_t h_group_image_set; + cls_method_handle_t h_image_group_add; + cls_method_handle_t h_image_group_remove; + cls_method_handle_t h_image_group_get; + cls_method_handle_t h_group_snap_set; + cls_method_handle_t h_group_snap_remove; + cls_method_handle_t h_group_snap_get_by_id; + cls_method_handle_t h_group_snap_list; + cls_method_handle_t h_trash_add; + cls_method_handle_t h_trash_remove; + cls_method_handle_t h_trash_list; + cls_method_handle_t h_trash_get; + cls_method_handle_t h_trash_state_set; + cls_method_handle_t h_namespace_add; + cls_method_handle_t h_namespace_remove; + cls_method_handle_t h_namespace_list; + cls_method_handle_t h_copyup; + cls_method_handle_t h_assert_snapc_seq; + cls_method_handle_t h_sparsify; + + cls_register("rbd", &h_class); + cls_register_cxx_method(h_class, "create", + CLS_METHOD_RD | CLS_METHOD_WR, + create, &h_create); + cls_register_cxx_method(h_class, "get_features", + CLS_METHOD_RD, + get_features, &h_get_features); + cls_register_cxx_method(h_class, "set_features", + CLS_METHOD_RD | CLS_METHOD_WR, + set_features, &h_set_features); + cls_register_cxx_method(h_class, "get_size", + CLS_METHOD_RD, + get_size, &h_get_size); + cls_register_cxx_method(h_class, "set_size", + CLS_METHOD_RD | CLS_METHOD_WR, + set_size, &h_set_size); + cls_register_cxx_method(h_class, "get_snapcontext", + CLS_METHOD_RD, + get_snapcontext, &h_get_snapcontext); + cls_register_cxx_method(h_class, "get_object_prefix", + CLS_METHOD_RD, + get_object_prefix, &h_get_object_prefix); + cls_register_cxx_method(h_class, "get_data_pool", CLS_METHOD_RD, + get_data_pool, &h_get_data_pool); + cls_register_cxx_method(h_class, "get_snapshot_name", + CLS_METHOD_RD, + get_snapshot_name, &h_get_snapshot_name); + cls_register_cxx_method(h_class, "get_snapshot_timestamp", + CLS_METHOD_RD, + get_snapshot_timestamp, &h_get_snapshot_timestamp); + cls_register_cxx_method(h_class, "snapshot_get", + CLS_METHOD_RD, + snapshot_get, &h_snapshot_get); + cls_register_cxx_method(h_class, "snapshot_add", + CLS_METHOD_RD | CLS_METHOD_WR, + snapshot_add, &h_snapshot_add); + cls_register_cxx_method(h_class, "snapshot_remove", + CLS_METHOD_RD | CLS_METHOD_WR, + snapshot_remove, &h_snapshot_remove); + cls_register_cxx_method(h_class, "snapshot_rename", + CLS_METHOD_RD | CLS_METHOD_WR, + snapshot_rename, &h_snapshot_rename); + cls_register_cxx_method(h_class, "snapshot_trash_add", + CLS_METHOD_RD | CLS_METHOD_WR, + snapshot_trash_add, &h_snapshot_trash_add); + cls_register_cxx_method(h_class, "get_all_features", + CLS_METHOD_RD, + get_all_features, &h_get_all_features); + + // NOTE: deprecate v1 parent APIs after mimic EOLed + cls_register_cxx_method(h_class, "get_parent", + CLS_METHOD_RD, + get_parent, &h_get_parent); + cls_register_cxx_method(h_class, "set_parent", + CLS_METHOD_RD | CLS_METHOD_WR, + set_parent, &h_set_parent); + cls_register_cxx_method(h_class, "remove_parent", + CLS_METHOD_RD | CLS_METHOD_WR, + remove_parent, &h_remove_parent); + + cls_register_cxx_method(h_class, "parent_get", + CLS_METHOD_RD, parent_get, &h_parent_get); + cls_register_cxx_method(h_class, "parent_overlap_get", + CLS_METHOD_RD, parent_overlap_get, + &h_parent_overlap_get); + cls_register_cxx_method(h_class, "parent_attach", + CLS_METHOD_RD | CLS_METHOD_WR, + parent_attach, &h_parent_attach); + cls_register_cxx_method(h_class, "parent_detach", + CLS_METHOD_RD | CLS_METHOD_WR, + parent_detach, &h_parent_detach); + + cls_register_cxx_method(h_class, "set_protection_status", + CLS_METHOD_RD | CLS_METHOD_WR, + set_protection_status, &h_set_protection_status); + cls_register_cxx_method(h_class, "get_protection_status", + CLS_METHOD_RD, + get_protection_status, &h_get_protection_status); + cls_register_cxx_method(h_class, "get_stripe_unit_count", + CLS_METHOD_RD, + get_stripe_unit_count, &h_get_stripe_unit_count); + cls_register_cxx_method(h_class, "set_stripe_unit_count", + CLS_METHOD_RD | CLS_METHOD_WR, + set_stripe_unit_count, &h_set_stripe_unit_count); + cls_register_cxx_method(h_class, "get_create_timestamp", + CLS_METHOD_RD, + get_create_timestamp, &h_get_create_timestamp); + cls_register_cxx_method(h_class, "get_access_timestamp", + CLS_METHOD_RD, + get_access_timestamp, &h_get_access_timestamp); + cls_register_cxx_method(h_class, "get_modify_timestamp", + CLS_METHOD_RD, + get_modify_timestamp, &h_get_modify_timestamp); + cls_register_cxx_method(h_class, "get_flags", + CLS_METHOD_RD, + get_flags, &h_get_flags); + cls_register_cxx_method(h_class, "set_flags", + CLS_METHOD_RD | CLS_METHOD_WR, + set_flags, &h_set_flags); + cls_register_cxx_method(h_class, "op_features_get", CLS_METHOD_RD, + op_features_get, &h_op_features_get); + cls_register_cxx_method(h_class, "op_features_set", + CLS_METHOD_RD | CLS_METHOD_WR, + op_features_set, &h_op_features_set); + cls_register_cxx_method(h_class, "metadata_list", + CLS_METHOD_RD, + metadata_list, &h_metadata_list); + cls_register_cxx_method(h_class, "metadata_set", + CLS_METHOD_RD | CLS_METHOD_WR, + metadata_set, &h_metadata_set); + cls_register_cxx_method(h_class, "metadata_remove", + CLS_METHOD_RD | CLS_METHOD_WR, + metadata_remove, &h_metadata_remove); + cls_register_cxx_method(h_class, "metadata_get", + CLS_METHOD_RD, + metadata_get, &h_metadata_get); + cls_register_cxx_method(h_class, "snapshot_get_limit", + CLS_METHOD_RD, + snapshot_get_limit, &h_snapshot_get_limit); + cls_register_cxx_method(h_class, "snapshot_set_limit", + CLS_METHOD_RD | CLS_METHOD_WR, + snapshot_set_limit, &h_snapshot_set_limit); + cls_register_cxx_method(h_class, "child_attach", + CLS_METHOD_RD | CLS_METHOD_WR, + child_attach, &h_child_attach); + cls_register_cxx_method(h_class, "child_detach", + CLS_METHOD_RD | CLS_METHOD_WR, + child_detach, &h_child_detach); + cls_register_cxx_method(h_class, "children_list", + CLS_METHOD_RD, + children_list, &h_children_list); + cls_register_cxx_method(h_class, "migration_set", + CLS_METHOD_RD | CLS_METHOD_WR, + migration_set, &h_migration_set); + cls_register_cxx_method(h_class, "migration_set_state", + CLS_METHOD_RD | CLS_METHOD_WR, + migration_set_state, &h_migration_set_state); + cls_register_cxx_method(h_class, "migration_get", + CLS_METHOD_RD, + migration_get, &h_migration_get); + cls_register_cxx_method(h_class, "migration_remove", + CLS_METHOD_RD | CLS_METHOD_WR, + migration_remove, &h_migration_remove); + + cls_register_cxx_method(h_class, "set_modify_timestamp", + CLS_METHOD_RD | CLS_METHOD_WR, + set_modify_timestamp, &h_set_modify_timestamp); + + cls_register_cxx_method(h_class, "set_access_timestamp", + CLS_METHOD_RD | CLS_METHOD_WR, + set_access_timestamp, &h_set_access_timestamp); + + /* methods for the rbd_children object */ + cls_register_cxx_method(h_class, "add_child", + CLS_METHOD_RD | CLS_METHOD_WR, + add_child, &h_add_child); + cls_register_cxx_method(h_class, "remove_child", + CLS_METHOD_RD | CLS_METHOD_WR, + remove_child, &h_remove_child); + cls_register_cxx_method(h_class, "get_children", + CLS_METHOD_RD, + get_children, &h_get_children); + + /* methods for the rbd_id.$image_name objects */ + cls_register_cxx_method(h_class, "get_id", + CLS_METHOD_RD, + get_id, &h_get_id); + cls_register_cxx_method(h_class, "set_id", + CLS_METHOD_RD | CLS_METHOD_WR, + set_id, &h_set_id); + + /* methods for the rbd_directory object */ + cls_register_cxx_method(h_class, "dir_get_id", + CLS_METHOD_RD, + dir_get_id, &h_dir_get_id); + cls_register_cxx_method(h_class, "dir_get_name", + CLS_METHOD_RD, + dir_get_name, &h_dir_get_name); + cls_register_cxx_method(h_class, "dir_list", + CLS_METHOD_RD, + dir_list, &h_dir_list); + cls_register_cxx_method(h_class, "dir_add_image", + CLS_METHOD_RD | CLS_METHOD_WR, + dir_add_image, &h_dir_add_image); + cls_register_cxx_method(h_class, "dir_remove_image", + CLS_METHOD_RD | CLS_METHOD_WR, + dir_remove_image, &h_dir_remove_image); + cls_register_cxx_method(h_class, "dir_rename_image", + CLS_METHOD_RD | CLS_METHOD_WR, + dir_rename_image, &h_dir_rename_image); + cls_register_cxx_method(h_class, "dir_state_assert", CLS_METHOD_RD, + dir_state_assert, &h_dir_state_assert); + cls_register_cxx_method(h_class, "dir_state_set", + CLS_METHOD_RD | CLS_METHOD_WR, + dir_state_set, &h_dir_state_set); + + /* methods for the rbd_object_map.$image_id object */ + cls_register_cxx_method(h_class, "object_map_load", + CLS_METHOD_RD, + object_map_load, &h_object_map_load); + cls_register_cxx_method(h_class, "object_map_save", + CLS_METHOD_RD | CLS_METHOD_WR, + object_map_save, &h_object_map_save); + cls_register_cxx_method(h_class, "object_map_resize", + CLS_METHOD_RD | CLS_METHOD_WR, + object_map_resize, &h_object_map_resize); + cls_register_cxx_method(h_class, "object_map_update", + CLS_METHOD_RD | CLS_METHOD_WR, + object_map_update, &h_object_map_update); + cls_register_cxx_method(h_class, "object_map_snap_add", + CLS_METHOD_RD | CLS_METHOD_WR, + object_map_snap_add, &h_object_map_snap_add); + cls_register_cxx_method(h_class, "object_map_snap_remove", + CLS_METHOD_RD | CLS_METHOD_WR, + object_map_snap_remove, &h_object_map_snap_remove); + + /* methods for the old format */ + cls_register_cxx_method(h_class, "snap_list", + CLS_METHOD_RD, + old_snapshots_list, &h_old_snapshots_list); + cls_register_cxx_method(h_class, "snap_add", + CLS_METHOD_RD | CLS_METHOD_WR, + old_snapshot_add, &h_old_snapshot_add); + cls_register_cxx_method(h_class, "snap_remove", + CLS_METHOD_RD | CLS_METHOD_WR, + old_snapshot_remove, &h_old_snapshot_remove); + cls_register_cxx_method(h_class, "snap_rename", + CLS_METHOD_RD | CLS_METHOD_WR, + old_snapshot_rename, &h_old_snapshot_rename); + + /* methods for the rbd_mirroring object */ + cls_register_cxx_method(h_class, "mirror_uuid_get", CLS_METHOD_RD, + mirror_uuid_get, &h_mirror_uuid_get); + cls_register_cxx_method(h_class, "mirror_uuid_set", + CLS_METHOD_RD | CLS_METHOD_WR, + mirror_uuid_set, &h_mirror_uuid_set); + cls_register_cxx_method(h_class, "mirror_mode_get", CLS_METHOD_RD, + mirror_mode_get, &h_mirror_mode_get); + cls_register_cxx_method(h_class, "mirror_mode_set", + CLS_METHOD_RD | CLS_METHOD_WR, + mirror_mode_set, &h_mirror_mode_set); + cls_register_cxx_method(h_class, "mirror_peer_list", CLS_METHOD_RD, + mirror_peer_list, &h_mirror_peer_list); + cls_register_cxx_method(h_class, "mirror_peer_add", + CLS_METHOD_RD | CLS_METHOD_WR, + mirror_peer_add, &h_mirror_peer_add); + cls_register_cxx_method(h_class, "mirror_peer_remove", + CLS_METHOD_RD | CLS_METHOD_WR, + mirror_peer_remove, &h_mirror_peer_remove); + cls_register_cxx_method(h_class, "mirror_peer_set_client", + CLS_METHOD_RD | CLS_METHOD_WR, + mirror_peer_set_client, &h_mirror_peer_set_client); + cls_register_cxx_method(h_class, "mirror_peer_set_cluster", + CLS_METHOD_RD | CLS_METHOD_WR, + mirror_peer_set_cluster, &h_mirror_peer_set_cluster); + cls_register_cxx_method(h_class, "mirror_image_list", CLS_METHOD_RD, + mirror_image_list, &h_mirror_image_list); + cls_register_cxx_method(h_class, "mirror_image_get_image_id", CLS_METHOD_RD, + mirror_image_get_image_id, + &h_mirror_image_get_image_id); + cls_register_cxx_method(h_class, "mirror_image_get", CLS_METHOD_RD, + mirror_image_get, &h_mirror_image_get); + cls_register_cxx_method(h_class, "mirror_image_set", + CLS_METHOD_RD | CLS_METHOD_WR, + mirror_image_set, &h_mirror_image_set); + cls_register_cxx_method(h_class, "mirror_image_remove", + CLS_METHOD_RD | CLS_METHOD_WR, + mirror_image_remove, &h_mirror_image_remove); + cls_register_cxx_method(h_class, "mirror_image_status_set", + CLS_METHOD_RD | CLS_METHOD_WR | CLS_METHOD_PROMOTE, + mirror_image_status_set, &h_mirror_image_status_set); + cls_register_cxx_method(h_class, "mirror_image_status_remove", + CLS_METHOD_RD | CLS_METHOD_WR, + mirror_image_status_remove, + &h_mirror_image_status_remove); + cls_register_cxx_method(h_class, "mirror_image_status_get", CLS_METHOD_RD, + mirror_image_status_get, &h_mirror_image_status_get); + cls_register_cxx_method(h_class, "mirror_image_status_list", CLS_METHOD_RD, + mirror_image_status_list, + &h_mirror_image_status_list); + cls_register_cxx_method(h_class, "mirror_image_status_get_summary", + CLS_METHOD_RD, mirror_image_status_get_summary, + &h_mirror_image_status_get_summary); + cls_register_cxx_method(h_class, "mirror_image_status_remove_down", + CLS_METHOD_RD | CLS_METHOD_WR, + mirror_image_status_remove_down, + &h_mirror_image_status_remove_down); + cls_register_cxx_method(h_class, "mirror_image_instance_get", CLS_METHOD_RD, + mirror_image_instance_get, + &h_mirror_image_instance_get); + cls_register_cxx_method(h_class, "mirror_image_instance_list", CLS_METHOD_RD, + mirror_image_instance_list, + &h_mirror_image_instance_list); + cls_register_cxx_method(h_class, "mirror_instances_list", CLS_METHOD_RD, + mirror_instances_list, &h_mirror_instances_list); + cls_register_cxx_method(h_class, "mirror_instances_add", + CLS_METHOD_RD | CLS_METHOD_WR | CLS_METHOD_PROMOTE, + mirror_instances_add, &h_mirror_instances_add); + cls_register_cxx_method(h_class, "mirror_instances_remove", + CLS_METHOD_RD | CLS_METHOD_WR, + mirror_instances_remove, + &h_mirror_instances_remove); + cls_register_cxx_method(h_class, "mirror_image_map_list", + CLS_METHOD_RD, mirror_image_map_list, + &h_mirror_image_map_list); + cls_register_cxx_method(h_class, "mirror_image_map_update", + CLS_METHOD_WR, mirror_image_map_update, + &h_mirror_image_map_update); + cls_register_cxx_method(h_class, "mirror_image_map_remove", + CLS_METHOD_WR, mirror_image_map_remove, + &h_mirror_image_map_remove); + + /* methods for the groups feature */ + cls_register_cxx_method(h_class, "group_dir_list", + CLS_METHOD_RD, + group_dir_list, &h_group_dir_list); + cls_register_cxx_method(h_class, "group_dir_add", + CLS_METHOD_RD | CLS_METHOD_WR, + group_dir_add, &h_group_dir_add); + cls_register_cxx_method(h_class, "group_dir_remove", + CLS_METHOD_RD | CLS_METHOD_WR, + group_dir_remove, &h_group_dir_remove); + cls_register_cxx_method(h_class, "group_dir_rename", + CLS_METHOD_RD | CLS_METHOD_WR, + group_dir_rename, &h_group_dir_rename); + cls_register_cxx_method(h_class, "group_image_remove", + CLS_METHOD_RD | CLS_METHOD_WR, + group_image_remove, &h_group_image_remove); + cls_register_cxx_method(h_class, "group_image_list", + CLS_METHOD_RD, + group_image_list, &h_group_image_list); + cls_register_cxx_method(h_class, "group_image_set", + CLS_METHOD_RD | CLS_METHOD_WR, + group_image_set, &h_group_image_set); + cls_register_cxx_method(h_class, "image_group_add", + CLS_METHOD_RD | CLS_METHOD_WR, + image_group_add, &h_image_group_add); + cls_register_cxx_method(h_class, "image_group_remove", + CLS_METHOD_RD | CLS_METHOD_WR, + image_group_remove, &h_image_group_remove); + cls_register_cxx_method(h_class, "image_group_get", + CLS_METHOD_RD, + image_group_get, &h_image_group_get); + cls_register_cxx_method(h_class, "group_snap_set", + CLS_METHOD_RD | CLS_METHOD_WR, + group_snap_set, &h_group_snap_set); + cls_register_cxx_method(h_class, "group_snap_remove", + CLS_METHOD_RD | CLS_METHOD_WR, + group_snap_remove, &h_group_snap_remove); + cls_register_cxx_method(h_class, "group_snap_get_by_id", + CLS_METHOD_RD, + group_snap_get_by_id, &h_group_snap_get_by_id); + cls_register_cxx_method(h_class, "group_snap_list", + CLS_METHOD_RD, + group_snap_list, &h_group_snap_list); + + /* rbd_trash object methods */ + cls_register_cxx_method(h_class, "trash_add", + CLS_METHOD_RD | CLS_METHOD_WR, + trash_add, &h_trash_add); + cls_register_cxx_method(h_class, "trash_remove", + CLS_METHOD_RD | CLS_METHOD_WR, + trash_remove, &h_trash_remove); + cls_register_cxx_method(h_class, "trash_list", + CLS_METHOD_RD, + trash_list, &h_trash_list); + cls_register_cxx_method(h_class, "trash_get", + CLS_METHOD_RD, + trash_get, &h_trash_get); + cls_register_cxx_method(h_class, "trash_state_set", + CLS_METHOD_RD | CLS_METHOD_WR, + trash_state_set, &h_trash_state_set); + + /* rbd_namespace object methods */ + cls_register_cxx_method(h_class, "namespace_add", + CLS_METHOD_RD | CLS_METHOD_WR, + namespace_add, &h_namespace_add); + cls_register_cxx_method(h_class, "namespace_remove", + CLS_METHOD_RD | CLS_METHOD_WR, + namespace_remove, &h_namespace_remove); + cls_register_cxx_method(h_class, "namespace_list", CLS_METHOD_RD, + namespace_list, &h_namespace_list); + + /* data object methods */ + cls_register_cxx_method(h_class, "copyup", + CLS_METHOD_RD | CLS_METHOD_WR, + copyup, &h_copyup); + cls_register_cxx_method(h_class, "assert_snapc_seq", + CLS_METHOD_RD | CLS_METHOD_WR, + assert_snapc_seq, + &h_assert_snapc_seq); + cls_register_cxx_method(h_class, "sparsify", + CLS_METHOD_RD | CLS_METHOD_WR, + sparsify, &h_sparsify); +} diff --git a/src/cls/rbd/cls_rbd.h b/src/cls/rbd/cls_rbd.h new file mode 100644 index 00000000..0dc0a9b9 --- /dev/null +++ b/src/cls/rbd/cls_rbd.h @@ -0,0 +1,243 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef __CEPH_CLS_RBD_H +#define __CEPH_CLS_RBD_H + +#include "include/types.h" +#include "include/buffer_fwd.h" +#include "include/rbd_types.h" +#include "common/Formatter.h" +#include "cls/rbd/cls_rbd_types.h" + +/// information about our parent image, if any +struct cls_rbd_parent { + int64_t pool_id = -1; + std::string pool_namespace; + std::string image_id; + snapid_t snap_id = CEPH_NOSNAP; + std::optional<uint64_t> head_overlap = std::nullopt; + + cls_rbd_parent() { + } + cls_rbd_parent(const cls::rbd::ParentImageSpec& parent_image_spec, + const std::optional<uint64_t>& head_overlap) + : pool_id(parent_image_spec.pool_id), + pool_namespace(parent_image_spec.pool_namespace), + image_id(parent_image_spec.image_id), snap_id(parent_image_spec.snap_id), + head_overlap(head_overlap) { + } + + inline bool exists() const { + return (pool_id >= 0 && !image_id.empty() && snap_id != CEPH_NOSNAP); + } + + inline bool operator==(const cls_rbd_parent& rhs) const { + return (pool_id == rhs.pool_id && + pool_namespace == rhs.pool_namespace && + image_id == rhs.image_id && + snap_id == rhs.snap_id); + } + inline bool operator!=(const cls_rbd_parent& rhs) const { + return !(*this == rhs); + } + + void encode(bufferlist& bl, uint64_t features) const { + // NOTE: remove support for version 1 after Nautilus EOLed + uint8_t version = 1; + if ((features & CEPH_FEATURE_SERVER_NAUTILUS) != 0ULL) { + // break backwards compatability when using nautilus or later OSDs + version = 2; + } + + ENCODE_START(version, version, bl); + encode(pool_id, bl); + if (version >= 2) { + encode(pool_namespace, bl); + } + encode(image_id, bl); + encode(snap_id, bl); + if (version == 1) { + encode(head_overlap.value_or(0ULL), bl); + } else { + encode(head_overlap, bl); + } + ENCODE_FINISH(bl); + } + + void decode(bufferlist::const_iterator& bl) { + DECODE_START(2, bl); + decode(pool_id, bl); + if (struct_v >= 2) { + decode(pool_namespace, bl); + } + decode(image_id, bl); + decode(snap_id, bl); + if (struct_v == 1) { + uint64_t overlap; + decode(overlap, bl); + head_overlap = overlap; + } else { + decode(head_overlap, bl); + } + DECODE_FINISH(bl); + } + + void dump(Formatter *f) const { + f->dump_int("pool_id", pool_id); + f->dump_string("pool_namespace", pool_namespace); + f->dump_string("image_id", image_id); + f->dump_unsigned("snap_id", snap_id); + if (head_overlap) { + f->dump_unsigned("head_overlap", *head_overlap); + } + } + + static void generate_test_instances(list<cls_rbd_parent*>& o) { + o.push_back(new cls_rbd_parent{}); + o.push_back(new cls_rbd_parent{{1, "", "image id", 234}, {}}); + o.push_back(new cls_rbd_parent{{1, "", "image id", 234}, {123}}); + o.push_back(new cls_rbd_parent{{1, "ns", "image id", 234}, {123}}); + } +}; +WRITE_CLASS_ENCODER_FEATURES(cls_rbd_parent) + +struct cls_rbd_snap { + snapid_t id = CEPH_NOSNAP; + std::string name; + uint64_t image_size = 0; + uint8_t protection_status = RBD_PROTECTION_STATUS_UNPROTECTED; + cls_rbd_parent parent; + uint64_t flags = 0; + utime_t timestamp; + cls::rbd::SnapshotNamespace snapshot_namespace = { + cls::rbd::UserSnapshotNamespace{}}; + uint32_t child_count = 0; + std::optional<uint64_t> parent_overlap = std::nullopt; + + cls_rbd_snap() { + } + cls_rbd_snap(snapid_t id, const std::string& name, uint64_t image_size, + uint8_t protection_status, const cls_rbd_parent& parent, + uint64_t flags, utime_t timestamp, + const cls::rbd::SnapshotNamespace& snapshot_namespace, + uint32_t child_count, + const std::optional<uint64_t>& parent_overlap) + : id(id), name(name), image_size(image_size), + protection_status(protection_status), parent(parent), flags(flags), + timestamp(timestamp), snapshot_namespace(snapshot_namespace), + child_count(child_count), parent_overlap(parent_overlap) { + } + + bool migrate_parent_format(uint64_t features) const { + return (((features & CEPH_FEATURE_SERVER_NAUTILUS) != 0) && + (parent.exists())); + } + + void encode(bufferlist& bl, uint64_t features) const { + // NOTE: remove support for versions < 8 after Nautilus EOLed + uint8_t min_version = 1; + if ((features & CEPH_FEATURE_SERVER_NAUTILUS) != 0ULL) { + // break backwards compatability when using nautilus or later OSDs + min_version = 8; + } + + ENCODE_START(8, min_version, bl); + encode(id, bl); + encode(name, bl); + encode(image_size, bl); + if (min_version < 8) { + uint64_t image_features = 0; + encode(image_features, bl); // unused -- preserve ABI + encode(parent, bl, features); + } + encode(protection_status, bl); + encode(flags, bl); + encode(snapshot_namespace, bl); + encode(timestamp, bl); + encode(child_count, bl); + encode(parent_overlap, bl); + ENCODE_FINISH(bl); + } + + void decode(bufferlist::const_iterator& p) { + DECODE_START(8, p); + decode(id, p); + decode(name, p); + decode(image_size, p); + if (struct_compat < 8) { + uint64_t features; + decode(features, p); // unused -- preserve ABI + } + if (struct_v >= 2 && struct_compat < 8) { + decode(parent, p); + } + if (struct_v >= 3) { + decode(protection_status, p); + } + if (struct_v >= 4) { + decode(flags, p); + } + if (struct_v >= 5) { + decode(snapshot_namespace, p); + } + if (struct_v >= 6) { + decode(timestamp, p); + } + if (struct_v >= 7) { + decode(child_count, p); + } + if (struct_v >= 8) { + decode(parent_overlap, p); + } + DECODE_FINISH(p); + } + + void dump(Formatter *f) const { + f->dump_unsigned("id", id); + f->dump_string("name", name); + f->dump_unsigned("image_size", image_size); + if (parent.exists()) { + f->open_object_section("parent"); + parent.dump(f); + f->close_section(); + } + switch (protection_status) { + case RBD_PROTECTION_STATUS_UNPROTECTED: + f->dump_string("protection_status", "unprotected"); + break; + case RBD_PROTECTION_STATUS_UNPROTECTING: + f->dump_string("protection_status", "unprotecting"); + break; + case RBD_PROTECTION_STATUS_PROTECTED: + f->dump_string("protection_status", "protected"); + break; + default: + ceph_abort(); + } + f->dump_unsigned("child_count", child_count); + if (parent_overlap) { + f->dump_unsigned("parent_overlap", *parent_overlap); + } + } + + static void generate_test_instances(list<cls_rbd_snap*>& o) { + o.push_back(new cls_rbd_snap{}); + o.push_back(new cls_rbd_snap{1, "snap", 123456, + RBD_PROTECTION_STATUS_PROTECTED, + {{1, "", "image", 123}, 234}, 31, {}, + cls::rbd::UserSnapshotNamespace{}, 543, {}}); + o.push_back(new cls_rbd_snap{1, "snap", 123456, + RBD_PROTECTION_STATUS_PROTECTED, + {{1, "", "image", 123}, 234}, 31, {}, + cls::rbd::UserSnapshotNamespace{}, 543, {0}}); + o.push_back(new cls_rbd_snap{1, "snap", 123456, + RBD_PROTECTION_STATUS_PROTECTED, + {{1, "ns", "image", 123}, 234}, 31, {}, + cls::rbd::UserSnapshotNamespace{}, 543, + {123}}); + } +}; +WRITE_CLASS_ENCODER_FEATURES(cls_rbd_snap) + +#endif // __CEPH_CLS_RBD_H diff --git a/src/cls/rbd/cls_rbd_client.cc b/src/cls/rbd/cls_rbd_client.cc new file mode 100644 index 00000000..9b868c55 --- /dev/null +++ b/src/cls/rbd/cls_rbd_client.cc @@ -0,0 +1,2807 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "cls/rbd/cls_rbd_client.h" +#include "cls/lock/cls_lock_client.h" +#include "include/buffer.h" +#include "include/encoding.h" +#include "include/rbd_types.h" +#include "include/rados/librados.hpp" +#include "common/bit_vector.hpp" + +#include <errno.h> + +namespace librbd { +namespace cls_client { + +void create_image(librados::ObjectWriteOperation *op, uint64_t size, + uint8_t order, uint64_t features, + const std::string &object_prefix, int64_t data_pool_id) +{ + bufferlist bl; + encode(size, bl); + encode(order, bl); + encode(features, bl); + encode(object_prefix, bl); + encode(data_pool_id, bl); + + op->exec("rbd", "create", bl); +} + +int create_image(librados::IoCtx *ioctx, const std::string &oid, + uint64_t size, uint8_t order, uint64_t features, + const std::string &object_prefix, int64_t data_pool_id) +{ + librados::ObjectWriteOperation op; + create_image(&op, size, order, features, object_prefix, data_pool_id); + + return ioctx->operate(oid, &op); +} + +void get_features_start(librados::ObjectReadOperation *op, bool read_only) +{ + bufferlist bl; + encode(static_cast<uint64_t>(CEPH_NOSNAP), bl); + encode(read_only, bl); + op->exec("rbd", "get_features", bl); +} + +int get_features_finish(bufferlist::const_iterator *it, uint64_t *features, + uint64_t *incompatible_features) +{ + try { + decode(*features, *it); + decode(*incompatible_features, *it); + } catch (const buffer::error &err) { + return -EBADMSG; + } + + return 0; +} + +int get_features(librados::IoCtx *ioctx, const std::string &oid, + bool read_only, uint64_t *features, + uint64_t *incompatible_features) +{ + librados::ObjectReadOperation op; + get_features_start(&op, read_only); + + bufferlist out_bl; + int r = ioctx->operate(oid, &op, &out_bl); + if (r < 0) { + return r; + } + + auto it = out_bl.cbegin(); + return get_features_finish(&it, features, incompatible_features); +} + +void set_features(librados::ObjectWriteOperation *op, uint64_t features, + uint64_t mask) +{ + bufferlist bl; + encode(features, bl); + encode(mask, bl); + + op->exec("rbd", "set_features", bl); +} + +int set_features(librados::IoCtx *ioctx, const std::string &oid, + uint64_t features, uint64_t mask) +{ + librados::ObjectWriteOperation op; + set_features(&op, features, mask); + + return ioctx->operate(oid, &op); +} + +void get_object_prefix_start(librados::ObjectReadOperation *op) +{ + bufferlist bl; + op->exec("rbd", "get_object_prefix", bl); +} + +int get_object_prefix_finish(bufferlist::const_iterator *it, + std::string *object_prefix) +{ + try { + decode(*object_prefix, *it); + } catch (const buffer::error &err) { + return -EBADMSG; + } + return 0; +} + +int get_object_prefix(librados::IoCtx *ioctx, const std::string &oid, + std::string *object_prefix) +{ + librados::ObjectReadOperation op; + get_object_prefix_start(&op); + + bufferlist out_bl; + int r = ioctx->operate(oid, &op, &out_bl); + if (r < 0) { + return r; + } + + auto it = out_bl.cbegin(); + return get_object_prefix_finish(&it, object_prefix); +} + +void get_data_pool_start(librados::ObjectReadOperation *op) { + bufferlist bl; + op->exec("rbd", "get_data_pool", bl); +} + +int get_data_pool_finish(bufferlist::const_iterator *it, int64_t *data_pool_id) { + try { + decode(*data_pool_id, *it); + } catch (const buffer::error &err) { + return -EBADMSG; + } + return 0; +} + +int get_data_pool(librados::IoCtx *ioctx, const std::string &oid, + int64_t *data_pool_id) { + librados::ObjectReadOperation op; + get_data_pool_start(&op); + + bufferlist out_bl; + int r = ioctx->operate(oid, &op, &out_bl); + if (r < 0) { + return r; + } + + auto it = out_bl.cbegin(); + return get_data_pool_finish(&it, data_pool_id); +} + +void get_size_start(librados::ObjectReadOperation *op, snapid_t snap_id) +{ + bufferlist bl; + encode(snap_id, bl); + op->exec("rbd", "get_size", bl); +} + +int get_size_finish(bufferlist::const_iterator *it, uint64_t *size, + uint8_t *order) +{ + try { + decode(*order, *it); + decode(*size, *it); + } catch (const buffer::error &err) { + return -EBADMSG; + } + return 0; +} + +int get_size(librados::IoCtx *ioctx, const std::string &oid, + snapid_t snap_id, uint64_t *size, uint8_t *order) +{ + librados::ObjectReadOperation op; + get_size_start(&op, snap_id); + + bufferlist out_bl; + int r = ioctx->operate(oid, &op, &out_bl); + if (r < 0) { + return r; + } + + auto it = out_bl.cbegin(); + return get_size_finish(&it, size, order); +} + +int set_size(librados::IoCtx *ioctx, const std::string &oid, + uint64_t size) +{ + librados::ObjectWriteOperation op; + set_size(&op, size); + return ioctx->operate(oid, &op); +} + +void set_size(librados::ObjectWriteOperation *op, uint64_t size) +{ + bufferlist bl; + encode(size, bl); + op->exec("rbd", "set_size", bl); +} + +void get_flags_start(librados::ObjectReadOperation *op, snapid_t snap_id) { + bufferlist in_bl; + encode(static_cast<snapid_t>(snap_id), in_bl); + op->exec("rbd", "get_flags", in_bl); +} + +int get_flags_finish(bufferlist::const_iterator *it, uint64_t *flags) { + try { + decode(*flags, *it); + } catch (const buffer::error &err) { + return -EBADMSG; + } + return 0; +} + +int get_flags(librados::IoCtx *ioctx, const std::string &oid, + snapid_t snap_id, uint64_t *flags) +{ + librados::ObjectReadOperation op; + get_flags_start(&op, snap_id); + + bufferlist out_bl; + int r = ioctx->operate(oid, &op, &out_bl); + if (r < 0) { + return r; + } + + auto it = out_bl.cbegin(); + return get_flags_finish(&it, flags); +} + +void set_flags(librados::ObjectWriteOperation *op, snapid_t snap_id, + uint64_t flags, uint64_t mask) +{ + bufferlist inbl; + encode(flags, inbl); + encode(mask, inbl); + encode(snap_id, inbl); + op->exec("rbd", "set_flags", inbl); +} + +void op_features_get_start(librados::ObjectReadOperation *op) +{ + bufferlist in_bl; + op->exec("rbd", "op_features_get", in_bl); +} + +int op_features_get_finish(bufferlist::const_iterator *it, uint64_t *op_features) +{ + try { + decode(*op_features, *it); + } catch (const buffer::error &err) { + return -EBADMSG; + } + return 0; +} + +int op_features_get(librados::IoCtx *ioctx, const std::string &oid, + uint64_t *op_features) +{ + librados::ObjectReadOperation op; + op_features_get_start(&op); + + bufferlist out_bl; + int r = ioctx->operate(oid, &op, &out_bl); + if (r < 0) { + return r; + } + + auto it = out_bl.cbegin(); + return op_features_get_finish(&it, op_features); +} + +void op_features_set(librados::ObjectWriteOperation *op, + uint64_t op_features, uint64_t mask) +{ + bufferlist inbl; + encode(op_features, inbl); + encode(mask, inbl); + op->exec("rbd", "op_features_set", inbl); +} + +int op_features_set(librados::IoCtx *ioctx, const std::string &oid, + uint64_t op_features, uint64_t mask) +{ + librados::ObjectWriteOperation op; + op_features_set(&op, op_features, mask); + + return ioctx->operate(oid, &op); +} + +void get_parent_start(librados::ObjectReadOperation *op, snapid_t snap_id) +{ + bufferlist bl; + encode(snap_id, bl); + op->exec("rbd", "get_parent", bl); +} + +int get_parent_finish(bufferlist::const_iterator *it, + cls::rbd::ParentImageSpec *pspec, + uint64_t *parent_overlap) +{ + *pspec = {}; + try { + decode(pspec->pool_id, *it); + decode(pspec->image_id, *it); + decode(pspec->snap_id, *it); + decode(*parent_overlap, *it); + } catch (const buffer::error &) { + return -EBADMSG; + } + return 0; +} + +int get_parent(librados::IoCtx *ioctx, const std::string &oid, + snapid_t snap_id, cls::rbd::ParentImageSpec *pspec, + uint64_t *parent_overlap) +{ + librados::ObjectReadOperation op; + get_parent_start(&op, snap_id); + + bufferlist out_bl; + int r = ioctx->operate(oid, &op, &out_bl); + if (r < 0) { + return r; + } + + auto it = out_bl.cbegin(); + return get_parent_finish(&it, pspec, parent_overlap); +} + +int set_parent(librados::IoCtx *ioctx, const std::string &oid, + const cls::rbd::ParentImageSpec &pspec, uint64_t parent_overlap) +{ + librados::ObjectWriteOperation op; + set_parent(&op, pspec, parent_overlap); + return ioctx->operate(oid, &op); +} + +void set_parent(librados::ObjectWriteOperation *op, + const cls::rbd::ParentImageSpec &pspec, + uint64_t parent_overlap) { + assert(pspec.pool_namespace.empty()); + + bufferlist in_bl; + encode(pspec.pool_id, in_bl); + encode(pspec.image_id, in_bl); + encode(pspec.snap_id, in_bl); + encode(parent_overlap, in_bl); + + op->exec("rbd", "set_parent", in_bl); +} + +int remove_parent(librados::IoCtx *ioctx, const std::string &oid) +{ + librados::ObjectWriteOperation op; + remove_parent(&op); + return ioctx->operate(oid, &op); +} + +void remove_parent(librados::ObjectWriteOperation *op) +{ + bufferlist inbl; + op->exec("rbd", "remove_parent", inbl); +} + +void parent_get_start(librados::ObjectReadOperation* op) { + bufferlist in_bl; + op->exec("rbd", "parent_get", in_bl); +} + +int parent_get_finish(bufferlist::const_iterator* it, + cls::rbd::ParentImageSpec* parent_image_spec) { + try { + decode(*parent_image_spec, *it); + } catch (const buffer::error &) { + return -EBADMSG; + } + return 0; +} + +int parent_get(librados::IoCtx* ioctx, const std::string &oid, + cls::rbd::ParentImageSpec* parent_image_spec) { + librados::ObjectReadOperation op; + parent_get_start(&op); + + bufferlist out_bl; + int r = ioctx->operate(oid, &op, &out_bl); + if (r < 0) { + return r; + } + + auto it = out_bl.cbegin(); + r = parent_get_finish(&it, parent_image_spec); + if (r < 0) { + return r; + } + return 0; +} + +void parent_overlap_get_start(librados::ObjectReadOperation* op, + snapid_t snap_id) { + bufferlist in_bl; + encode(snap_id, in_bl); + op->exec("rbd", "parent_overlap_get", in_bl); +} + +int parent_overlap_get_finish(bufferlist::const_iterator* it, + std::optional<uint64_t>* parent_overlap) { + try { + decode(*parent_overlap, *it); + } catch (const buffer::error &) { + return -EBADMSG; + } + return 0; +} + +int parent_overlap_get(librados::IoCtx* ioctx, const std::string &oid, + snapid_t snap_id, + std::optional<uint64_t>* parent_overlap) { + librados::ObjectReadOperation op; + parent_overlap_get_start(&op, snap_id); + + bufferlist out_bl; + int r = ioctx->operate(oid, &op, &out_bl); + if (r < 0) { + return r; + } + + auto it = out_bl.cbegin(); + r = parent_overlap_get_finish(&it, parent_overlap); + if (r < 0) { + return r; + } + return 0; +} + +void parent_attach(librados::ObjectWriteOperation* op, + const cls::rbd::ParentImageSpec& parent_image_spec, + uint64_t parent_overlap, bool reattach) { + bufferlist in_bl; + encode(parent_image_spec, in_bl); + encode(parent_overlap, in_bl); + encode(reattach, in_bl); + op->exec("rbd", "parent_attach", in_bl); +} + +int parent_attach(librados::IoCtx *ioctx, const std::string &oid, + const cls::rbd::ParentImageSpec& parent_image_spec, + uint64_t parent_overlap, bool reattach) { + librados::ObjectWriteOperation op; + parent_attach(&op, parent_image_spec, parent_overlap, reattach); + return ioctx->operate(oid, &op); +} + +void parent_detach(librados::ObjectWriteOperation* op) { + bufferlist in_bl; + op->exec("rbd", "parent_detach", in_bl); +} + +int parent_detach(librados::IoCtx *ioctx, const std::string &oid) { + librados::ObjectWriteOperation op; + parent_detach(&op); + return ioctx->operate(oid, &op); +} + +int add_child(librados::IoCtx *ioctx, const std::string &oid, + const cls::rbd::ParentImageSpec &pspec, + const std::string &c_imageid) +{ + librados::ObjectWriteOperation op; + add_child(&op, pspec, c_imageid); + return ioctx->operate(oid, &op); +} + +void add_child(librados::ObjectWriteOperation *op, + const cls::rbd::ParentImageSpec& pspec, + const std::string &c_imageid) +{ + assert(pspec.pool_namespace.empty()); + + bufferlist in; + encode(pspec.pool_id, in); + encode(pspec.image_id, in); + encode(pspec.snap_id, in); + encode(c_imageid, in); + + op->exec("rbd", "add_child", in); +} + +void remove_child(librados::ObjectWriteOperation *op, + const cls::rbd::ParentImageSpec &pspec, + const std::string &c_imageid) +{ + assert(pspec.pool_namespace.empty()); + + bufferlist in; + encode(pspec.pool_id, in); + encode(pspec.image_id, in); + encode(pspec.snap_id, in); + encode(c_imageid, in); + op->exec("rbd", "remove_child", in); +} + +int remove_child(librados::IoCtx *ioctx, const std::string &oid, + const cls::rbd::ParentImageSpec &pspec, + const std::string &c_imageid) +{ + librados::ObjectWriteOperation op; + remove_child(&op, pspec, c_imageid); + return ioctx->operate(oid, &op); +} + +void get_children_start(librados::ObjectReadOperation *op, + const cls::rbd::ParentImageSpec &pspec) { + bufferlist in_bl; + encode(pspec.pool_id, in_bl); + encode(pspec.image_id, in_bl); + encode(pspec.snap_id, in_bl); + op->exec("rbd", "get_children", in_bl); +} + +int get_children_finish(bufferlist::const_iterator *it, + std::set<std::string>* children) { + try { + decode(*children, *it); + } catch (const buffer::error &err) { + return -EBADMSG; + } + return 0; +} + +int get_children(librados::IoCtx *ioctx, const std::string &oid, + const cls::rbd::ParentImageSpec &pspec, set<string>& children) +{ + librados::ObjectReadOperation op; + get_children_start(&op, pspec); + + bufferlist out_bl; + int r = ioctx->operate(oid, &op, &out_bl); + if (r < 0) { + return r; + } + + auto it = out_bl.cbegin(); + return get_children_finish(&it, &children); +} + +void snapshot_get_start(librados::ObjectReadOperation *op, snapid_t snap_id) +{ + bufferlist bl; + encode(snap_id, bl); + op->exec("rbd", "snapshot_get", bl); +} + +int snapshot_get_finish(bufferlist::const_iterator* it, + cls::rbd::SnapshotInfo* snap_info) +{ + try { + decode(*snap_info, *it); + } catch (const buffer::error &err) { + return -EBADMSG; + } + return 0; +} + +int snapshot_get(librados::IoCtx *ioctx, const std::string &oid, + snapid_t snap_id, cls::rbd::SnapshotInfo* snap_info) +{ + librados::ObjectReadOperation op; + snapshot_get_start(&op, snap_id); + + bufferlist out_bl; + int r = ioctx->operate(oid, &op, &out_bl); + if (r < 0) { + return r; + } + + auto it = out_bl.cbegin(); + return snapshot_get_finish(&it, snap_info); +} + +void snapshot_add(librados::ObjectWriteOperation *op, snapid_t snap_id, + const std::string &snap_name, + const cls::rbd::SnapshotNamespace &snap_namespace) +{ + bufferlist bl; + encode(snap_name, bl); + encode(snap_id, bl); + encode(snap_namespace, bl); + op->exec("rbd", "snapshot_add", bl); +} + +void snapshot_remove(librados::ObjectWriteOperation *op, snapid_t snap_id) +{ + bufferlist bl; + encode(snap_id, bl); + op->exec("rbd", "snapshot_remove", bl); +} + +void snapshot_rename(librados::ObjectWriteOperation *op, + snapid_t src_snap_id, + const std::string &dst_name) +{ + bufferlist bl; + encode(src_snap_id, bl); + encode(dst_name, bl); + op->exec("rbd", "snapshot_rename", bl); +} + +void snapshot_trash_add(librados::ObjectWriteOperation *op, + snapid_t snap_id) +{ + bufferlist bl; + encode(snap_id, bl); + op->exec("rbd", "snapshot_trash_add", bl); +} + +void get_snapcontext_start(librados::ObjectReadOperation *op) +{ + bufferlist bl; + op->exec("rbd", "get_snapcontext", bl); +} + +int get_snapcontext_finish(bufferlist::const_iterator *it, + ::SnapContext *snapc) +{ + try { + decode(*snapc, *it); + } catch (const buffer::error &err) { + return -EBADMSG; + } + if (!snapc->is_valid()) { + return -EBADMSG; + } + return 0; +} + +int get_snapcontext(librados::IoCtx *ioctx, const std::string &oid, + ::SnapContext *snapc) +{ + librados::ObjectReadOperation op; + get_snapcontext_start(&op); + + bufferlist out_bl; + int r = ioctx->operate(oid, &op, &out_bl); + if (r < 0) { + return r; + } + + auto bl_it = out_bl.cbegin(); + return get_snapcontext_finish(&bl_it, snapc); +} + +void get_snapshot_name_start(librados::ObjectReadOperation *op, + snapid_t snap_id) +{ + bufferlist bl; + encode(snap_id, bl); + op->exec("rbd", "get_snapshot_name", bl); +} + +int get_snapshot_name_finish(bufferlist::const_iterator *it, + std::string *name) +{ + try { + decode(*name, *it); + } catch (const buffer::error &err) { + return -EBADMSG; + } + return 0; +} + +int get_snapshot_name(librados::IoCtx *ioctx, const std::string &oid, + snapid_t snap_id, std::string *name) +{ + librados::ObjectReadOperation op; + get_snapshot_name_start(&op, snap_id); + + bufferlist out_bl; + int r = ioctx->operate(oid, &op, &out_bl); + if (r < 0) { + return r; + } + + auto it = out_bl.cbegin(); + return get_snapshot_name_finish(&it, name); +} + +void get_snapshot_timestamp_start(librados::ObjectReadOperation *op, + snapid_t snap_id) +{ + bufferlist bl; + encode(snap_id, bl); + op->exec("rbd", "get_snapshot_timestamp", bl); +} + +int get_snapshot_timestamp_finish(bufferlist::const_iterator *it, + utime_t *timestamp) +{ + try { + decode(*timestamp, *it); + } catch (const buffer::error &err) { + return -EBADMSG; + } + return 0; +} + +int get_snapshot_timestamp(librados::IoCtx *ioctx, const std::string &oid, + snapid_t snap_id, utime_t *timestamp) +{ + librados::ObjectReadOperation op; + get_snapshot_timestamp_start(&op, snap_id); + + bufferlist out_bl; + int r = ioctx->operate(oid, &op, &out_bl); + if (r < 0) { + return r; + } + + auto it = out_bl.cbegin(); + return get_snapshot_timestamp_finish(&it, timestamp); +} + +void old_snapshot_add(librados::ObjectWriteOperation *op, + snapid_t snap_id, const std::string &snap_name) +{ + bufferlist bl; + encode(snap_name, bl); + encode(snap_id, bl); + op->exec("rbd", "snap_add", bl); +} + +void old_snapshot_remove(librados::ObjectWriteOperation *op, + const std::string &snap_name) +{ + bufferlist bl; + encode(snap_name, bl); + op->exec("rbd", "snap_remove", bl); +} + +void old_snapshot_rename(librados::ObjectWriteOperation *op, + snapid_t src_snap_id, const std::string &dst_name) +{ + bufferlist bl; + encode(src_snap_id, bl); + encode(dst_name, bl); + op->exec("rbd", "snap_rename", bl); +} + +void old_snapshot_list_start(librados::ObjectReadOperation *op) { + bufferlist in_bl; + op->exec("rbd", "snap_list", in_bl); +} + +int old_snapshot_list_finish(bufferlist::const_iterator *it, + std::vector<string> *names, + std::vector<uint64_t> *sizes, + ::SnapContext *snapc) { + try { + uint32_t num_snaps; + decode(snapc->seq, *it); + decode(num_snaps, *it); + + names->resize(num_snaps); + sizes->resize(num_snaps); + snapc->snaps.resize(num_snaps); + for (uint32_t i = 0; i < num_snaps; ++i) { + decode(snapc->snaps[i], *it); + decode((*sizes)[i], *it); + decode((*names)[i], *it); + } + } catch (const buffer::error &err) { + return -EBADMSG; + } + return 0; +} + +int old_snapshot_list(librados::IoCtx *ioctx, const std::string &oid, + std::vector<string> *names, + std::vector<uint64_t> *sizes, + ::SnapContext *snapc) +{ + librados::ObjectReadOperation op; + old_snapshot_list_start(&op); + + bufferlist out_bl; + int r = ioctx->operate(oid, &op, &out_bl); + if (r < 0) { + return r; + } + + auto it = out_bl.cbegin(); + return old_snapshot_list_finish(&it, names, sizes, snapc); +} + +void get_all_features_start(librados::ObjectReadOperation *op) { + bufferlist in; + op->exec("rbd", "get_all_features", in); +} + +int get_all_features_finish(bufferlist::const_iterator *it, + uint64_t *all_features) { + try { + decode(*all_features, *it); + } catch (const buffer::error &err) { + return -EBADMSG; + } + return 0; +} + +int get_all_features(librados::IoCtx *ioctx, const std::string &oid, + uint64_t *all_features) { + librados::ObjectReadOperation op; + get_all_features_start(&op); + + bufferlist out_bl; + int r = ioctx->operate(oid, &op, &out_bl); + if (r < 0) { + return r; + } + + auto it = out_bl.cbegin(); + return get_all_features_finish(&it, all_features); +} + +int copyup(librados::IoCtx *ioctx, const std::string &oid, + bufferlist data) { + bufferlist out; + return ioctx->exec(oid, "rbd", "copyup", data, out); +} + +void get_protection_status_start(librados::ObjectReadOperation *op, + snapid_t snap_id) +{ + bufferlist bl; + encode(snap_id, bl); + op->exec("rbd", "get_protection_status", bl); +} + +int get_protection_status_finish(bufferlist::const_iterator *it, + uint8_t *protection_status) +{ + try { + decode(*protection_status, *it); + } catch (const buffer::error &) { + return -EBADMSG; + } + return 0; +} + +int get_protection_status(librados::IoCtx *ioctx, const std::string &oid, + snapid_t snap_id, uint8_t *protection_status) +{ + librados::ObjectReadOperation op; + get_protection_status_start(&op, snap_id); + + bufferlist out_bl; + int r = ioctx->operate(oid, &op, &out_bl); + if (r < 0) { + return r; + } + + auto it = out_bl.cbegin(); + return get_protection_status_finish(&it, protection_status); +} + +int set_protection_status(librados::IoCtx *ioctx, const std::string &oid, + snapid_t snap_id, uint8_t protection_status) +{ + // TODO remove + librados::ObjectWriteOperation op; + set_protection_status(&op, snap_id, protection_status); + return ioctx->operate(oid, &op); +} + +void set_protection_status(librados::ObjectWriteOperation *op, + snapid_t snap_id, uint8_t protection_status) +{ + bufferlist in; + encode(snap_id, in); + encode(protection_status, in); + op->exec("rbd", "set_protection_status", in); +} + +int snapshot_get_limit(librados::IoCtx *ioctx, const std::string &oid, + uint64_t *limit) +{ + bufferlist in, out; + int r = ioctx->exec(oid, "rbd", "snapshot_get_limit", in, out); + + if (r < 0) { + return r; + } + + try { + auto iter = out.cbegin(); + decode(*limit, iter); + } catch (const buffer::error &err) { + return -EBADMSG; + } + + return 0; +} + +void snapshot_set_limit(librados::ObjectWriteOperation *op, uint64_t limit) +{ + bufferlist in; + encode(limit, in); + op->exec("rbd", "snapshot_set_limit", in); +} + +void get_stripe_unit_count_start(librados::ObjectReadOperation *op) { + bufferlist empty_bl; + op->exec("rbd", "get_stripe_unit_count", empty_bl); +} + +int get_stripe_unit_count_finish(bufferlist::const_iterator *it, + uint64_t *stripe_unit, + uint64_t *stripe_count) { + ceph_assert(stripe_unit); + ceph_assert(stripe_count); + + try { + decode(*stripe_unit, *it); + decode(*stripe_count, *it); + } catch (const buffer::error &err) { + return -EBADMSG; + } + return 0; +} + +int get_stripe_unit_count(librados::IoCtx *ioctx, const std::string &oid, + uint64_t *stripe_unit, uint64_t *stripe_count) +{ + librados::ObjectReadOperation op; + get_stripe_unit_count_start(&op); + + bufferlist out_bl; + int r = ioctx->operate(oid, &op, &out_bl); + if (r < 0) { + return r; + } + + auto it = out_bl.cbegin(); + return get_stripe_unit_count_finish(&it, stripe_unit, stripe_count); +} + +void set_stripe_unit_count(librados::ObjectWriteOperation *op, + uint64_t stripe_unit, uint64_t stripe_count) +{ + bufferlist bl; + encode(stripe_unit, bl); + encode(stripe_count, bl); + + op->exec("rbd", "set_stripe_unit_count", bl); +} + +int set_stripe_unit_count(librados::IoCtx *ioctx, const std::string &oid, + uint64_t stripe_unit, uint64_t stripe_count) +{ + librados::ObjectWriteOperation op; + set_stripe_unit_count(&op, stripe_unit, stripe_count); + + return ioctx->operate(oid, &op); +} + +void get_create_timestamp_start(librados::ObjectReadOperation *op) { + bufferlist empty_bl; + op->exec("rbd", "get_create_timestamp", empty_bl); +} + +int get_create_timestamp_finish(bufferlist::const_iterator *it, + utime_t *timestamp) { + ceph_assert(timestamp); + + try { + decode(*timestamp, *it); + } catch (const buffer::error &err) { + return -EBADMSG; + } + return 0; +} + +int get_create_timestamp(librados::IoCtx *ioctx, const std::string &oid, + utime_t *timestamp) +{ + librados::ObjectReadOperation op; + get_create_timestamp_start(&op); + + bufferlist out_bl; + int r = ioctx->operate(oid, &op, &out_bl); + if (r < 0) { + return r; + } + + auto it = out_bl.cbegin(); + return get_create_timestamp_finish(&it, timestamp); +} + +void get_access_timestamp_start(librados::ObjectReadOperation *op) { + bufferlist empty_bl; + op->exec("rbd", "get_access_timestamp", empty_bl); +} + +int get_access_timestamp_finish(bufferlist::const_iterator *it, + utime_t *timestamp) { + ceph_assert(timestamp); + + try { + decode(*timestamp, *it); + } catch (const buffer::error &err) { + return -EBADMSG; + } + return 0; +} + +int get_access_timestamp(librados::IoCtx *ioctx, const std::string &oid, + utime_t *timestamp) +{ + librados::ObjectReadOperation op; + get_access_timestamp_start(&op); + + bufferlist out_bl; + int r = ioctx->operate(oid, &op, &out_bl); + if (r < 0) { + return r; + } + + auto it = out_bl.cbegin(); + return get_access_timestamp_finish(&it, timestamp); +} + +void set_access_timestamp(librados::ObjectWriteOperation *op) +{ + bufferlist empty_bl; + op->exec("rbd","set_access_timestamp",empty_bl); +} + +int set_access_timestamp(librados::IoCtx *ioctx, const std::string &oid) +{ + librados::ObjectWriteOperation op; + set_access_timestamp(&op); + return ioctx->operate(oid, &op); +} + +void get_modify_timestamp_start(librados::ObjectReadOperation *op) { + bufferlist empty_bl; + op->exec("rbd", "get_modify_timestamp", empty_bl); +} + +int get_modify_timestamp_finish(bufferlist::const_iterator *it, + utime_t *timestamp) { + ceph_assert(timestamp); + + try { + decode(*timestamp, *it); + } catch (const buffer::error &err) { + return -EBADMSG; + } + return 0; +} + +int get_modify_timestamp(librados::IoCtx *ioctx, const std::string &oid, + utime_t *timestamp) +{ + librados::ObjectReadOperation op; + get_modify_timestamp_start(&op); + + bufferlist out_bl; + int r = ioctx->operate(oid, &op, &out_bl); + if (r < 0) { + return r; + } + + auto it = out_bl.cbegin(); + return get_modify_timestamp_finish(&it, timestamp); +} + +void set_modify_timestamp(librados::ObjectWriteOperation *op) +{ + bufferlist empty_bl; + op->exec("rbd","set_modify_timestamp",empty_bl); +} + +int set_modify_timestamp(librados::IoCtx *ioctx, const std::string &oid) +{ + librados::ObjectWriteOperation op; + set_modify_timestamp(&op); + return ioctx->operate(oid, &op); +} + + +/************************ rbd_id object methods ************************/ + +void get_id_start(librados::ObjectReadOperation *op) { + bufferlist empty_bl; + op->exec("rbd", "get_id", empty_bl); +} + +int get_id_finish(bufferlist::const_iterator *it, std::string *id) { + try { + decode(*id, *it); + } catch (const buffer::error &err) { + return -EBADMSG; + } + return 0; +} + +int get_id(librados::IoCtx *ioctx, const std::string &oid, std::string *id) +{ + librados::ObjectReadOperation op; + get_id_start(&op); + + bufferlist out_bl; + int r = ioctx->operate(oid, &op, &out_bl); + if (r < 0) { + return r; + } + + auto it = out_bl.cbegin(); + return get_id_finish(&it, id); +} + +void set_id(librados::ObjectWriteOperation *op, const std::string &id) +{ + bufferlist bl; + encode(id, bl); + op->exec("rbd", "set_id", bl); +} + +int set_id(librados::IoCtx *ioctx, const std::string &oid, const std::string &id) +{ + librados::ObjectWriteOperation op; + set_id(&op, id); + + return ioctx->operate(oid, &op); +} + +/******************** rbd_directory object methods ********************/ + +void dir_get_id_start(librados::ObjectReadOperation *op, + const std::string &image_name) { + bufferlist bl; + encode(image_name, bl); + + op->exec("rbd", "dir_get_id", bl); +} + +int dir_get_id_finish(bufferlist::const_iterator *iter, std::string *image_id) { + try { + decode(*image_id, *iter); + } catch (const buffer::error &err) { + return -EBADMSG; + } + + return 0; +} + +int dir_get_id(librados::IoCtx *ioctx, const std::string &oid, + const std::string &name, std::string *id) { + librados::ObjectReadOperation op; + dir_get_id_start(&op, name); + + bufferlist out_bl; + int r = ioctx->operate(oid, &op, &out_bl); + if (r < 0) { + return r; + } + + auto iter = out_bl.cbegin(); + return dir_get_id_finish(&iter, id); +} + +void dir_get_name_start(librados::ObjectReadOperation *op, + const std::string &id) { + bufferlist in_bl; + encode(id, in_bl); + op->exec("rbd", "dir_get_name", in_bl); +} + +int dir_get_name_finish(bufferlist::const_iterator *it, std::string *name) { + try { + decode(*name, *it); + } catch (const buffer::error &err) { + return -EBADMSG; + } + return 0; +} + +int dir_get_name(librados::IoCtx *ioctx, const std::string &oid, + const std::string &id, std::string *name) { + librados::ObjectReadOperation op; + dir_get_name_start(&op, id); + + bufferlist out_bl; + int r = ioctx->operate(oid, &op, &out_bl); + if (r < 0) { + return r; + } + + auto it = out_bl.cbegin(); + return dir_get_name_finish(&it, name); +} + +void dir_list_start(librados::ObjectReadOperation *op, + const std::string &start, uint64_t max_return) +{ + bufferlist in_bl; + encode(start, in_bl); + encode(max_return, in_bl); + + op->exec("rbd", "dir_list", in_bl); +} + +int dir_list_finish(bufferlist::const_iterator *it, map<string, string> *images) +{ + try { + decode(*images, *it); + } catch (const buffer::error &err) { + return -EBADMSG; + } + return 0; +} + +int dir_list(librados::IoCtx *ioctx, const std::string &oid, + const std::string &start, uint64_t max_return, + map<string, string> *images) +{ + librados::ObjectReadOperation op; + dir_list_start(&op, start, max_return); + + bufferlist out_bl; + int r = ioctx->operate(oid, &op, &out_bl); + if (r < 0) { + return r; + } + + auto iter = out_bl.cbegin(); + return dir_list_finish(&iter, images); +} + +void dir_add_image(librados::ObjectWriteOperation *op, + const std::string &name, const std::string &id) +{ + bufferlist bl; + encode(name, bl); + encode(id, bl); + op->exec("rbd", "dir_add_image", bl); +} + +int dir_add_image(librados::IoCtx *ioctx, const std::string &oid, + const std::string &name, const std::string &id) +{ + librados::ObjectWriteOperation op; + dir_add_image(&op, name, id); + + return ioctx->operate(oid, &op); +} + +int dir_remove_image(librados::IoCtx *ioctx, const std::string &oid, + const std::string &name, const std::string &id) +{ + librados::ObjectWriteOperation op; + dir_remove_image(&op, name, id); + + return ioctx->operate(oid, &op); +} + +void dir_state_assert(librados::ObjectOperation *op, + cls::rbd::DirectoryState directory_state) +{ + bufferlist bl; + encode(directory_state, bl); + op->exec("rbd", "dir_state_assert", bl); +} + +int dir_state_assert(librados::IoCtx *ioctx, const std::string &oid, + cls::rbd::DirectoryState directory_state) +{ + librados::ObjectWriteOperation op; + dir_state_assert(&op, directory_state); + + return ioctx->operate(oid, &op); +} + +void dir_state_set(librados::ObjectWriteOperation *op, + cls::rbd::DirectoryState directory_state) +{ + bufferlist bl; + encode(directory_state, bl); + op->exec("rbd", "dir_state_set", bl); +} + +int dir_state_set(librados::IoCtx *ioctx, const std::string &oid, + cls::rbd::DirectoryState directory_state) +{ + librados::ObjectWriteOperation op; + dir_state_set(&op, directory_state); + + return ioctx->operate(oid, &op); +} + +void dir_remove_image(librados::ObjectWriteOperation *op, + const std::string &name, const std::string &id) +{ + bufferlist bl; + encode(name, bl); + encode(id, bl); + + op->exec("rbd", "dir_remove_image", bl); +} + +void dir_rename_image(librados::ObjectWriteOperation *op, + const std::string &src, const std::string &dest, + const std::string &id) +{ + bufferlist in; + encode(src, in); + encode(dest, in); + encode(id, in); + op->exec("rbd", "dir_rename_image", in); +} + +void object_map_load_start(librados::ObjectReadOperation *op) { + bufferlist in_bl; + op->exec("rbd", "object_map_load", in_bl); +} + +int object_map_load_finish(bufferlist::const_iterator *it, + ceph::BitVector<2> *object_map) { + try { + decode(*object_map, *it); + } catch (const buffer::error &err) { + return -EBADMSG; + } + return 0; +} + +int object_map_load(librados::IoCtx *ioctx, const std::string &oid, + ceph::BitVector<2> *object_map) +{ + librados::ObjectReadOperation op; + object_map_load_start(&op); + + bufferlist out_bl; + int r = ioctx->operate(oid, &op, &out_bl); + if (r < 0) { + return r; + } + + auto it = out_bl.cbegin(); + return object_map_load_finish(&it, object_map); +} + +void object_map_save(librados::ObjectWriteOperation *rados_op, + const ceph::BitVector<2> &object_map) +{ + ceph::BitVector<2> object_map_copy(object_map); + object_map_copy.set_crc_enabled(false); + + bufferlist in; + encode(object_map_copy, in); + rados_op->exec("rbd", "object_map_save", in); +} + +void object_map_resize(librados::ObjectWriteOperation *rados_op, + uint64_t object_count, uint8_t default_state) +{ + bufferlist in; + encode(object_count, in); + encode(default_state, in); + rados_op->exec("rbd", "object_map_resize", in); +} + +void object_map_update(librados::ObjectWriteOperation *rados_op, + uint64_t start_object_no, uint64_t end_object_no, + uint8_t new_object_state, + const boost::optional<uint8_t> ¤t_object_state) +{ + bufferlist in; + encode(start_object_no, in); + encode(end_object_no, in); + encode(new_object_state, in); + encode(current_object_state, in); + rados_op->exec("rbd", "object_map_update", in); +} + +void object_map_snap_add(librados::ObjectWriteOperation *rados_op) +{ + bufferlist in; + rados_op->exec("rbd", "object_map_snap_add", in); +} + +void object_map_snap_remove(librados::ObjectWriteOperation *rados_op, + const ceph::BitVector<2> &object_map) +{ + ceph::BitVector<2> object_map_copy(object_map); + object_map_copy.set_crc_enabled(false); + + bufferlist in; + encode(object_map_copy, in); + rados_op->exec("rbd", "object_map_snap_remove", in); +} + +void metadata_set(librados::ObjectWriteOperation *op, + const map<string, bufferlist> &data) +{ + bufferlist bl; + encode(data, bl); + + op->exec("rbd", "metadata_set", bl); +} + +int metadata_set(librados::IoCtx *ioctx, const std::string &oid, + const map<string, bufferlist> &data) +{ + librados::ObjectWriteOperation op; + metadata_set(&op, data); + + return ioctx->operate(oid, &op); +} + +void metadata_remove(librados::ObjectWriteOperation *op, + const std::string &key) +{ + bufferlist bl; + encode(key, bl); + + op->exec("rbd", "metadata_remove", bl); +} + +int metadata_remove(librados::IoCtx *ioctx, const std::string &oid, + const std::string &key) +{ + librados::ObjectWriteOperation op; + metadata_remove(&op, key); + + return ioctx->operate(oid, &op); +} + +int metadata_list(librados::IoCtx *ioctx, const std::string &oid, + const std::string &start, uint64_t max_return, + map<string, bufferlist> *pairs) +{ + librados::ObjectReadOperation op; + metadata_list_start(&op, start, max_return); + + bufferlist out_bl; + int r = ioctx->operate(oid, &op, &out_bl); + if (r < 0) { + return r; + } + + auto it = out_bl.cbegin(); + return metadata_list_finish(&it, pairs); +} + +void metadata_list_start(librados::ObjectReadOperation *op, + const std::string &start, uint64_t max_return) +{ + bufferlist in_bl; + encode(start, in_bl); + encode(max_return, in_bl); + op->exec("rbd", "metadata_list", in_bl); +} + +int metadata_list_finish(bufferlist::const_iterator *it, + std::map<std::string, bufferlist> *pairs) +{ + ceph_assert(pairs); + try { + decode(*pairs, *it); + } catch (const buffer::error &err) { + return -EBADMSG; + } + return 0; +} + +int metadata_get(librados::IoCtx *ioctx, const std::string &oid, + const std::string &key, string *s) +{ + ceph_assert(s); + bufferlist in, out; + encode(key, in); + int r = ioctx->exec(oid, "rbd", "metadata_get", in, out); + if (r < 0) + return r; + + auto iter = out.cbegin(); + try { + decode(*s, iter); + } catch (const buffer::error &err) { + return -EBADMSG; + } + + return 0; +} + +void child_attach(librados::ObjectWriteOperation *op, snapid_t snap_id, + const cls::rbd::ChildImageSpec& child_image) +{ + bufferlist bl; + encode(snap_id, bl); + encode(child_image, bl); + op->exec("rbd", "child_attach", bl); +} + +int child_attach(librados::IoCtx *ioctx, const std::string &oid, + snapid_t snap_id, + const cls::rbd::ChildImageSpec& child_image) +{ + librados::ObjectWriteOperation op; + child_attach(&op, snap_id, child_image); + + int r = ioctx->operate(oid, &op); + if (r < 0) { + return r; + } + return 0; +} + +void child_detach(librados::ObjectWriteOperation *op, snapid_t snap_id, + const cls::rbd::ChildImageSpec& child_image) +{ + bufferlist bl; + encode(snap_id, bl); + encode(child_image, bl); + op->exec("rbd", "child_detach", bl); +} + +int child_detach(librados::IoCtx *ioctx, const std::string &oid, + snapid_t snap_id, + const cls::rbd::ChildImageSpec& child_image) +{ + librados::ObjectWriteOperation op; + child_detach(&op, snap_id, child_image); + + int r = ioctx->operate(oid, &op); + if (r < 0) { + return r; + } + return 0; +} + +void children_list_start(librados::ObjectReadOperation *op, + snapid_t snap_id) +{ + bufferlist bl; + encode(snap_id, bl); + op->exec("rbd", "children_list", bl); +} + +int children_list_finish(bufferlist::const_iterator *it, + cls::rbd::ChildImageSpecs *child_images) +{ + child_images->clear(); + try { + decode(*child_images, *it); + } catch (const buffer::error &err) { + return -EBADMSG; + } + return 0; +} + +int children_list(librados::IoCtx *ioctx, const std::string &oid, + snapid_t snap_id, + cls::rbd::ChildImageSpecs *child_images) +{ + librados::ObjectReadOperation op; + children_list_start(&op, snap_id); + + bufferlist out_bl; + int r = ioctx->operate(oid, &op, &out_bl); + if (r < 0) { + return r; + } + + auto it = out_bl.cbegin(); + r = children_list_finish(&it, child_images); + if (r < 0) { + return r; + } + return 0; +} + +int migration_set(librados::IoCtx *ioctx, const std::string &oid, + const cls::rbd::MigrationSpec &migration_spec) { + librados::ObjectWriteOperation op; + migration_set(&op, migration_spec); + return ioctx->operate(oid, &op); +} + +void migration_set(librados::ObjectWriteOperation *op, + const cls::rbd::MigrationSpec &migration_spec) { + bufferlist bl; + encode(migration_spec, bl); + op->exec("rbd", "migration_set", bl); +} + +int migration_set_state(librados::IoCtx *ioctx, const std::string &oid, + cls::rbd::MigrationState state, + const std::string &description) { + librados::ObjectWriteOperation op; + migration_set_state(&op, state, description); + return ioctx->operate(oid, &op); +} + +void migration_set_state(librados::ObjectWriteOperation *op, + cls::rbd::MigrationState state, + const std::string &description) { + bufferlist bl; + encode(state, bl); + encode(description, bl); + op->exec("rbd", "migration_set_state", bl); +} + +void migration_get_start(librados::ObjectReadOperation *op) { + bufferlist bl; + op->exec("rbd", "migration_get", bl); +} + +int migration_get_finish(bufferlist::const_iterator *it, + cls::rbd::MigrationSpec *migration_spec) { + try { + decode(*migration_spec, *it); + } catch (const buffer::error &err) { + return -EBADMSG; + } + return 0; +} + +int migration_get(librados::IoCtx *ioctx, const std::string &oid, + cls::rbd::MigrationSpec *migration_spec) { + librados::ObjectReadOperation op; + migration_get_start(&op); + + bufferlist out_bl; + int r = ioctx->operate(oid, &op, &out_bl); + if (r < 0) { + return r; + } + + auto iter = out_bl.cbegin(); + r = migration_get_finish(&iter, migration_spec); + if (r < 0) { + return r; + } + return 0; +} + +int migration_remove(librados::IoCtx *ioctx, const std::string &oid) { + librados::ObjectWriteOperation op; + migration_remove(&op); + return ioctx->operate(oid, &op); +} + +void migration_remove(librados::ObjectWriteOperation *op) { + bufferlist bl; + op->exec("rbd", "migration_remove", bl); +} + +int assert_snapc_seq(librados::IoCtx *ioctx, const std::string &oid, + uint64_t snapc_seq, + cls::rbd::AssertSnapcSeqState state) { + librados::ObjectWriteOperation op; + assert_snapc_seq(&op, snapc_seq, state); + return ioctx->operate(oid, &op); +} + +void assert_snapc_seq(librados::ObjectWriteOperation *op, + uint64_t snapc_seq, + cls::rbd::AssertSnapcSeqState state) { + bufferlist bl; + encode(snapc_seq, bl); + encode(state, bl); + op->exec("rbd", "assert_snapc_seq", bl); +} + +void mirror_uuid_get_start(librados::ObjectReadOperation *op) { + bufferlist bl; + op->exec("rbd", "mirror_uuid_get", bl); +} + +int mirror_uuid_get_finish(bufferlist::const_iterator *it, + std::string *uuid) { + try { + decode(*uuid, *it); + } catch (const buffer::error &err) { + return -EBADMSG; + } + return 0; +} + +int mirror_uuid_get(librados::IoCtx *ioctx, std::string *uuid) { + librados::ObjectReadOperation op; + mirror_uuid_get_start(&op); + + bufferlist out_bl; + int r = ioctx->operate(RBD_MIRRORING, &op, &out_bl); + if (r < 0) { + return r; + } + + auto it = out_bl.cbegin(); + r = mirror_uuid_get_finish(&it, uuid); + if (r < 0) { + return r; + } + return 0; +} + +int mirror_uuid_set(librados::IoCtx *ioctx, const std::string &uuid) { + bufferlist in_bl; + encode(uuid, in_bl); + + bufferlist out_bl; + int r = ioctx->exec(RBD_MIRRORING, "rbd", "mirror_uuid_set", in_bl, + out_bl); + if (r < 0) { + return r; + } + return 0; +} + +void mirror_mode_get_start(librados::ObjectReadOperation *op) { + bufferlist bl; + op->exec("rbd", "mirror_mode_get", bl); +} + +int mirror_mode_get_finish(bufferlist::const_iterator *it, + cls::rbd::MirrorMode *mirror_mode) { + try { + uint32_t mirror_mode_decode; + decode(mirror_mode_decode, *it); + *mirror_mode = static_cast<cls::rbd::MirrorMode>(mirror_mode_decode); + } catch (const buffer::error &err) { + return -EBADMSG; + } + + return 0; +} + +int mirror_mode_get(librados::IoCtx *ioctx, + cls::rbd::MirrorMode *mirror_mode) { + librados::ObjectReadOperation op; + mirror_mode_get_start(&op); + + bufferlist out_bl; + int r = ioctx->operate(RBD_MIRRORING, &op, &out_bl); + if (r == -ENOENT) { + *mirror_mode = cls::rbd::MIRROR_MODE_DISABLED; + return 0; + } else if (r < 0) { + return r; + } + + auto it = out_bl.cbegin(); + r = mirror_mode_get_finish(&it, mirror_mode); + if (r < 0) { + return r; + } + return 0; +} + +int mirror_mode_set(librados::IoCtx *ioctx, + cls::rbd::MirrorMode mirror_mode) { + bufferlist in_bl; + encode(static_cast<uint32_t>(mirror_mode), in_bl); + + bufferlist out_bl; + int r = ioctx->exec(RBD_MIRRORING, "rbd", "mirror_mode_set", in_bl, + out_bl); + if (r < 0) { + return r; + } + return 0; +} + +int mirror_peer_list(librados::IoCtx *ioctx, + std::vector<cls::rbd::MirrorPeer> *peers) { + bufferlist in_bl; + bufferlist out_bl; + int r = ioctx->exec(RBD_MIRRORING, "rbd", "mirror_peer_list", in_bl, + out_bl); + if (r < 0) { + return r; + } + + peers->clear(); + try { + auto bl_it = out_bl.cbegin(); + decode(*peers, bl_it); + } catch (const buffer::error &err) { + return -EBADMSG; + } + return 0; +} + +int mirror_peer_add(librados::IoCtx *ioctx, const std::string &uuid, + const std::string &cluster_name, + const std::string &client_name) { + cls::rbd::MirrorPeer peer(uuid, cluster_name, client_name, -1); + bufferlist in_bl; + encode(peer, in_bl); + + bufferlist out_bl; + int r = ioctx->exec(RBD_MIRRORING, "rbd", "mirror_peer_add", in_bl, + out_bl); + if (r < 0) { + return r; + } + return 0; +} + +int mirror_peer_remove(librados::IoCtx *ioctx, + const std::string &uuid) { + bufferlist in_bl; + encode(uuid, in_bl); + + bufferlist out_bl; + int r = ioctx->exec(RBD_MIRRORING, "rbd", "mirror_peer_remove", in_bl, + out_bl); + if (r < 0) { + return r; + } + return 0; +} + +int mirror_peer_set_client(librados::IoCtx *ioctx, + const std::string &uuid, + const std::string &client_name) { + bufferlist in_bl; + encode(uuid, in_bl); + encode(client_name, in_bl); + + bufferlist out_bl; + int r = ioctx->exec(RBD_MIRRORING, "rbd", "mirror_peer_set_client", + in_bl, out_bl); + if (r < 0) { + return r; + } + return 0; +} + +int mirror_peer_set_cluster(librados::IoCtx *ioctx, + const std::string &uuid, + const std::string &cluster_name) { + bufferlist in_bl; + encode(uuid, in_bl); + encode(cluster_name, in_bl); + + bufferlist out_bl; + int r = ioctx->exec(RBD_MIRRORING, "rbd", "mirror_peer_set_cluster", + in_bl, out_bl); + if (r < 0) { + return r; + } + return 0; +} + +void mirror_image_list_start(librados::ObjectReadOperation *op, + const std::string &start, uint64_t max_return) +{ + bufferlist in_bl; + encode(start, in_bl); + encode(max_return, in_bl); + op->exec("rbd", "mirror_image_list", in_bl); +} + +int mirror_image_list_finish(bufferlist::const_iterator *it, + std::map<string, string> *mirror_image_ids) +{ + try { + decode(*mirror_image_ids, *it); + } catch (const buffer::error &err) { + return -EBADMSG; + } + return 0; +} + +int mirror_image_list(librados::IoCtx *ioctx, + const std::string &start, uint64_t max_return, + std::map<std::string, std::string> *mirror_image_ids) { + librados::ObjectReadOperation op; + mirror_image_list_start(&op, start, max_return); + + bufferlist out_bl; + int r = ioctx->operate(RBD_MIRRORING, &op, &out_bl); + if (r < 0) { + return r; + } + + auto bl_it = out_bl.cbegin(); + return mirror_image_list_finish(&bl_it, mirror_image_ids); +} + +void mirror_image_get_image_id_start(librados::ObjectReadOperation *op, + const std::string &global_image_id) { + bufferlist in_bl; + encode(global_image_id, in_bl); + op->exec( "rbd", "mirror_image_get_image_id", in_bl); +} + +int mirror_image_get_image_id_finish(bufferlist::const_iterator *it, + std::string *image_id) { + try { + decode(*image_id, *it); + } catch (const buffer::error &err) { + return -EBADMSG; + } + return 0; +} + +int mirror_image_get_image_id(librados::IoCtx *ioctx, + const std::string &global_image_id, + std::string *image_id) { + librados::ObjectReadOperation op; + mirror_image_get_image_id_start(&op, global_image_id); + + bufferlist out_bl; + int r = ioctx->operate(RBD_MIRRORING, &op, &out_bl); + if (r < 0) { + return r; + } + + auto it = out_bl.cbegin(); + return mirror_image_get_image_id_finish(&it, image_id); +} + +int mirror_image_get(librados::IoCtx *ioctx, const std::string &image_id, + cls::rbd::MirrorImage *mirror_image) { + librados::ObjectReadOperation op; + mirror_image_get_start(&op, image_id); + + bufferlist out_bl; + int r = ioctx->operate(RBD_MIRRORING, &op, &out_bl); + if (r < 0) { + return r; + } + + auto iter = out_bl.cbegin(); + r = mirror_image_get_finish(&iter, mirror_image); + if (r < 0) { + return r; + } + return 0; +} + +void mirror_image_get_start(librados::ObjectReadOperation *op, + const std::string &image_id) { + bufferlist in_bl; + encode(image_id, in_bl); + + op->exec("rbd", "mirror_image_get", in_bl); +} + +int mirror_image_get_finish(bufferlist::const_iterator *iter, + cls::rbd::MirrorImage *mirror_image) { + try { + decode(*mirror_image, *iter); + } catch (const buffer::error &err) { + return -EBADMSG; + } + return 0; +} + +void mirror_image_set(librados::ObjectWriteOperation *op, + const std::string &image_id, + const cls::rbd::MirrorImage &mirror_image) { + bufferlist bl; + encode(image_id, bl); + encode(mirror_image, bl); + + op->exec("rbd", "mirror_image_set", bl); +} + +int mirror_image_set(librados::IoCtx *ioctx, const std::string &image_id, + const cls::rbd::MirrorImage &mirror_image) { + librados::ObjectWriteOperation op; + mirror_image_set(&op, image_id, mirror_image); + + int r = ioctx->operate(RBD_MIRRORING, &op); + if (r < 0) { + return r; + } + return 0; +} + +void mirror_image_remove(librados::ObjectWriteOperation *op, + const std::string &image_id) { + bufferlist bl; + encode(image_id, bl); + + op->exec("rbd", "mirror_image_remove", bl); +} + +int mirror_image_remove(librados::IoCtx *ioctx, const std::string &image_id) { + librados::ObjectWriteOperation op; + mirror_image_remove(&op, image_id); + + int r = ioctx->operate(RBD_MIRRORING, &op); + if (r < 0) { + return r; + } + return 0; +} + +int mirror_image_status_set(librados::IoCtx *ioctx, + const std::string &global_image_id, + const cls::rbd::MirrorImageStatus &status) { + librados::ObjectWriteOperation op; + mirror_image_status_set(&op, global_image_id, status); + return ioctx->operate(RBD_MIRRORING, &op); +} + +void mirror_image_status_set(librados::ObjectWriteOperation *op, + const std::string &global_image_id, + const cls::rbd::MirrorImageStatus &status) { + bufferlist bl; + encode(global_image_id, bl); + encode(status, bl); + op->exec("rbd", "mirror_image_status_set", bl); +} + +int mirror_image_status_remove(librados::IoCtx *ioctx, + const std::string &global_image_id) { + librados::ObjectWriteOperation op; + mirror_image_status_remove(&op, global_image_id); + return ioctx->operate(RBD_MIRRORING, &op); +} + +void mirror_image_status_remove(librados::ObjectWriteOperation *op, + const std::string &global_image_id) { + bufferlist bl; + encode(global_image_id, bl); + op->exec("rbd", "mirror_image_status_remove", bl); +} + +int mirror_image_status_get(librados::IoCtx *ioctx, + const std::string &global_image_id, + cls::rbd::MirrorImageStatus *status) { + librados::ObjectReadOperation op; + mirror_image_status_get_start(&op, global_image_id); + + bufferlist out_bl; + int r = ioctx->operate(RBD_MIRRORING, &op, &out_bl); + if (r < 0) { + return r; + } + + auto iter = out_bl.cbegin(); + r = mirror_image_status_get_finish(&iter, status); + if (r < 0) { + return r; + } + return 0; +} + +void mirror_image_status_get_start(librados::ObjectReadOperation *op, + const std::string &global_image_id) { + bufferlist bl; + encode(global_image_id, bl); + op->exec("rbd", "mirror_image_status_get", bl); +} + +int mirror_image_status_get_finish(bufferlist::const_iterator *iter, + cls::rbd::MirrorImageStatus *status) { + try { + decode(*status, *iter); + } catch (const buffer::error &err) { + return -EBADMSG; + } + return 0; +} + +int mirror_image_status_list(librados::IoCtx *ioctx, + const std::string &start, uint64_t max_return, + std::map<std::string, cls::rbd::MirrorImage> *images, + std::map<std::string, cls::rbd::MirrorImageStatus> *statuses) { + librados::ObjectReadOperation op; + mirror_image_status_list_start(&op, start, max_return); + + bufferlist out_bl; + int r = ioctx->operate(RBD_MIRRORING, &op, &out_bl); + if (r < 0) { + return r; + } + + auto iter = out_bl.cbegin(); + r = mirror_image_status_list_finish(&iter, images, statuses); + if (r < 0) { + return r; + } + return 0; +} + +void mirror_image_status_list_start(librados::ObjectReadOperation *op, + const std::string &start, + uint64_t max_return) { + bufferlist bl; + encode(start, bl); + encode(max_return, bl); + op->exec("rbd", "mirror_image_status_list", bl); +} + +int mirror_image_status_list_finish(bufferlist::const_iterator *iter, + std::map<std::string, cls::rbd::MirrorImage> *images, + std::map<std::string, cls::rbd::MirrorImageStatus> *statuses) { + images->clear(); + statuses->clear(); + try { + decode(*images, *iter); + decode(*statuses, *iter); + } catch (const buffer::error &err) { + return -EBADMSG; + } + return 0; +} + +int mirror_image_status_get_summary(librados::IoCtx *ioctx, + std::map<cls::rbd::MirrorImageStatusState, int> *states) { + librados::ObjectReadOperation op; + mirror_image_status_get_summary_start(&op); + + bufferlist out_bl; + int r = ioctx->operate(RBD_MIRRORING, &op, &out_bl); + if (r < 0) { + return r; + } + + auto iter = out_bl.cbegin(); + r = mirror_image_status_get_summary_finish(&iter, states); + if (r < 0) { + return r; + } + return 0; +} + +void mirror_image_status_get_summary_start(librados::ObjectReadOperation *op) { + bufferlist bl; + op->exec("rbd", "mirror_image_status_get_summary", bl); +} + +int mirror_image_status_get_summary_finish(bufferlist::const_iterator *iter, + std::map<cls::rbd::MirrorImageStatusState, int> *states) { + try { + decode(*states, *iter); + } catch (const buffer::error &err) { + return -EBADMSG; + } + return 0; +} + +int mirror_image_status_remove_down(librados::IoCtx *ioctx) { + librados::ObjectWriteOperation op; + mirror_image_status_remove_down(&op); + return ioctx->operate(RBD_MIRRORING, &op); +} + +void mirror_image_status_remove_down(librados::ObjectWriteOperation *op) { + bufferlist bl; + op->exec("rbd", "mirror_image_status_remove_down", bl); +} + +int mirror_image_instance_get(librados::IoCtx *ioctx, + const std::string &global_image_id, + entity_inst_t *instance) { + librados::ObjectReadOperation op; + mirror_image_instance_get_start(&op, global_image_id); + + bufferlist out_bl; + int r = ioctx->operate(RBD_MIRRORING, &op, &out_bl); + if (r < 0) { + return r; + } + + auto iter = out_bl.cbegin(); + r = mirror_image_instance_get_finish(&iter, instance); + if (r < 0) { + return r; + } + return 0; +} + +void mirror_image_instance_get_start(librados::ObjectReadOperation *op, + const std::string &global_image_id) { + bufferlist bl; + encode(global_image_id, bl); + op->exec("rbd", "mirror_image_instance_get", bl); +} + +int mirror_image_instance_get_finish(bufferlist::const_iterator *iter, + entity_inst_t *instance) { + try { + decode(*instance, *iter); + } catch (const buffer::error &err) { + return -EBADMSG; + } + return 0; +} + +int mirror_image_instance_list( + librados::IoCtx *ioctx, const std::string &start, uint64_t max_return, + std::map<std::string, entity_inst_t> *instances) { + librados::ObjectReadOperation op; + mirror_image_instance_list_start(&op, start, max_return); + + bufferlist out_bl; + int r = ioctx->operate(RBD_MIRRORING, &op, &out_bl); + if (r < 0) { + return r; + } + + auto iter = out_bl.cbegin(); + r = mirror_image_instance_list_finish(&iter, instances); + if (r < 0) { + return r; + } + return 0; +} + +void mirror_image_instance_list_start(librados::ObjectReadOperation *op, + const std::string &start, + uint64_t max_return) { + bufferlist bl; + encode(start, bl); + encode(max_return, bl); + op->exec("rbd", "mirror_image_instance_list", bl); +} + +int mirror_image_instance_list_finish( + bufferlist::const_iterator *iter, + std::map<std::string, entity_inst_t> *instances) { + instances->clear(); + try { + decode(*instances, *iter); + } catch (const buffer::error &err) { + return -EBADMSG; + } + return 0; +} + +void mirror_instances_list_start(librados::ObjectReadOperation *op) { + bufferlist bl; + op->exec("rbd", "mirror_instances_list", bl); +} + +int mirror_instances_list_finish(bufferlist::const_iterator *iter, + std::vector<std::string> *instance_ids) { + instance_ids->clear(); + try { + decode(*instance_ids, *iter); + } catch (const buffer::error &err) { + return -EBADMSG; + } + return 0; +} + +int mirror_instances_list(librados::IoCtx *ioctx, + std::vector<std::string> *instance_ids) { + librados::ObjectReadOperation op; + mirror_instances_list_start(&op); + + bufferlist out_bl; + int r = ioctx->operate(RBD_MIRROR_LEADER, &op, &out_bl); + if (r < 0) { + return r; + } + + auto iter = out_bl.cbegin(); + r = mirror_instances_list_finish(&iter, instance_ids); + if (r < 0) { + return r; + } + return 0; +} + +void mirror_instances_add(librados::ObjectWriteOperation *op, + const std::string &instance_id) { + bufferlist bl; + encode(instance_id, bl); + op->exec("rbd", "mirror_instances_add", bl); +} + +int mirror_instances_add(librados::IoCtx *ioctx, + const std::string &instance_id) { + librados::ObjectWriteOperation op; + mirror_instances_add(&op, instance_id); + return ioctx->operate(RBD_MIRROR_LEADER, &op); +} + +void mirror_instances_remove(librados::ObjectWriteOperation *op, + const std::string &instance_id) { + bufferlist bl; + encode(instance_id, bl); + op->exec("rbd", "mirror_instances_remove", bl); +} + +int mirror_instances_remove(librados::IoCtx *ioctx, + const std::string &instance_id) { + librados::ObjectWriteOperation op; + mirror_instances_remove(&op, instance_id); + return ioctx->operate(RBD_MIRROR_LEADER, &op); +} + +void mirror_image_map_list_start(librados::ObjectReadOperation *op, + const std::string &start_after, + uint64_t max_read) { + bufferlist bl; + encode(start_after, bl); + encode(max_read, bl); + + op->exec("rbd", "mirror_image_map_list", bl); +} + +int mirror_image_map_list_finish(bufferlist::const_iterator *iter, + std::map<std::string, cls::rbd::MirrorImageMap> *image_mapping) { + try { + decode(*image_mapping, *iter); + } catch (const buffer::error &err) { + return -EBADMSG; + } + return 0; +} + +int mirror_image_map_list( + librados::IoCtx *ioctx, const std::string &start_after, + uint64_t max_read, + std::map<std::string, cls::rbd::MirrorImageMap> *image_mapping) { + librados::ObjectReadOperation op; + mirror_image_map_list_start(&op, start_after, max_read); + + bufferlist out_bl; + int r = ioctx->operate(RBD_MIRRORING, &op, &out_bl); + if (r < 0) { + return r; + } + + auto iter = out_bl.cbegin(); + return mirror_image_map_list_finish(&iter, image_mapping); +} + +void mirror_image_map_update(librados::ObjectWriteOperation *op, + const std::string &global_image_id, + const cls::rbd::MirrorImageMap &image_map) { + bufferlist bl; + encode(global_image_id, bl); + encode(image_map, bl); + + op->exec("rbd", "mirror_image_map_update", bl); +} + +void mirror_image_map_remove(librados::ObjectWriteOperation *op, + const std::string &global_image_id) { + bufferlist bl; + encode(global_image_id, bl); + + op->exec("rbd", "mirror_image_map_remove", bl); +} + +// Groups functions +int group_dir_list(librados::IoCtx *ioctx, const std::string &oid, + const std::string &start, uint64_t max_return, + map<string, string> *cgs) +{ + bufferlist in, out; + encode(start, in); + encode(max_return, in); + int r = ioctx->exec(oid, "rbd", "group_dir_list", in, out); + if (r < 0) + return r; + + auto iter = out.cbegin(); + try { + decode(*cgs, iter); + } catch (const buffer::error &err) { + return -EBADMSG; + } + + return 0; +} + +int group_dir_add(librados::IoCtx *ioctx, const std::string &oid, + const std::string &name, const std::string &id) +{ + bufferlist in, out; + encode(name, in); + encode(id, in); + return ioctx->exec(oid, "rbd", "group_dir_add", in, out); +} + +int group_dir_rename(librados::IoCtx *ioctx, const std::string &oid, + const std::string &src, const std::string &dest, + const std::string &id) +{ + bufferlist in, out; + encode(src, in); + encode(dest, in); + encode(id, in); + return ioctx->exec(oid, "rbd", "group_dir_rename", in, out); +} + +int group_dir_remove(librados::IoCtx *ioctx, const std::string &oid, + const std::string &name, const std::string &id) +{ + bufferlist in, out; + encode(name, in); + encode(id, in); + return ioctx->exec(oid, "rbd", "group_dir_remove", in, out); +} + +int group_image_remove(librados::IoCtx *ioctx, const std::string &oid, + const cls::rbd::GroupImageSpec &spec) +{ + bufferlist bl, bl2; + encode(spec, bl); + + return ioctx->exec(oid, "rbd", "group_image_remove", bl, bl2); +} + +int group_image_list(librados::IoCtx *ioctx, + const std::string &oid, + const cls::rbd::GroupImageSpec &start, + uint64_t max_return, + std::vector<cls::rbd::GroupImageStatus> *images) +{ + bufferlist bl, bl2; + encode(start, bl); + encode(max_return, bl); + + int r = ioctx->exec(oid, "rbd", "group_image_list", bl, bl2); + if (r < 0) + return r; + + auto iter = bl2.cbegin(); + try { + decode(*images, iter); + } catch (const buffer::error &err) { + return -EBADMSG; + } + + return 0; +} + +int group_image_set(librados::IoCtx *ioctx, const std::string &oid, + const cls::rbd::GroupImageStatus &st) +{ + bufferlist bl, bl2; + encode(st, bl); + + return ioctx->exec(oid, "rbd", "group_image_set", bl, bl2); +} + +int image_group_add(librados::IoCtx *ioctx, const std::string &oid, + const cls::rbd::GroupSpec &group_spec) +{ + bufferlist bl, bl2; + encode(group_spec, bl); + + return ioctx->exec(oid, "rbd", "image_group_add", bl, bl2); +} + +int image_group_remove(librados::IoCtx *ioctx, const std::string &oid, + const cls::rbd::GroupSpec &group_spec) +{ + bufferlist bl, bl2; + encode(group_spec, bl); + + return ioctx->exec(oid, "rbd", "image_group_remove", bl, bl2); +} + +void image_group_get_start(librados::ObjectReadOperation *op) +{ + bufferlist in_bl; + op->exec("rbd", "image_group_get", in_bl); +} + +int image_group_get_finish(bufferlist::const_iterator *iter, + cls::rbd::GroupSpec *group_spec) +{ + try { + decode(*group_spec, *iter); + } catch (const buffer::error &err) { + return -EBADMSG; + } + return 0; +} + +int image_group_get(librados::IoCtx *ioctx, const std::string &oid, + cls::rbd::GroupSpec *group_spec) +{ + librados::ObjectReadOperation op; + image_group_get_start(&op); + + bufferlist out_bl; + int r = ioctx->operate(oid, &op, &out_bl); + if (r < 0) { + return r; + } + + auto iter = out_bl.cbegin(); + return image_group_get_finish(&iter, group_spec); +} + +int group_snap_set(librados::IoCtx *ioctx, const std::string &oid, + const cls::rbd::GroupSnapshot &snapshot) +{ + using ceph::encode; + bufferlist inbl, outbl; + encode(snapshot, inbl); + int r = ioctx->exec(oid, "rbd", "group_snap_set", inbl, outbl); + return r; +} + +int group_snap_remove(librados::IoCtx *ioctx, const std::string &oid, + const std::string &snap_id) +{ + using ceph::encode; + bufferlist inbl, outbl; + encode(snap_id, inbl); + return ioctx->exec(oid, "rbd", "group_snap_remove", inbl, outbl); +} + +int group_snap_get_by_id(librados::IoCtx *ioctx, const std::string &oid, + const std::string &snap_id, + cls::rbd::GroupSnapshot *snapshot) +{ + using ceph::encode; + using ceph::decode; + bufferlist inbl, outbl; + + encode(snap_id, inbl); + int r = ioctx->exec(oid, "rbd", "group_snap_get_by_id", inbl, outbl); + if (r < 0) { + return r; + } + + auto iter = outbl.cbegin(); + try { + decode(*snapshot, iter); + } catch (const buffer::error &err) { + return -EBADMSG; + } + + return 0; +} +int group_snap_list(librados::IoCtx *ioctx, const std::string &oid, + const cls::rbd::GroupSnapshot &start, + uint64_t max_return, + std::vector<cls::rbd::GroupSnapshot> *snapshots) +{ + using ceph::encode; + using ceph::decode; + bufferlist inbl, outbl; + encode(start, inbl); + encode(max_return, inbl); + + int r = ioctx->exec(oid, "rbd", "group_snap_list", inbl, outbl); + if (r < 0) { + return r; + } + auto iter = outbl.cbegin(); + try { + decode(*snapshots, iter); + } catch (const buffer::error &err) { + return -EBADMSG; + } + + return 0; +} + +// rbd_trash functions +void trash_add(librados::ObjectWriteOperation *op, + const std::string &id, + const cls::rbd::TrashImageSpec &trash_spec) +{ + bufferlist bl; + encode(id, bl); + encode(trash_spec, bl); + op->exec("rbd", "trash_add", bl); +} + +int trash_add(librados::IoCtx *ioctx, const std::string &id, + const cls::rbd::TrashImageSpec &trash_spec) +{ + librados::ObjectWriteOperation op; + trash_add(&op, id, trash_spec); + + return ioctx->operate(RBD_TRASH, &op); +} + +void trash_remove(librados::ObjectWriteOperation *op, + const std::string &id) +{ + bufferlist bl; + encode(id, bl); + op->exec("rbd", "trash_remove", bl); +} + +int trash_remove(librados::IoCtx *ioctx, const std::string &id) +{ + librados::ObjectWriteOperation op; + trash_remove(&op, id); + + return ioctx->operate(RBD_TRASH, &op); +} + +void trash_list_start(librados::ObjectReadOperation *op, + const std::string &start, uint64_t max_return) +{ + bufferlist bl; + encode(start, bl); + encode(max_return, bl); + op->exec("rbd", "trash_list", bl); +} + +int trash_list_finish(bufferlist::const_iterator *it, + map<string, cls::rbd::TrashImageSpec> *entries) +{ + ceph_assert(entries); + + try { + decode(*entries, *it); + } catch (const buffer::error &err) { + return -EBADMSG; + } + + return 0; +} + +int trash_list(librados::IoCtx *ioctx, + const std::string &start, uint64_t max_return, + map<string, cls::rbd::TrashImageSpec> *entries) +{ + librados::ObjectReadOperation op; + trash_list_start(&op, start, max_return); + + bufferlist out_bl; + int r = ioctx->operate(RBD_TRASH, &op, &out_bl); + if (r < 0) { + return r; + } + + auto iter = out_bl.cbegin(); + return trash_list_finish(&iter, entries); +} + +void trash_get_start(librados::ObjectReadOperation *op, + const std::string &id) +{ + bufferlist bl; + encode(id, bl); + op->exec("rbd", "trash_get", bl); +} + +int trash_get_finish(bufferlist::const_iterator *it, + cls::rbd::TrashImageSpec *trash_spec) { + ceph_assert(trash_spec); + try { + decode(*trash_spec, *it); + } catch (const buffer::error &err) { + return -EBADMSG; + } + + return 0; +} + +int trash_get(librados::IoCtx *ioctx, const std::string &id, + cls::rbd::TrashImageSpec *trash_spec) +{ + librados::ObjectReadOperation op; + trash_get_start(&op, id); + + bufferlist out_bl; + int r = ioctx->operate(RBD_TRASH, &op, &out_bl); + if (r < 0) { + return r; + } + + auto it = out_bl.cbegin(); + return trash_get_finish(&it, trash_spec); +} + +void trash_state_set(librados::ObjectWriteOperation *op, + const std::string &id, + const cls::rbd::TrashImageState &trash_state, + const cls::rbd::TrashImageState &expect_state) +{ + bufferlist bl; + encode(id, bl); + encode(trash_state, bl); + encode(expect_state, bl); + op->exec("rbd", "trash_state_set", bl); +} + +int trash_state_set(librados::IoCtx *ioctx, const std::string &id, + const cls::rbd::TrashImageState &trash_state, + const cls::rbd::TrashImageState &expect_state) +{ + librados::ObjectWriteOperation op; + trash_state_set(&op, id, trash_state, expect_state); + + return ioctx->operate(RBD_TRASH, &op); +} + +void namespace_add(librados::ObjectWriteOperation *op, + const std::string &name) +{ + bufferlist bl; + encode(name, bl); + op->exec("rbd", "namespace_add", bl); +} + +int namespace_add(librados::IoCtx *ioctx, const std::string &name) +{ + librados::ObjectWriteOperation op; + namespace_add(&op, name); + + return ioctx->operate(RBD_NAMESPACE, &op); +} + +void namespace_remove(librados::ObjectWriteOperation *op, + const std::string &name) +{ + bufferlist bl; + encode(name, bl); + op->exec("rbd", "namespace_remove", bl); +} + +int namespace_remove(librados::IoCtx *ioctx, const std::string &name) +{ + librados::ObjectWriteOperation op; + namespace_remove(&op, name); + + return ioctx->operate(RBD_NAMESPACE, &op); +} + +void namespace_list_start(librados::ObjectReadOperation *op, + const std::string &start, uint64_t max_return) +{ + bufferlist bl; + encode(start, bl); + encode(max_return, bl); + op->exec("rbd", "namespace_list", bl); +} + +int namespace_list_finish(bufferlist::const_iterator *it, + std::list<std::string> *entries) +{ + ceph_assert(entries); + + try { + decode(*entries, *it); + } catch (const buffer::error &err) { + return -EBADMSG; + } + + return 0; +} + +int namespace_list(librados::IoCtx *ioctx, + const std::string &start, uint64_t max_return, + std::list<std::string> *entries) +{ + librados::ObjectReadOperation op; + namespace_list_start(&op, start, max_return); + + bufferlist out_bl; + int r = ioctx->operate(RBD_NAMESPACE, &op, &out_bl); + if (r < 0) { + return r; + } + + auto iter = out_bl.cbegin(); + return namespace_list_finish(&iter, entries); +} + +void sparsify(librados::ObjectWriteOperation *op, size_t sparse_size, + bool remove_empty) +{ + bufferlist bl; + encode(sparse_size, bl); + encode(remove_empty, bl); + op->exec("rbd", "sparsify", bl); +} + +int sparsify(librados::IoCtx *ioctx, const std::string &oid, size_t sparse_size, + bool remove_empty) +{ + librados::ObjectWriteOperation op; + sparsify(&op, sparse_size, remove_empty); + + return ioctx->operate(oid, &op); +} + +} // namespace cls_client +} // namespace librbd diff --git a/src/cls/rbd/cls_rbd_client.h b/src/cls/rbd/cls_rbd_client.h new file mode 100644 index 00000000..27a64cc2 --- /dev/null +++ b/src/cls/rbd/cls_rbd_client.h @@ -0,0 +1,610 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef CEPH_LIBRBD_CLS_RBD_CLIENT_H +#define CEPH_LIBRBD_CLS_RBD_CLIENT_H + +#include "cls/lock/cls_lock_types.h" +#include "cls/rbd/cls_rbd_types.h" +#include "common/snap_types.h" +#include "include/types.h" +#include "include/rados/librados_fwd.hpp" + +class Context; +namespace ceph { template <uint8_t> class BitVector; } + +namespace librbd { +namespace cls_client { + +// low-level interface (mainly for testing) +void create_image(librados::ObjectWriteOperation *op, uint64_t size, + uint8_t order, uint64_t features, + const std::string &object_prefix, int64_t data_pool_id); +int create_image(librados::IoCtx *ioctx, const std::string &oid, + uint64_t size, uint8_t order, uint64_t features, + const std::string &object_prefix, int64_t data_pool_id); + +void get_features_start(librados::ObjectReadOperation *op, bool read_only); +int get_features_finish(bufferlist::const_iterator *it, uint64_t *features, + uint64_t *incompatible_features); +int get_features(librados::IoCtx *ioctx, const std::string &oid, + bool read_only, uint64_t *features, + uint64_t *incompatible_features); +void set_features(librados::ObjectWriteOperation *op, uint64_t features, + uint64_t mask); +int set_features(librados::IoCtx *ioctx, const std::string &oid, + uint64_t features, uint64_t mask); + +void get_object_prefix_start(librados::ObjectReadOperation *op); +int get_object_prefix_finish(bufferlist::const_iterator *it, + std::string *object_prefix); +int get_object_prefix(librados::IoCtx *ioctx, const std::string &oid, + std::string *object_prefix); + +void get_data_pool_start(librados::ObjectReadOperation *op); +int get_data_pool_finish(bufferlist::const_iterator *it, int64_t *data_pool_id); +int get_data_pool(librados::IoCtx *ioctx, const std::string &oid, + int64_t *data_pool_id); + +void get_size_start(librados::ObjectReadOperation *op, snapid_t snap_id); +int get_size_finish(bufferlist::const_iterator *it, uint64_t *size, + uint8_t *order); +int get_size(librados::IoCtx *ioctx, const std::string &oid, + snapid_t snap_id, uint64_t *size, uint8_t *order); +int set_size(librados::IoCtx *ioctx, const std::string &oid, + uint64_t size); +void set_size(librados::ObjectWriteOperation *op, uint64_t size); + +void get_flags_start(librados::ObjectReadOperation *op, snapid_t snap_id); +int get_flags_finish(bufferlist::const_iterator *it, uint64_t *flags); +int get_flags(librados::IoCtx *ioctx, const std::string &oid, + snapid_t snap_id, uint64_t *flags); + +void set_flags(librados::ObjectWriteOperation *op, snapid_t snap_id, + uint64_t flags, uint64_t mask); + +void op_features_get_start(librados::ObjectReadOperation *op); +int op_features_get_finish(bufferlist::const_iterator *it, + uint64_t *op_features); +int op_features_get(librados::IoCtx *ioctx, const std::string &oid, + uint64_t *op_features); +void op_features_set(librados::ObjectWriteOperation *op, + uint64_t op_features, uint64_t mask); +int op_features_set(librados::IoCtx *ioctx, const std::string &oid, + uint64_t op_features, uint64_t mask); + +// NOTE: deprecate v1 parent APIs after mimic EOLed +void get_parent_start(librados::ObjectReadOperation *op, snapid_t snap_id); +int get_parent_finish(bufferlist::const_iterator *it, + cls::rbd::ParentImageSpec *pspec, + uint64_t *parent_overlap); +int get_parent(librados::IoCtx *ioctx, const std::string &oid, + snapid_t snap_id, cls::rbd::ParentImageSpec *pspec, + uint64_t *parent_overlap); +int set_parent(librados::IoCtx *ioctx, const std::string &oid, + const cls::rbd::ParentImageSpec &pspec, uint64_t parent_overlap); +void set_parent(librados::ObjectWriteOperation *op, + const cls::rbd::ParentImageSpec &pspec, + uint64_t parent_overlap); +int remove_parent(librados::IoCtx *ioctx, const std::string &oid); +void remove_parent(librados::ObjectWriteOperation *op); + +// v2 parent APIs +void parent_get_start(librados::ObjectReadOperation* op); +int parent_get_finish(bufferlist::const_iterator* it, + cls::rbd::ParentImageSpec* parent_image_spec); +int parent_get(librados::IoCtx* ioctx, const std::string &oid, + cls::rbd::ParentImageSpec* parent_image_spec); + +void parent_overlap_get_start(librados::ObjectReadOperation* op, + snapid_t snap_id); +int parent_overlap_get_finish(bufferlist::const_iterator* it, + std::optional<uint64_t>* parent_overlap); +int parent_overlap_get(librados::IoCtx* ioctx, const std::string &oid, + snapid_t snap_id, + std::optional<uint64_t>* parent_overlap); + +void parent_attach(librados::ObjectWriteOperation* op, + const cls::rbd::ParentImageSpec& parent_image_spec, + uint64_t parent_overlap, bool reattach); +int parent_attach(librados::IoCtx *ioctx, const std::string &oid, + const cls::rbd::ParentImageSpec& parent_image_spec, + uint64_t parent_overlap, bool reattach); + +void parent_detach(librados::ObjectWriteOperation* op); +int parent_detach(librados::IoCtx *ioctx, const std::string &oid); + +int add_child(librados::IoCtx *ioctx, const std::string &oid, + const cls::rbd::ParentImageSpec &pspec, + const std::string &c_imageid); +void add_child(librados::ObjectWriteOperation *op, + const cls::rbd::ParentImageSpec& pspec, + const std::string &c_imageid); +void remove_child(librados::ObjectWriteOperation *op, + const cls::rbd::ParentImageSpec &pspec, + const std::string &c_imageid); +int remove_child(librados::IoCtx *ioctx, const std::string &oid, + const cls::rbd::ParentImageSpec &pspec, + const std::string &c_imageid); +void get_children_start(librados::ObjectReadOperation *op, + const cls::rbd::ParentImageSpec &pspec); +int get_children_finish(bufferlist::const_iterator *it, + std::set<string> *children); +int get_children(librados::IoCtx *ioctx, const std::string &oid, + const cls::rbd::ParentImageSpec& pspec, set<string>& children); + +void snapshot_get_start(librados::ObjectReadOperation* op, + snapid_t snap_id); +int snapshot_get_finish(bufferlist::const_iterator* it, + cls::rbd::SnapshotInfo* snap_info); +int snapshot_get(librados::IoCtx *ioctx, const std::string &oid, + snapid_t snap_id, cls::rbd::SnapshotInfo* snap_info); + +void snapshot_add(librados::ObjectWriteOperation *op, snapid_t snap_id, + const std::string &snap_name, + const cls::rbd::SnapshotNamespace &snap_namespace); +void snapshot_remove(librados::ObjectWriteOperation *op, snapid_t snap_id); +void snapshot_rename(librados::ObjectWriteOperation *op, + snapid_t src_snap_id, + const std::string &dst_name); +void snapshot_trash_add(librados::ObjectWriteOperation *op, + snapid_t snap_id); + +void get_snapcontext_start(librados::ObjectReadOperation *op); +int get_snapcontext_finish(bufferlist::const_iterator *it, + ::SnapContext *snapc); +int get_snapcontext(librados::IoCtx *ioctx, const std::string &oid, + ::SnapContext *snapc); + +/// NOTE: remove after Luminous is retired +void get_snapshot_name_start(librados::ObjectReadOperation *op, + snapid_t snap_id); +int get_snapshot_name_finish(bufferlist::const_iterator *it, + std::string *name); +int get_snapshot_name(librados::IoCtx *ioctx, const std::string &oid, + snapid_t snap_id, std::string *name); + +/// NOTE: remove after Luminous is retired +void get_snapshot_timestamp_start(librados::ObjectReadOperation *op, + snapid_t snap_id); +int get_snapshot_timestamp_finish(bufferlist::const_iterator *it, + utime_t *timestamp); +int get_snapshot_timestamp(librados::IoCtx *ioctx, const std::string &oid, + snapid_t snap_id, utime_t *timestamp); + +void get_all_features_start(librados::ObjectReadOperation *op); +int get_all_features_finish(bufferlist::const_iterator *it, + uint64_t *all_features); +int get_all_features(librados::IoCtx *ioctx, const std::string &oid, + uint64_t *all_features); + +/// NOTE: remove protection after clone v1 is retired +void get_protection_status_start(librados::ObjectReadOperation *op, + snapid_t snap_id); +int get_protection_status_finish(bufferlist::const_iterator *it, + uint8_t *protection_status); +int get_protection_status(librados::IoCtx *ioctx, const std::string &oid, + snapid_t snap_id, uint8_t *protection_status); + +int set_protection_status(librados::IoCtx *ioctx, const std::string &oid, + snapid_t snap_id, uint8_t protection_status); +void set_protection_status(librados::ObjectWriteOperation *op, + snapid_t snap_id, uint8_t protection_status); + +int snapshot_get_limit(librados::IoCtx *ioctx, const std::string &oid, + uint64_t *limit); +void snapshot_set_limit(librados::ObjectWriteOperation *op, + uint64_t limit); + +void get_stripe_unit_count_start(librados::ObjectReadOperation *op); +int get_stripe_unit_count_finish(bufferlist::const_iterator *it, + uint64_t *stripe_unit, + uint64_t *stripe_count); +int get_stripe_unit_count(librados::IoCtx *ioctx, const std::string &oid, + uint64_t *stripe_unit, uint64_t *stripe_count); + +void set_stripe_unit_count(librados::ObjectWriteOperation *op, + uint64_t stripe_unit, uint64_t stripe_count); +int set_stripe_unit_count(librados::IoCtx *ioctx, const std::string &oid, + uint64_t stripe_unit, uint64_t stripe_count); + +void get_create_timestamp_start(librados::ObjectReadOperation *op); +int get_create_timestamp_finish(bufferlist::const_iterator *it, + utime_t *timestamp); +int get_create_timestamp(librados::IoCtx *ioctx, const std::string &oid, + utime_t *timestamp); + +void get_access_timestamp_start(librados::ObjectReadOperation *op); +int get_access_timestamp_finish(bufferlist::const_iterator *it, + utime_t *timestamp); +int get_access_timestamp(librados::IoCtx *ioctx, const std::string &oid, + utime_t *timestamp); + +void set_access_timestamp(librados::ObjectWriteOperation *op); +int set_access_timestamp(librados::IoCtx *ioctx, const std::string &oid); + +void get_modify_timestamp_start(librados::ObjectReadOperation *op); +int get_modify_timestamp_finish(bufferlist::const_iterator *it, + utime_t *timestamp); +int get_modify_timestamp(librados::IoCtx *ioctx, const std::string &oid, + utime_t *timestamp); + +void set_modify_timestamp(librados::ObjectWriteOperation *op); +int set_modify_timestamp(librados::IoCtx *ioctx, const std::string &oid); + +int metadata_list(librados::IoCtx *ioctx, const std::string &oid, + const std::string &start, uint64_t max_return, + map<string, bufferlist> *pairs); +void metadata_list_start(librados::ObjectReadOperation *op, + const std::string &start, uint64_t max_return); +int metadata_list_finish(bufferlist::const_iterator *it, + std::map<std::string, bufferlist> *pairs); +void metadata_set(librados::ObjectWriteOperation *op, + const map<std::string, bufferlist> &data); +int metadata_set(librados::IoCtx *ioctx, const std::string &oid, + const map<std::string, bufferlist> &data); +void metadata_remove(librados::ObjectWriteOperation *op, + const std::string &key); +int metadata_remove(librados::IoCtx *ioctx, const std::string &oid, + const std::string &key); +int metadata_get(librados::IoCtx *ioctx, const std::string &oid, + const std::string &key, string *v); + +void child_attach(librados::ObjectWriteOperation *op, snapid_t snap_id, + const cls::rbd::ChildImageSpec& child_image); +int child_attach(librados::IoCtx *ioctx, const std::string &oid, + snapid_t snap_id, + const cls::rbd::ChildImageSpec& child_image); +void child_detach(librados::ObjectWriteOperation *op, snapid_t snap_id, + const cls::rbd::ChildImageSpec& child_image); +int child_detach(librados::IoCtx *ioctx, const std::string &oid, + snapid_t snap_id, + const cls::rbd::ChildImageSpec& child_image); +void children_list_start(librados::ObjectReadOperation *op, + snapid_t snap_id); +int children_list_finish(bufferlist::const_iterator *it, + cls::rbd::ChildImageSpecs *child_images); +int children_list(librados::IoCtx *ioctx, const std::string &oid, + snapid_t snap_id, + cls::rbd::ChildImageSpecs *child_images); +int migration_set(librados::IoCtx *ioctx, const std::string &oid, + const cls::rbd::MigrationSpec &migration_spec); +void migration_set(librados::ObjectWriteOperation *op, + const cls::rbd::MigrationSpec &migration_spec); +int migration_set_state(librados::IoCtx *ioctx, const std::string &oid, + cls::rbd::MigrationState state, + const std::string &description); +void migration_set_state(librados::ObjectWriteOperation *op, + cls::rbd::MigrationState state, + const std::string &description); +void migration_get_start(librados::ObjectReadOperation *op); +int migration_get_finish(bufferlist::const_iterator *it, + cls::rbd::MigrationSpec *migration_spec); +int migration_get(librados::IoCtx *ioctx, const std::string &oid, + cls::rbd::MigrationSpec *migration_spec); +int migration_remove(librados::IoCtx *ioctx, const std::string &oid); +void migration_remove(librados::ObjectWriteOperation *op); + +// operations on rbd_id objects +void get_id_start(librados::ObjectReadOperation *op); +int get_id_finish(bufferlist::const_iterator *it, std::string *id); +int get_id(librados::IoCtx *ioctx, const std::string &oid, std::string *id); + +void set_id(librados::ObjectWriteOperation *op, const std::string &id); +int set_id(librados::IoCtx *ioctx, const std::string &oid, const std::string &id); + +// operations on rbd_directory objects +int dir_get_id(librados::IoCtx *ioctx, const std::string &oid, + const std::string &name, std::string *id); +void dir_get_id_start(librados::ObjectReadOperation *op, + const std::string &image_name); +int dir_get_id_finish(bufferlist::const_iterator *iter, std::string *image_id); +void dir_get_name_start(librados::ObjectReadOperation *op, + const std::string &id); +int dir_get_name_finish(bufferlist::const_iterator *it, std::string *name); +int dir_get_name(librados::IoCtx *ioctx, const std::string &oid, + const std::string &id, std::string *name); +void dir_list_start(librados::ObjectReadOperation *op, + const std::string &start, uint64_t max_return); +int dir_list_finish(bufferlist::const_iterator *it, map<string, string> *images); +int dir_list(librados::IoCtx *ioctx, const std::string &oid, + const std::string &start, uint64_t max_return, + map<string, string> *images); +void dir_add_image(librados::ObjectWriteOperation *op, + const std::string &name, const std::string &id); +int dir_add_image(librados::IoCtx *ioctx, const std::string &oid, + const std::string &name, const std::string &id); +int dir_remove_image(librados::IoCtx *ioctx, const std::string &oid, + const std::string &name, const std::string &id); +void dir_remove_image(librados::ObjectWriteOperation *op, + const std::string &name, const std::string &id); +// atomic remove and add +void dir_rename_image(librados::ObjectWriteOperation *op, + const std::string &src, const std::string &dest, + const std::string &id); +void dir_state_assert(librados::ObjectOperation *op, + cls::rbd::DirectoryState directory_state); +int dir_state_assert(librados::IoCtx *ioctx, const std::string &oid, + cls::rbd::DirectoryState directory_state); +void dir_state_set(librados::ObjectWriteOperation *op, + cls::rbd::DirectoryState directory_state); +int dir_state_set(librados::IoCtx *ioctx, const std::string &oid, + cls::rbd::DirectoryState directory_state); + +// operations on the rbd_object_map.$image_id object +void object_map_load_start(librados::ObjectReadOperation *op); +int object_map_load_finish(bufferlist::const_iterator *it, + ceph::BitVector<2> *object_map); +int object_map_load(librados::IoCtx *ioctx, const std::string &oid, + ceph::BitVector<2> *object_map); +void object_map_save(librados::ObjectWriteOperation *rados_op, + const ceph::BitVector<2> &object_map); +void object_map_resize(librados::ObjectWriteOperation *rados_op, + uint64_t object_count, uint8_t default_state); +void object_map_update(librados::ObjectWriteOperation *rados_op, + uint64_t start_object_no, uint64_t end_object_no, + uint8_t new_object_state, + const boost::optional<uint8_t> ¤t_object_state); +void object_map_snap_add(librados::ObjectWriteOperation *rados_op); +void object_map_snap_remove(librados::ObjectWriteOperation *rados_op, + const ceph::BitVector<2> &object_map); + +// class operations on the old format, kept for +// backwards compatibility +void old_snapshot_add(librados::ObjectWriteOperation *rados_op, + snapid_t snap_id, const std::string &snap_name); +void old_snapshot_remove(librados::ObjectWriteOperation *rados_op, + const std::string &snap_name); +void old_snapshot_rename(librados::ObjectWriteOperation *rados_op, + snapid_t src_snap_id, const std::string &dst_name); + +void old_snapshot_list_start(librados::ObjectReadOperation *op); +int old_snapshot_list_finish(bufferlist::const_iterator *it, + std::vector<string> *names, + std::vector<uint64_t> *sizes, + ::SnapContext *snapc); +int old_snapshot_list(librados::IoCtx *ioctx, const std::string &oid, + std::vector<string> *names, + std::vector<uint64_t> *sizes, + ::SnapContext *snapc); + +// operations on the rbd_mirroring object +void mirror_uuid_get_start(librados::ObjectReadOperation *op); +int mirror_uuid_get_finish(bufferlist::const_iterator *it, + std::string *uuid); +int mirror_uuid_get(librados::IoCtx *ioctx, std::string *uuid); +int mirror_uuid_set(librados::IoCtx *ioctx, const std::string &uuid); +void mirror_mode_get_start(librados::ObjectReadOperation *op); +int mirror_mode_get_finish(bufferlist::const_iterator *it, + cls::rbd::MirrorMode *mirror_mode); +int mirror_mode_get(librados::IoCtx *ioctx, + cls::rbd::MirrorMode *mirror_mode); +int mirror_mode_set(librados::IoCtx *ioctx, + cls::rbd::MirrorMode mirror_mode); +int mirror_peer_list(librados::IoCtx *ioctx, + std::vector<cls::rbd::MirrorPeer> *peers); +int mirror_peer_add(librados::IoCtx *ioctx, const std::string &uuid, + const std::string &cluster_name, + const std::string &client_name); +int mirror_peer_remove(librados::IoCtx *ioctx, + const std::string &uuid); +int mirror_peer_set_client(librados::IoCtx *ioctx, + const std::string &uuid, + const std::string &client_name); +int mirror_peer_set_cluster(librados::IoCtx *ioctx, + const std::string &uuid, + const std::string &cluster_name); +void mirror_image_list_start(librados::ObjectReadOperation *op, + const std::string &start, uint64_t max_return); +int mirror_image_list_finish(bufferlist::const_iterator *it, + std::map<string, string> *mirror_image_ids); +int mirror_image_list(librados::IoCtx *ioctx, + const std::string &start, uint64_t max_return, + std::map<std::string, std::string> *mirror_image_ids); +void mirror_image_get_image_id_start(librados::ObjectReadOperation *op, + const std::string &global_image_id); +int mirror_image_get_image_id_finish(bufferlist::const_iterator *it, + std::string *image_id); +int mirror_image_get_image_id(librados::IoCtx *ioctx, + const std::string &global_image_id, + std::string *image_id); +int mirror_image_get(librados::IoCtx *ioctx, const std::string &image_id, + cls::rbd::MirrorImage *mirror_image); +void mirror_image_get_start(librados::ObjectReadOperation *op, + const std::string &image_id); +int mirror_image_get_finish(bufferlist::const_iterator *iter, + cls::rbd::MirrorImage *mirror_image); +void mirror_image_set(librados::ObjectWriteOperation *op, + const std::string &image_id, + const cls::rbd::MirrorImage &mirror_image); +int mirror_image_set(librados::IoCtx *ioctx, const std::string &image_id, + const cls::rbd::MirrorImage &mirror_image); +void mirror_image_remove(librados::ObjectWriteOperation *op, + const std::string &image_id); +int mirror_image_remove(librados::IoCtx *ioctx, + const std::string &image_id); +int mirror_image_status_set(librados::IoCtx *ioctx, + const std::string &global_image_id, + const cls::rbd::MirrorImageStatus &status); +void mirror_image_status_set(librados::ObjectWriteOperation *op, + const std::string &global_image_id, + const cls::rbd::MirrorImageStatus &status); +int mirror_image_status_remove(librados::IoCtx *ioctx, + const std::string &global_image_id); +void mirror_image_status_remove(librados::ObjectWriteOperation *op, + const std::string &global_image_id); +int mirror_image_status_get(librados::IoCtx *ioctx, + const std::string &global_image_id, + cls::rbd::MirrorImageStatus *status); +void mirror_image_status_get_start(librados::ObjectReadOperation *op, + const std::string &global_image_id); +int mirror_image_status_get_finish(bufferlist::const_iterator *iter, + cls::rbd::MirrorImageStatus *status); +int mirror_image_status_list(librados::IoCtx *ioctx, + const std::string &start, uint64_t max_return, + std::map<std::string, cls::rbd::MirrorImage> *images, + std::map<std::string, cls::rbd::MirrorImageStatus> *statuses); +void mirror_image_status_list_start(librados::ObjectReadOperation *op, + const std::string &start, + uint64_t max_return); +int mirror_image_status_list_finish(bufferlist::const_iterator *iter, + std::map<std::string, cls::rbd::MirrorImage> *images, + std::map<std::string, cls::rbd::MirrorImageStatus> *statuses); +int mirror_image_status_get_summary(librados::IoCtx *ioctx, + std::map<cls::rbd::MirrorImageStatusState, int> *states); +void mirror_image_status_get_summary_start(librados::ObjectReadOperation *op); +int mirror_image_status_get_summary_finish(bufferlist::const_iterator *iter, + std::map<cls::rbd::MirrorImageStatusState, int> *states); +int mirror_image_status_remove_down(librados::IoCtx *ioctx); +void mirror_image_status_remove_down(librados::ObjectWriteOperation *op); + +int mirror_image_instance_get(librados::IoCtx *ioctx, + const std::string &global_image_id, + entity_inst_t *instance); +void mirror_image_instance_get_start(librados::ObjectReadOperation *op, + const std::string &global_image_id); +int mirror_image_instance_get_finish(bufferlist::const_iterator *iter, + entity_inst_t *instance); +int mirror_image_instance_list(librados::IoCtx *ioctx, + const std::string &start, uint64_t max_return, + std::map<std::string, entity_inst_t> *instances); +void mirror_image_instance_list_start(librados::ObjectReadOperation *op, + const std::string &start, + uint64_t max_return); +int mirror_image_instance_list_finish(bufferlist::const_iterator *iter, + std::map<std::string, entity_inst_t> *instances); + +void mirror_instances_list_start(librados::ObjectReadOperation *op); +int mirror_instances_list_finish(bufferlist::const_iterator *iter, + std::vector<std::string> *instance_ids); +int mirror_instances_list(librados::IoCtx *ioctx, + std::vector<std::string> *instance_ids); +void mirror_instances_add(librados::ObjectWriteOperation *op, + const std::string &instance_id); +int mirror_instances_add(librados::IoCtx *ioctx, + const std::string &instance_id); +void mirror_instances_remove(librados::ObjectWriteOperation *op, + const std::string &instance_id); +int mirror_instances_remove(librados::IoCtx *ioctx, + const std::string &instance_id); + +// image mapping related routines +void mirror_image_map_list_start(librados::ObjectReadOperation *op, + const std::string &start_after, + uint64_t max_read); +int mirror_image_map_list_finish(bufferlist::const_iterator *iter, + std::map<std::string, cls::rbd::MirrorImageMap> *image_mapping); +int mirror_image_map_list(librados::IoCtx *ioctx, + const std::string &start_after, uint64_t max_read, + std::map<std::string, cls::rbd::MirrorImageMap> *image_mapping); +void mirror_image_map_update(librados::ObjectWriteOperation *op, + const std::string &global_image_id, + const cls::rbd::MirrorImageMap &image_map); +void mirror_image_map_remove(librados::ObjectWriteOperation *op, + const std::string &global_image_id); + +// Groups functions +int group_dir_list(librados::IoCtx *ioctx, const std::string &oid, + const std::string &start, uint64_t max_return, + map<string, string> *groups); +int group_dir_add(librados::IoCtx *ioctx, const std::string &oid, + const std::string &name, const std::string &id); +int group_dir_rename(librados::IoCtx *ioctx, const std::string &oid, + const std::string &src, const std::string &dest, + const std::string &id); +int group_dir_remove(librados::IoCtx *ioctx, const std::string &oid, + const std::string &name, const std::string &id); +int group_image_remove(librados::IoCtx *ioctx, const std::string &oid, + const cls::rbd::GroupImageSpec &spec); +int group_image_list(librados::IoCtx *ioctx, const std::string &oid, + const cls::rbd::GroupImageSpec &start, + uint64_t max_return, + std::vector<cls::rbd::GroupImageStatus> *images); +int group_image_set(librados::IoCtx *ioctx, const std::string &oid, + const cls::rbd::GroupImageStatus &st); +int image_group_add(librados::IoCtx *ioctx, const std::string &oid, + const cls::rbd::GroupSpec &group_spec); +int image_group_remove(librados::IoCtx *ioctx, const std::string &oid, + const cls::rbd::GroupSpec &group_spec); +void image_group_get_start(librados::ObjectReadOperation *op); +int image_group_get_finish(bufferlist::const_iterator *iter, + cls::rbd::GroupSpec *group_spec); +int image_group_get(librados::IoCtx *ioctx, const std::string &oid, + cls::rbd::GroupSpec *group_spec); +int group_snap_set(librados::IoCtx *ioctx, const std::string &oid, + const cls::rbd::GroupSnapshot &snapshot); +int group_snap_remove(librados::IoCtx *ioctx, const std::string &oid, + const std::string &snap_id); +int group_snap_get_by_id(librados::IoCtx *ioctx, const std::string &oid, + const std::string &snap_id, + cls::rbd::GroupSnapshot *snapshot); +int group_snap_list(librados::IoCtx *ioctx, const std::string &oid, + const cls::rbd::GroupSnapshot &start, + uint64_t max_return, + std::vector<cls::rbd::GroupSnapshot> *snapshots); + +// operations on rbd_trash object +void trash_add(librados::ObjectWriteOperation *op, + const std::string &id, + const cls::rbd::TrashImageSpec &trash_spec); +int trash_add(librados::IoCtx *ioctx, const std::string &id, + const cls::rbd::TrashImageSpec &trash_spec); +void trash_remove(librados::ObjectWriteOperation *op, + const std::string &id); +int trash_remove(librados::IoCtx *ioctx, const std::string &id); +void trash_list_start(librados::ObjectReadOperation *op, + const std::string &start, uint64_t max_return); +int trash_list_finish(bufferlist::const_iterator *it, + map<string, cls::rbd::TrashImageSpec> *entries); +int trash_list(librados::IoCtx *ioctx, + const std::string &start, uint64_t max_return, + map<string, cls::rbd::TrashImageSpec> *entries); +void trash_get_start(librados::ObjectReadOperation *op, + const std::string &id); +int trash_get_finish(bufferlist::const_iterator *it, + cls::rbd::TrashImageSpec *trash_spec); +int trash_get(librados::IoCtx *ioctx, const std::string &id, + cls::rbd::TrashImageSpec *trash_spec); +void trash_state_set(librados::ObjectWriteOperation *op, + const std::string &id, + const cls::rbd::TrashImageState &trash_state, + const cls::rbd::TrashImageState &expect_state); +int trash_state_set(librados::IoCtx *ioctx, const std::string &id, + const cls::rbd::TrashImageState &trash_state, + const cls::rbd::TrashImageState &expect_state); + +// operations on rbd_namespace object +void namespace_add(librados::ObjectWriteOperation *op, + const std::string &name); +int namespace_add(librados::IoCtx *ioctx, const std::string &name); +void namespace_remove(librados::ObjectWriteOperation *op, + const std::string &name); +int namespace_remove(librados::IoCtx *ioctx, const std::string &name); +void namespace_list_start(librados::ObjectReadOperation *op, + const std::string &start, uint64_t max_return); +int namespace_list_finish(bufferlist::const_iterator *it, + std::list<std::string> *entries); +int namespace_list(librados::IoCtx *ioctx, + const std::string &start, uint64_t max_return, + std::list<std::string> *entries); + +// operations on data objects +int assert_snapc_seq(librados::IoCtx *ioctx, const std::string &oid, + uint64_t snapc_seq, + cls::rbd::AssertSnapcSeqState state); +void assert_snapc_seq(librados::ObjectWriteOperation *op, + uint64_t snapc_seq, + cls::rbd::AssertSnapcSeqState state); + +int copyup(librados::IoCtx *ioctx, const std::string &oid, + bufferlist data); + +void sparsify(librados::ObjectWriteOperation *op, size_t sparse_size, + bool remove_empty); +int sparsify(librados::IoCtx *ioctx, const std::string &oid, size_t sparse_size, + bool remove_empty); + +} // namespace cls_client +} // namespace librbd + +#endif // CEPH_LIBRBD_CLS_RBD_CLIENT_H diff --git a/src/cls/rbd/cls_rbd_types.cc b/src/cls/rbd/cls_rbd_types.cc new file mode 100644 index 00000000..0d2c9c75 --- /dev/null +++ b/src/cls/rbd/cls_rbd_types.cc @@ -0,0 +1,909 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include <boost/variant.hpp> +#include "cls/rbd/cls_rbd_types.h" +#include "common/Formatter.h" + +namespace cls { +namespace rbd { + +void MirrorPeer::encode(bufferlist &bl) const { + ENCODE_START(1, 1, bl); + encode(uuid, bl); + encode(cluster_name, bl); + encode(client_name, bl); + encode(pool_id, bl); + ENCODE_FINISH(bl); +} + +void MirrorPeer::decode(bufferlist::const_iterator &it) { + DECODE_START(1, it); + decode(uuid, it); + decode(cluster_name, it); + decode(client_name, it); + decode(pool_id, it); + DECODE_FINISH(it); +} + +void MirrorPeer::dump(Formatter *f) const { + f->dump_string("uuid", uuid); + f->dump_string("cluster_name", cluster_name); + f->dump_string("client_name", client_name); + f->dump_int("pool_id", pool_id); +} + +void MirrorPeer::generate_test_instances(std::list<MirrorPeer*> &o) { + o.push_back(new MirrorPeer()); + o.push_back(new MirrorPeer("uuid-123", "cluster name", "client name", 123)); +} + +bool MirrorPeer::operator==(const MirrorPeer &rhs) const { + return (uuid == rhs.uuid && + cluster_name == rhs.cluster_name && + client_name == rhs.client_name && + pool_id == rhs.pool_id); +} + +std::ostream& operator<<(std::ostream& os, const MirrorMode& mirror_mode) { + switch (mirror_mode) { + case MIRROR_MODE_DISABLED: + os << "disabled"; + break; + case MIRROR_MODE_IMAGE: + os << "image"; + break; + case MIRROR_MODE_POOL: + os << "pool"; + break; + default: + os << "unknown (" << static_cast<uint32_t>(mirror_mode) << ")"; + break; + } + return os; +} + +std::ostream& operator<<(std::ostream& os, const MirrorPeer& peer) { + os << "[" + << "uuid=" << peer.uuid << ", " + << "cluster_name=" << peer.cluster_name << ", " + << "client_name=" << peer.client_name; + if (peer.pool_id != -1) { + os << ", pool_id=" << peer.pool_id; + } + os << "]"; + return os; +} + +void MirrorImage::encode(bufferlist &bl) const { + ENCODE_START(1, 1, bl); + encode(global_image_id, bl); + encode(static_cast<uint8_t>(state), bl); + ENCODE_FINISH(bl); +} + +void MirrorImage::decode(bufferlist::const_iterator &it) { + uint8_t int_state; + DECODE_START(1, it); + decode(global_image_id, it); + decode(int_state, it); + state = static_cast<MirrorImageState>(int_state); + DECODE_FINISH(it); +} + +void MirrorImage::dump(Formatter *f) const { + f->dump_string("global_image_id", global_image_id); + f->dump_int("state", state); +} + +void MirrorImage::generate_test_instances(std::list<MirrorImage*> &o) { + o.push_back(new MirrorImage()); + o.push_back(new MirrorImage("uuid-123", MIRROR_IMAGE_STATE_ENABLED)); + o.push_back(new MirrorImage("uuid-abc", MIRROR_IMAGE_STATE_DISABLING)); +} + +bool MirrorImage::operator==(const MirrorImage &rhs) const { + return global_image_id == rhs.global_image_id && state == rhs.state; +} + +bool MirrorImage::operator<(const MirrorImage &rhs) const { + return global_image_id < rhs.global_image_id || + (global_image_id == rhs.global_image_id && state < rhs.state); +} + +std::ostream& operator<<(std::ostream& os, const MirrorImageState& mirror_state) { + switch (mirror_state) { + case MIRROR_IMAGE_STATE_DISABLING: + os << "disabling"; + break; + case MIRROR_IMAGE_STATE_ENABLED: + os << "enabled"; + break; + default: + os << "unknown (" << static_cast<uint32_t>(mirror_state) << ")"; + break; + } + return os; +} + +std::ostream& operator<<(std::ostream& os, const MirrorImage& mirror_image) { + os << "[" + << "global_image_id=" << mirror_image.global_image_id << ", " + << "state=" << mirror_image.state << "]"; + return os; +} + +void MirrorImageStatus::encode(bufferlist &bl) const { + ENCODE_START(1, 1, bl); + encode(state, bl); + encode(description, bl); + encode(last_update, bl); + encode(up, bl); + ENCODE_FINISH(bl); +} + +void MirrorImageStatus::decode(bufferlist::const_iterator &it) { + DECODE_START(1, it); + decode(state, it); + decode(description, it); + decode(last_update, it); + decode(up, it); + DECODE_FINISH(it); +} + +void MirrorImageStatus::dump(Formatter *f) const { + f->dump_string("state", state_to_string()); + f->dump_string("description", description); + f->dump_stream("last_update") << last_update; +} + +std::string MirrorImageStatus::state_to_string() const { + std::stringstream ss; + ss << (up ? "up+" : "down+") << state; + return ss.str(); +} + +void MirrorImageStatus::generate_test_instances( + std::list<MirrorImageStatus*> &o) { + o.push_back(new MirrorImageStatus()); + o.push_back(new MirrorImageStatus(MIRROR_IMAGE_STATUS_STATE_REPLAYING)); + o.push_back(new MirrorImageStatus(MIRROR_IMAGE_STATUS_STATE_ERROR, "error")); +} + +bool MirrorImageStatus::operator==(const MirrorImageStatus &rhs) const { + return state == rhs.state && description == rhs.description && up == rhs.up; +} + +std::ostream& operator<<(std::ostream& os, const MirrorImageStatusState& state) { + switch (state) { + case MIRROR_IMAGE_STATUS_STATE_UNKNOWN: + os << "unknown"; + break; + case MIRROR_IMAGE_STATUS_STATE_ERROR: + os << "error"; + break; + case MIRROR_IMAGE_STATUS_STATE_SYNCING: + os << "syncing"; + break; + case MIRROR_IMAGE_STATUS_STATE_STARTING_REPLAY: + os << "starting_replay"; + break; + case MIRROR_IMAGE_STATUS_STATE_REPLAYING: + os << "replaying"; + break; + case MIRROR_IMAGE_STATUS_STATE_STOPPING_REPLAY: + os << "stopping_replay"; + break; + case MIRROR_IMAGE_STATUS_STATE_STOPPED: + os << "stopped"; + break; + default: + os << "unknown (" << static_cast<uint32_t>(state) << ")"; + break; + } + return os; +} + +std::ostream& operator<<(std::ostream& os, const MirrorImageStatus& status) { + os << "[" + << "state=" << status.state_to_string() << ", " + << "description=" << status.description << ", " + << "last_update=" << status.last_update << "]"; + return os; +} + +void ParentImageSpec::encode(bufferlist& bl) const { + ENCODE_START(1, 1, bl); + encode(pool_id, bl); + encode(pool_namespace, bl); + encode(image_id, bl); + encode(snap_id, bl); + ENCODE_FINISH(bl); +} + +void ParentImageSpec::decode(bufferlist::const_iterator& bl) { + DECODE_START(1, bl); + decode(pool_id, bl); + decode(pool_namespace, bl); + decode(image_id, bl); + decode(snap_id, bl); + DECODE_FINISH(bl); +} + +void ParentImageSpec::dump(Formatter *f) const { + f->dump_int("pool_id", pool_id); + f->dump_string("pool_namespace", pool_namespace); + f->dump_string("image_id", image_id); + f->dump_unsigned("snap_id", snap_id); +} + +void ParentImageSpec::generate_test_instances(std::list<ParentImageSpec*>& o) { + o.push_back(new ParentImageSpec{}); + o.push_back(new ParentImageSpec{1, "", "foo", 3}); + o.push_back(new ParentImageSpec{1, "ns", "foo", 3}); +} + +void ChildImageSpec::encode(bufferlist &bl) const { + ENCODE_START(2, 1, bl); + encode(pool_id, bl); + encode(image_id, bl); + encode(pool_namespace, bl); + ENCODE_FINISH(bl); +} + +void ChildImageSpec::decode(bufferlist::const_iterator &it) { + DECODE_START(2, it); + decode(pool_id, it); + decode(image_id, it); + if (struct_v >= 2) { + decode(pool_namespace, it); + } + DECODE_FINISH(it); +} + +void ChildImageSpec::dump(Formatter *f) const { + f->dump_int("pool_id", pool_id); + f->dump_string("pool_namespace", pool_namespace); + f->dump_string("image_id", image_id); +} + +void ChildImageSpec::generate_test_instances(std::list<ChildImageSpec*> &o) { + o.push_back(new ChildImageSpec()); + o.push_back(new ChildImageSpec(123, "", "abc")); + o.push_back(new ChildImageSpec(123, "ns", "abc")); +} + +void GroupImageSpec::encode(bufferlist &bl) const { + ENCODE_START(1, 1, bl); + encode(image_id, bl); + encode(pool_id, bl); + ENCODE_FINISH(bl); +} + +void GroupImageSpec::decode(bufferlist::const_iterator &it) { + DECODE_START(1, it); + decode(image_id, it); + decode(pool_id, it); + DECODE_FINISH(it); +} + +void GroupImageSpec::dump(Formatter *f) const { + f->dump_string("image_id", image_id); + f->dump_int("pool_id", pool_id); +} + +int GroupImageSpec::from_key(const std::string &image_key, + GroupImageSpec *spec) { + if (nullptr == spec) return -EINVAL; + int prefix_len = cls::rbd::RBD_GROUP_IMAGE_KEY_PREFIX.size(); + std::string data_string = image_key.substr(prefix_len, + image_key.size() - prefix_len); + size_t p = data_string.find("_"); + if (std::string::npos == p) { + return -EIO; + } + data_string[p] = ' '; + + istringstream iss(data_string); + uint64_t pool_id; + string image_id; + iss >> std::hex >> pool_id >> image_id; + + spec->image_id = image_id; + spec->pool_id = pool_id; + return 0; +} + +std::string GroupImageSpec::image_key() { + if (-1 == pool_id) + return ""; + else { + ostringstream oss; + oss << RBD_GROUP_IMAGE_KEY_PREFIX << std::setw(16) + << std::setfill('0') << std::hex << pool_id << "_" << image_id; + return oss.str(); + } +} + +void GroupImageSpec::generate_test_instances(std::list<GroupImageSpec*> &o) { + o.push_back(new GroupImageSpec("10152ae8944a", 0)); + o.push_back(new GroupImageSpec("1018643c9869", 3)); +} + +void GroupImageStatus::encode(bufferlist &bl) const { + ENCODE_START(1, 1, bl); + encode(spec, bl); + encode(state, bl); + ENCODE_FINISH(bl); +} + +void GroupImageStatus::decode(bufferlist::const_iterator &it) { + DECODE_START(1, it); + decode(spec, it); + decode(state, it); + DECODE_FINISH(it); +} + +std::string GroupImageStatus::state_to_string() const { + std::stringstream ss; + if (state == GROUP_IMAGE_LINK_STATE_INCOMPLETE) { + ss << "incomplete"; + } + if (state == GROUP_IMAGE_LINK_STATE_ATTACHED) { + ss << "attached"; + } + return ss.str(); +} + +void GroupImageStatus::dump(Formatter *f) const { + spec.dump(f); + f->dump_string("state", state_to_string()); +} + +void GroupImageStatus::generate_test_instances(std::list<GroupImageStatus*> &o) { + o.push_back(new GroupImageStatus(GroupImageSpec("10152ae8944a", 0), GROUP_IMAGE_LINK_STATE_ATTACHED)); + o.push_back(new GroupImageStatus(GroupImageSpec("1018643c9869", 3), GROUP_IMAGE_LINK_STATE_ATTACHED)); + o.push_back(new GroupImageStatus(GroupImageSpec("10152ae8944a", 0), GROUP_IMAGE_LINK_STATE_INCOMPLETE)); + o.push_back(new GroupImageStatus(GroupImageSpec("1018643c9869", 3), GROUP_IMAGE_LINK_STATE_INCOMPLETE)); +} + + +void GroupSpec::encode(bufferlist &bl) const { + ENCODE_START(1, 1, bl); + encode(pool_id, bl); + encode(group_id, bl); + ENCODE_FINISH(bl); +} + +void GroupSpec::decode(bufferlist::const_iterator &it) { + DECODE_START(1, it); + decode(pool_id, it); + decode(group_id, it); + DECODE_FINISH(it); +} + +void GroupSpec::dump(Formatter *f) const { + f->dump_string("group_id", group_id); + f->dump_int("pool_id", pool_id); +} + +bool GroupSpec::is_valid() const { + return (!group_id.empty()) && (pool_id != -1); +} + +void GroupSpec::generate_test_instances(std::list<GroupSpec *> &o) { + o.push_back(new GroupSpec("10152ae8944a", 0)); + o.push_back(new GroupSpec("1018643c9869", 3)); +} + +void GroupSnapshotNamespace::encode(bufferlist& bl) const { + using ceph::encode; + encode(group_pool, bl); + encode(group_id, bl); + encode(group_snapshot_id, bl); +} + +void GroupSnapshotNamespace::decode(bufferlist::const_iterator& it) { + using ceph::decode; + decode(group_pool, it); + decode(group_id, it); + decode(group_snapshot_id, it); +} + +void GroupSnapshotNamespace::dump(Formatter *f) const { + f->dump_int("group_pool", group_pool); + f->dump_string("group_id", group_id); + f->dump_string("group_snapshot_id", group_snapshot_id); +} + +void TrashSnapshotNamespace::encode(bufferlist& bl) const { + using ceph::encode; + encode(original_name, bl); + encode(static_cast<uint32_t>(original_snapshot_namespace_type), bl); +} + +void TrashSnapshotNamespace::decode(bufferlist::const_iterator& it) { + using ceph::decode; + decode(original_name, it); + uint32_t snap_type; + decode(snap_type, it); + original_snapshot_namespace_type = static_cast<SnapshotNamespaceType>( + snap_type); +} + +void TrashSnapshotNamespace::dump(Formatter *f) const { + f->dump_string("original_name", original_name); + f->dump_stream("original_snapshot_namespace") + << original_snapshot_namespace_type; +} + +class EncodeSnapshotNamespaceVisitor : public boost::static_visitor<void> { +public: + explicit EncodeSnapshotNamespaceVisitor(bufferlist &bl) : m_bl(bl) { + } + + template <typename T> + inline void operator()(const T& t) const { + using ceph::encode; + encode(static_cast<uint32_t>(T::SNAPSHOT_NAMESPACE_TYPE), m_bl); + t.encode(m_bl); + } + +private: + bufferlist &m_bl; +}; + +class DecodeSnapshotNamespaceVisitor : public boost::static_visitor<void> { +public: + DecodeSnapshotNamespaceVisitor(bufferlist::const_iterator &iter) + : m_iter(iter) { + } + + template <typename T> + inline void operator()(T& t) const { + t.decode(m_iter); + } +private: + bufferlist::const_iterator &m_iter; +}; + +class DumpSnapshotNamespaceVisitor : public boost::static_visitor<void> { +public: + explicit DumpSnapshotNamespaceVisitor(Formatter *formatter, const std::string &key) + : m_formatter(formatter), m_key(key) {} + + template <typename T> + inline void operator()(const T& t) const { + auto type = T::SNAPSHOT_NAMESPACE_TYPE; + m_formatter->dump_string(m_key.c_str(), stringify(type)); + t.dump(m_formatter); + } +private: + ceph::Formatter *m_formatter; + std::string m_key; +}; + +class GetTypeVisitor : public boost::static_visitor<SnapshotNamespaceType> { +public: + template <typename T> + inline SnapshotNamespaceType operator()(const T&) const { + return static_cast<SnapshotNamespaceType>(T::SNAPSHOT_NAMESPACE_TYPE); + } +}; + +SnapshotNamespaceType get_snap_namespace_type( + const SnapshotNamespace& snapshot_namespace) { + return static_cast<SnapshotNamespaceType>(boost::apply_visitor( + GetTypeVisitor(), snapshot_namespace)); +} + +void SnapshotInfo::encode(bufferlist& bl) const { + ENCODE_START(1, 1, bl); + encode(id, bl); + encode(snapshot_namespace, bl); + encode(name, bl); + encode(image_size, bl); + encode(timestamp, bl); + encode(child_count, bl); + ENCODE_FINISH(bl); +} + +void SnapshotInfo::decode(bufferlist::const_iterator& it) { + DECODE_START(1, it); + decode(id, it); + decode(snapshot_namespace, it); + decode(name, it); + decode(image_size, it); + decode(timestamp, it); + decode(child_count, it); + DECODE_FINISH(it); +} + +void SnapshotInfo::dump(Formatter *f) const { + f->dump_unsigned("id", id); + f->open_object_section("namespace"); + boost::apply_visitor(DumpSnapshotNamespaceVisitor(f, "type"), + snapshot_namespace); + f->close_section(); + f->dump_string("name", name); + f->dump_unsigned("image_size", image_size); + f->dump_stream("timestamp") << timestamp; +} + +void SnapshotInfo::generate_test_instances(std::list<SnapshotInfo*> &o) { + o.push_back(new SnapshotInfo(1ULL, UserSnapshotNamespace{}, "snap1", 123, + {123456, 0}, 12)); + o.push_back(new SnapshotInfo(2ULL, + GroupSnapshotNamespace{567, "group1", "snap1"}, + "snap1", 123, {123456, 0}, 987)); + o.push_back(new SnapshotInfo(3ULL, + TrashSnapshotNamespace{ + SNAPSHOT_NAMESPACE_TYPE_USER, "snap1"}, + "12345", 123, {123456, 0}, 429)); +} + +void SnapshotNamespace::encode(bufferlist& bl) const { + ENCODE_START(1, 1, bl); + boost::apply_visitor(EncodeSnapshotNamespaceVisitor(bl), *this); + ENCODE_FINISH(bl); +} + +void SnapshotNamespace::decode(bufferlist::const_iterator &p) +{ + DECODE_START(1, p); + uint32_t snap_type; + decode(snap_type, p); + switch (snap_type) { + case cls::rbd::SNAPSHOT_NAMESPACE_TYPE_USER: + *this = UserSnapshotNamespace(); + break; + case cls::rbd::SNAPSHOT_NAMESPACE_TYPE_GROUP: + *this = GroupSnapshotNamespace(); + break; + case cls::rbd::SNAPSHOT_NAMESPACE_TYPE_TRASH: + *this = TrashSnapshotNamespace(); + break; + default: + *this = UnknownSnapshotNamespace(); + break; + } + boost::apply_visitor(DecodeSnapshotNamespaceVisitor(p), *this); + DECODE_FINISH(p); +} + +void SnapshotNamespace::dump(Formatter *f) const { + boost::apply_visitor( + DumpSnapshotNamespaceVisitor(f, "snapshot_namespace_type"), *this); +} + +void SnapshotNamespace::generate_test_instances(std::list<SnapshotNamespace*> &o) { + o.push_back(new SnapshotNamespace(UserSnapshotNamespace())); + o.push_back(new SnapshotNamespace(GroupSnapshotNamespace(0, "10152ae8944a", + "2118643c9732"))); + o.push_back(new SnapshotNamespace(GroupSnapshotNamespace(5, "1018643c9869", + "33352be8933c"))); + o.push_back(new SnapshotNamespace(TrashSnapshotNamespace())); +} + +std::ostream& operator<<(std::ostream& os, const SnapshotNamespaceType& type) { + switch (type) { + case SNAPSHOT_NAMESPACE_TYPE_USER: + os << "user"; + break; + case SNAPSHOT_NAMESPACE_TYPE_GROUP: + os << "group"; + break; + case SNAPSHOT_NAMESPACE_TYPE_TRASH: + os << "trash"; + break; + default: + os << "unknown"; + break; + } + return os; +} + +std::ostream& operator<<(std::ostream& os, const UserSnapshotNamespace& ns) { + os << "[" << SNAPSHOT_NAMESPACE_TYPE_USER << "]"; + return os; +} + +std::ostream& operator<<(std::ostream& os, const GroupSnapshotNamespace& ns) { + os << "[" << SNAPSHOT_NAMESPACE_TYPE_GROUP << " " + << "group_pool=" << ns.group_pool << ", " + << "group_id=" << ns.group_id << ", " + << "group_snapshot_id=" << ns.group_snapshot_id << "]"; + return os; +} + +std::ostream& operator<<(std::ostream& os, const TrashSnapshotNamespace& ns) { + os << "[" << SNAPSHOT_NAMESPACE_TYPE_TRASH << " " + << "original_name=" << ns.original_name << ", " + << "original_snapshot_namespace=" << ns.original_snapshot_namespace_type + << "]"; + return os; +} + +std::ostream& operator<<(std::ostream& os, const UnknownSnapshotNamespace& ns) { + os << "[unknown]"; + return os; +} + +void ImageSnapshotSpec::encode(bufferlist& bl) const { + using ceph::encode; + ENCODE_START(1, 1, bl); + encode(pool, bl); + encode(image_id, bl); + encode(snap_id, bl); + ENCODE_FINISH(bl); +} + +void ImageSnapshotSpec::decode(bufferlist::const_iterator& it) { + using ceph::decode; + DECODE_START(1, it); + decode(pool, it); + decode(image_id, it); + decode(snap_id, it); + DECODE_FINISH(it); +} + +void ImageSnapshotSpec::dump(Formatter *f) const { + f->dump_int("pool", pool); + f->dump_string("image_id", image_id); + f->dump_int("snap_id", snap_id); +} + +void ImageSnapshotSpec::generate_test_instances(std::list<ImageSnapshotSpec *> &o) { + o.push_back(new ImageSnapshotSpec(0, "myimage", 2)); + o.push_back(new ImageSnapshotSpec(1, "testimage", 7)); +} + +void GroupSnapshot::encode(bufferlist& bl) const { + using ceph::encode; + ENCODE_START(1, 1, bl); + encode(id, bl); + encode(name, bl); + encode(state, bl); + encode(snaps, bl); + ENCODE_FINISH(bl); +} + +void GroupSnapshot::decode(bufferlist::const_iterator& it) { + using ceph::decode; + DECODE_START(1, it); + decode(id, it); + decode(name, it); + decode(state, it); + decode(snaps, it); + DECODE_FINISH(it); +} + +void GroupSnapshot::dump(Formatter *f) const { + f->dump_string("id", id); + f->dump_string("name", name); + f->dump_int("state", state); +} + +void GroupSnapshot::generate_test_instances(std::list<GroupSnapshot *> &o) { + o.push_back(new GroupSnapshot("10152ae8944a", "groupsnapshot1", GROUP_SNAPSHOT_STATE_INCOMPLETE)); + o.push_back(new GroupSnapshot("1018643c9869", "groupsnapshot2", GROUP_SNAPSHOT_STATE_COMPLETE)); +} +void TrashImageSpec::encode(bufferlist& bl) const { + ENCODE_START(2, 1, bl); + encode(source, bl); + encode(name, bl); + encode(deletion_time, bl); + encode(deferment_end_time, bl); + encode(state, bl); + ENCODE_FINISH(bl); +} + +void TrashImageSpec::decode(bufferlist::const_iterator &it) { + DECODE_START(2, it); + decode(source, it); + decode(name, it); + decode(deletion_time, it); + decode(deferment_end_time, it); + if (struct_v >= 2) { + decode(state, it); + } + DECODE_FINISH(it); +} + +void TrashImageSpec::dump(Formatter *f) const { + f->dump_stream("source") << source; + f->dump_string("name", name); + f->dump_unsigned("deletion_time", deletion_time); + f->dump_unsigned("deferment_end_time", deferment_end_time); +} + +void MirrorImageMap::encode(bufferlist &bl) const { + ENCODE_START(1, 1, bl); + encode(instance_id, bl); + encode(mapped_time, bl); + encode(data, bl); + ENCODE_FINISH(bl); +} + +void MirrorImageMap::decode(bufferlist::const_iterator &it) { + DECODE_START(1, it); + decode(instance_id, it); + decode(mapped_time, it); + decode(data, it); + DECODE_FINISH(it); +} + +void MirrorImageMap::dump(Formatter *f) const { + f->dump_string("instance_id", instance_id); + f->dump_stream("mapped_time") << mapped_time; + + std::stringstream data_ss; + data.hexdump(data_ss); + f->dump_string("data", data_ss.str()); +} + +void MirrorImageMap::generate_test_instances( + std::list<MirrorImageMap*> &o) { + bufferlist data; + data.append(std::string(128, '1')); + + o.push_back(new MirrorImageMap("uuid-123", utime_t(), data)); + o.push_back(new MirrorImageMap("uuid-abc", utime_t(), data)); +} + +bool MirrorImageMap::operator==(const MirrorImageMap &rhs) const { + return instance_id == rhs.instance_id && mapped_time == rhs.mapped_time && + data.contents_equal(rhs.data); +} + +bool MirrorImageMap::operator<(const MirrorImageMap &rhs) const { + return instance_id < rhs.instance_id || + (instance_id == rhs.instance_id && mapped_time < rhs.mapped_time); +} + +std::ostream& operator<<(std::ostream& os, + const MirrorImageMap &image_map) { + return os << "[" << "instance_id=" << image_map.instance_id << ", mapped_time=" + << image_map.mapped_time << "]"; +} + +std::ostream& operator<<(std::ostream& os, + const MigrationHeaderType& type) { + switch (type) { + case MIGRATION_HEADER_TYPE_SRC: + os << "source"; + break; + case MIGRATION_HEADER_TYPE_DST: + os << "destination"; + break; + default: + os << "unknown (" << static_cast<uint32_t>(type) << ")"; + break; + } + return os; +} + +std::ostream& operator<<(std::ostream& os, + const MigrationState& migration_state) { + switch (migration_state) { + case MIGRATION_STATE_ERROR: + os << "error"; + break; + case MIGRATION_STATE_PREPARING: + os << "preparing"; + break; + case MIGRATION_STATE_PREPARED: + os << "prepared"; + break; + case MIGRATION_STATE_EXECUTING: + os << "executing"; + break; + case MIGRATION_STATE_EXECUTED: + os << "executed"; + break; + case MIGRATION_STATE_ABORTING: + os << "aborting"; + break; + default: + os << "unknown (" << static_cast<uint32_t>(migration_state) << ")"; + break; + } + return os; +} + +void MigrationSpec::encode(bufferlist& bl) const { + ENCODE_START(1, 1, bl); + encode(header_type, bl); + encode(pool_id, bl); + encode(pool_namespace, bl); + encode(image_name, bl); + encode(image_id, bl); + encode(snap_seqs, bl); + encode(overlap, bl); + encode(flatten, bl); + encode(mirroring, bl); + encode(state, bl); + encode(state_description, bl); + ENCODE_FINISH(bl); +} + +void MigrationSpec::decode(bufferlist::const_iterator& bl) { + DECODE_START(1, bl); + decode(header_type, bl); + decode(pool_id, bl); + decode(pool_namespace, bl); + decode(image_name, bl); + decode(image_id, bl); + decode(snap_seqs, bl); + decode(overlap, bl); + decode(flatten, bl); + decode(mirroring, bl); + decode(state, bl); + decode(state_description, bl); + DECODE_FINISH(bl); +} + +std::ostream& operator<<(std::ostream& os, + const std::map<uint64_t, uint64_t>& snap_seqs) { + os << "{"; + size_t count = 0; + for (auto &it : snap_seqs) { + os << (count++ > 0 ? ", " : "") << "(" << it.first << ", " << it.second + << ")"; + } + os << "}"; + return os; +} + +void MigrationSpec::dump(Formatter *f) const { + f->dump_stream("header_type") << header_type; + f->dump_int("pool_id", pool_id); + f->dump_string("pool_namespace", pool_namespace); + f->dump_string("image_name", image_name); + f->dump_string("image_id", image_id); + f->dump_stream("snap_seqs") << snap_seqs; + f->dump_unsigned("overlap", overlap); + f->dump_bool("mirroring", mirroring); +} + +void MigrationSpec::generate_test_instances(std::list<MigrationSpec*> &o) { + o.push_back(new MigrationSpec()); + o.push_back(new MigrationSpec(MIGRATION_HEADER_TYPE_SRC, 1, "ns", + "image_name", "image_id", {{1, 2}}, 123, true, + true, MIGRATION_STATE_PREPARED, "description")); +} + +std::ostream& operator<<(std::ostream& os, + const MigrationSpec& migration_spec) { + os << "[" + << "header_type=" << migration_spec.header_type << ", " + << "pool_id=" << migration_spec.pool_id << ", " + << "pool_namespace=" << migration_spec.pool_namespace << ", " + << "image_name=" << migration_spec.image_name << ", " + << "image_id=" << migration_spec.image_id << ", " + << "snap_seqs=" << migration_spec.snap_seqs << ", " + << "overlap=" << migration_spec.overlap << ", " + << "flatten=" << migration_spec.flatten << ", " + << "mirroring=" << migration_spec.mirroring << ", " + << "state=" << migration_spec.state << ", " + << "state_description=" << migration_spec.state_description << "]"; + return os; +} + +std::ostream& operator<<(std::ostream& os, const AssertSnapcSeqState& state) { + switch (state) { + case ASSERT_SNAPC_SEQ_GT_SNAPSET_SEQ: + os << "gt"; + break; + case ASSERT_SNAPC_SEQ_LE_SNAPSET_SEQ: + os << "le"; + break; + default: + os << "unknown (" << static_cast<uint32_t>(state) << ")"; + break; + } + return os; +} + +} // namespace rbd +} // namespace cls diff --git a/src/cls/rbd/cls_rbd_types.h b/src/cls/rbd/cls_rbd_types.h new file mode 100644 index 00000000..073006b3 --- /dev/null +++ b/src/cls/rbd/cls_rbd_types.h @@ -0,0 +1,791 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef CEPH_CLS_RBD_TYPES_H +#define CEPH_CLS_RBD_TYPES_H + +#include <boost/variant.hpp> +#include "include/int_types.h" +#include "include/buffer.h" +#include "include/encoding.h" +#include "include/stringify.h" +#include "include/utime.h" +#include <iosfwd> +#include <string> +#include <set> + +#define RBD_GROUP_REF "rbd_group_ref" + +namespace ceph { class Formatter; } + +namespace cls { +namespace rbd { + +static const uint32_t MAX_OBJECT_MAP_OBJECT_COUNT = 256000000; +static const string RBD_GROUP_IMAGE_KEY_PREFIX = "image_"; + +enum DirectoryState { + DIRECTORY_STATE_READY = 0, + DIRECTORY_STATE_ADD_DISABLED = 1 +}; + +inline void encode(DirectoryState state, bufferlist& bl, + uint64_t features=0) +{ + ceph::encode(static_cast<uint8_t>(state), bl); +} + +inline void decode(DirectoryState &state, bufferlist::const_iterator& it) +{ + uint8_t int_state; + ceph::decode(int_state, it); + state = static_cast<DirectoryState>(int_state); +} + +enum MirrorMode { + MIRROR_MODE_DISABLED = 0, + MIRROR_MODE_IMAGE = 1, + MIRROR_MODE_POOL = 2 +}; + +enum GroupImageLinkState { + GROUP_IMAGE_LINK_STATE_ATTACHED, + GROUP_IMAGE_LINK_STATE_INCOMPLETE +}; + +inline void encode(const GroupImageLinkState &state, bufferlist& bl, + uint64_t features=0) +{ + using ceph::encode; + encode(static_cast<uint8_t>(state), bl); +} + +inline void decode(GroupImageLinkState &state, bufferlist::const_iterator& it) +{ + uint8_t int_state; + using ceph::decode; + decode(int_state, it); + state = static_cast<GroupImageLinkState>(int_state); +} + +struct MirrorPeer { + MirrorPeer() { + } + MirrorPeer(const std::string &uuid, const std::string &cluster_name, + const std::string &client_name, int64_t pool_id) + : uuid(uuid), cluster_name(cluster_name), client_name(client_name), + pool_id(pool_id) { + } + + std::string uuid; + std::string cluster_name; + std::string client_name; + int64_t pool_id = -1; + + inline bool is_valid() const { + return (!uuid.empty() && !cluster_name.empty() && !client_name.empty()); + } + + void encode(bufferlist &bl) const; + void decode(bufferlist::const_iterator &it); + void dump(Formatter *f) const; + + static void generate_test_instances(std::list<MirrorPeer*> &o); + + bool operator==(const MirrorPeer &rhs) const; +}; + +std::ostream& operator<<(std::ostream& os, const MirrorMode& mirror_mode); +std::ostream& operator<<(std::ostream& os, const MirrorPeer& peer); + +WRITE_CLASS_ENCODER(MirrorPeer); + +enum MirrorImageState { + MIRROR_IMAGE_STATE_DISABLING = 0, + MIRROR_IMAGE_STATE_ENABLED = 1, + MIRROR_IMAGE_STATE_DISABLED = 2, +}; + +struct MirrorImage { + MirrorImage() {} + MirrorImage(const std::string &global_image_id, MirrorImageState state) + : global_image_id(global_image_id), state(state) {} + + std::string global_image_id; + MirrorImageState state = MIRROR_IMAGE_STATE_DISABLING; + + void encode(bufferlist &bl) const; + void decode(bufferlist::const_iterator &it); + void dump(Formatter *f) const; + + static void generate_test_instances(std::list<MirrorImage*> &o); + + bool operator==(const MirrorImage &rhs) const; + bool operator<(const MirrorImage &rhs) const; +}; + +std::ostream& operator<<(std::ostream& os, const MirrorImageState& mirror_state); +std::ostream& operator<<(std::ostream& os, const MirrorImage& mirror_image); + +WRITE_CLASS_ENCODER(MirrorImage); + +enum MirrorImageStatusState { + MIRROR_IMAGE_STATUS_STATE_UNKNOWN = 0, + MIRROR_IMAGE_STATUS_STATE_ERROR = 1, + MIRROR_IMAGE_STATUS_STATE_SYNCING = 2, + MIRROR_IMAGE_STATUS_STATE_STARTING_REPLAY = 3, + MIRROR_IMAGE_STATUS_STATE_REPLAYING = 4, + MIRROR_IMAGE_STATUS_STATE_STOPPING_REPLAY = 5, + MIRROR_IMAGE_STATUS_STATE_STOPPED = 6, +}; + +inline void encode(const MirrorImageStatusState &state, bufferlist& bl, + uint64_t features=0) +{ + using ceph::encode; + encode(static_cast<uint8_t>(state), bl); +} + +inline void decode(MirrorImageStatusState &state, bufferlist::const_iterator& it) +{ + uint8_t int_state; + using ceph::decode; + decode(int_state, it); + state = static_cast<MirrorImageStatusState>(int_state); +} + +struct MirrorImageStatus { + MirrorImageStatus() {} + MirrorImageStatus(MirrorImageStatusState state, + const std::string &description = "") + : state(state), description(description) {} + + MirrorImageStatusState state = MIRROR_IMAGE_STATUS_STATE_UNKNOWN; + std::string description; + utime_t last_update; + bool up = false; + + void encode(bufferlist &bl) const; + void decode(bufferlist::const_iterator &it); + void dump(Formatter *f) const; + + std::string state_to_string() const; + + static void generate_test_instances(std::list<MirrorImageStatus*> &o); + + bool operator==(const MirrorImageStatus &rhs) const; +}; + +std::ostream& operator<<(std::ostream& os, const MirrorImageStatus& status); +std::ostream& operator<<(std::ostream& os, const MirrorImageStatusState& state); + +WRITE_CLASS_ENCODER(MirrorImageStatus); + +struct ParentImageSpec { + int64_t pool_id = -1; + std::string pool_namespace; + std::string image_id; + snapid_t snap_id = CEPH_NOSNAP; + + ParentImageSpec() { + } + ParentImageSpec(int64_t pool_id, const std::string& pool_namespace, + const std::string& image_id, snapid_t snap_id) + : pool_id(pool_id), pool_namespace(pool_namespace), image_id(image_id), + snap_id(snap_id) { + } + + bool exists() const { + return (pool_id >= 0 && !image_id.empty() && snap_id != CEPH_NOSNAP); + } + + bool operator==(const ParentImageSpec& rhs) const { + return ((pool_id == rhs.pool_id) && + (pool_namespace == rhs.pool_namespace) && + (image_id == rhs.image_id) && + (snap_id == rhs.snap_id)); + } + + bool operator!=(const ParentImageSpec& rhs) const { + return !(*this == rhs); + } + + void encode(bufferlist &bl) const; + void decode(bufferlist::const_iterator &it); + void dump(Formatter *f) const; + + static void generate_test_instances(std::list<ParentImageSpec*> &o); +}; + +WRITE_CLASS_ENCODER(ParentImageSpec); + +struct ChildImageSpec { + int64_t pool_id = -1; + std::string pool_namespace; + std::string image_id; + + ChildImageSpec() {} + ChildImageSpec(int64_t pool_id, const std::string& pool_namespace, + const std::string& image_id) + : pool_id(pool_id), pool_namespace(pool_namespace), image_id(image_id) { + } + + void encode(bufferlist &bl) const; + void decode(bufferlist::const_iterator &it); + void dump(Formatter *f) const; + + static void generate_test_instances(std::list<ChildImageSpec*> &o); + + inline bool operator==(const ChildImageSpec& rhs) const { + return (pool_id == rhs.pool_id && + pool_namespace == rhs.pool_namespace && + image_id == rhs.image_id); + } + inline bool operator<(const ChildImageSpec& rhs) const { + if (pool_id != rhs.pool_id) { + return pool_id < rhs.pool_id; + } + if (pool_namespace != rhs.pool_namespace) { + return pool_namespace < rhs.pool_namespace; + } + return image_id < rhs.image_id; + } +}; +WRITE_CLASS_ENCODER(ChildImageSpec); + +typedef std::set<ChildImageSpec> ChildImageSpecs; + +struct GroupImageSpec { + GroupImageSpec() {} + + GroupImageSpec(const std::string &image_id, int64_t pool_id) + : image_id(image_id), pool_id(pool_id) {} + + static int from_key(const std::string &image_key, GroupImageSpec *spec); + + std::string image_id; + int64_t pool_id = -1; + + void encode(bufferlist &bl) const; + void decode(bufferlist::const_iterator &it); + void dump(Formatter *f) const; + + static void generate_test_instances(std::list<GroupImageSpec*> &o); + + std::string image_key(); + +}; +WRITE_CLASS_ENCODER(GroupImageSpec); + +struct GroupImageStatus { + GroupImageStatus() {} + GroupImageStatus(const std::string &image_id, + int64_t pool_id, + GroupImageLinkState state) + : spec(image_id, pool_id), state(state) {} + + GroupImageStatus(GroupImageSpec spec, + GroupImageLinkState state) + : spec(spec), state(state) {} + + GroupImageSpec spec; + GroupImageLinkState state = GROUP_IMAGE_LINK_STATE_INCOMPLETE; + + void encode(bufferlist &bl) const; + void decode(bufferlist::const_iterator &it); + void dump(Formatter *f) const; + + static void generate_test_instances(std::list<GroupImageStatus*> &o); + + std::string state_to_string() const; +}; + +WRITE_CLASS_ENCODER(GroupImageStatus); + +struct GroupSpec { + GroupSpec() {} + GroupSpec(const std::string &group_id, int64_t pool_id) + : group_id(group_id), pool_id(pool_id) {} + + std::string group_id; + int64_t pool_id = -1; + + void encode(bufferlist &bl) const; + void decode(bufferlist::const_iterator &it); + void dump(Formatter *f) const; + bool is_valid() const; + + static void generate_test_instances(std::list<GroupSpec *> &o); +}; + +WRITE_CLASS_ENCODER(GroupSpec); + +enum SnapshotNamespaceType { + SNAPSHOT_NAMESPACE_TYPE_USER = 0, + SNAPSHOT_NAMESPACE_TYPE_GROUP = 1, + SNAPSHOT_NAMESPACE_TYPE_TRASH = 2 +}; + +struct UserSnapshotNamespace { + static const SnapshotNamespaceType SNAPSHOT_NAMESPACE_TYPE = + SNAPSHOT_NAMESPACE_TYPE_USER; + + UserSnapshotNamespace() {} + + void encode(bufferlist& bl) const {} + void decode(bufferlist::const_iterator& it) {} + + void dump(Formatter *f) const {} + + inline bool operator==(const UserSnapshotNamespace& usn) const { + return true; + } + + inline bool operator<(const UserSnapshotNamespace& usn) const { + return false; + } +}; + +struct GroupSnapshotNamespace { + static const SnapshotNamespaceType SNAPSHOT_NAMESPACE_TYPE = + SNAPSHOT_NAMESPACE_TYPE_GROUP; + + GroupSnapshotNamespace() {} + + GroupSnapshotNamespace(int64_t _group_pool, + const string &_group_id, + const string &_group_snapshot_id) + : group_id(_group_id), group_pool(_group_pool), + group_snapshot_id(_group_snapshot_id) {} + + string group_id; + int64_t group_pool = 0; + string group_snapshot_id; + + void encode(bufferlist& bl) const; + void decode(bufferlist::const_iterator& it); + + void dump(Formatter *f) const; + + inline bool operator==(const GroupSnapshotNamespace& gsn) const { + return group_pool == gsn.group_pool && + group_id == gsn.group_id && + group_snapshot_id == gsn.group_snapshot_id; + } + + inline bool operator<(const GroupSnapshotNamespace& gsn) const { + if (group_pool < gsn.group_pool) { + return true; + } else if (group_id < gsn.group_id) { + return true; + } else { + return (group_snapshot_id < gsn.group_snapshot_id); + } + return false; + } +}; + +struct TrashSnapshotNamespace { + static const SnapshotNamespaceType SNAPSHOT_NAMESPACE_TYPE = + SNAPSHOT_NAMESPACE_TYPE_TRASH; + + std::string original_name; + SnapshotNamespaceType original_snapshot_namespace_type = + SNAPSHOT_NAMESPACE_TYPE_USER; + + TrashSnapshotNamespace() {} + TrashSnapshotNamespace(SnapshotNamespaceType original_snapshot_namespace_type, + const std::string& original_name) + : original_name(original_name), + original_snapshot_namespace_type(original_snapshot_namespace_type) {} + + void encode(bufferlist& bl) const; + void decode(bufferlist::const_iterator& it); + void dump(Formatter *f) const; + + inline bool operator==(const TrashSnapshotNamespace& usn) const { + return true; + } + inline bool operator<(const TrashSnapshotNamespace& usn) const { + return false; + } +}; + +struct UnknownSnapshotNamespace { + static const SnapshotNamespaceType SNAPSHOT_NAMESPACE_TYPE = + static_cast<SnapshotNamespaceType>(-1); + + UnknownSnapshotNamespace() {} + + void encode(bufferlist& bl) const {} + void decode(bufferlist::const_iterator& it) {} + void dump(Formatter *f) const {} + + inline bool operator==(const UnknownSnapshotNamespace& gsn) const { + return true; + } + + inline bool operator<(const UnknownSnapshotNamespace& gsn) const { + return false; + } +}; + +std::ostream& operator<<(std::ostream& os, const SnapshotNamespaceType& type); +std::ostream& operator<<(std::ostream& os, const UserSnapshotNamespace& ns); +std::ostream& operator<<(std::ostream& os, const GroupSnapshotNamespace& ns); +std::ostream& operator<<(std::ostream& os, const TrashSnapshotNamespace& ns); +std::ostream& operator<<(std::ostream& os, const UnknownSnapshotNamespace& ns); + +typedef boost::variant<UserSnapshotNamespace, + GroupSnapshotNamespace, + TrashSnapshotNamespace, + UnknownSnapshotNamespace> SnapshotNamespaceVariant; + +struct SnapshotNamespace : public SnapshotNamespaceVariant { + SnapshotNamespace() { + } + + template <typename T> + SnapshotNamespace(T&& t) : SnapshotNamespaceVariant(std::forward<T>(t)) { + } + + void encode(bufferlist& bl) const; + void decode(bufferlist::const_iterator& it); + void dump(Formatter *f) const; + + static void generate_test_instances(std::list<SnapshotNamespace*> &o); + + inline bool operator==(const SnapshotNamespaceVariant& sn) const { + return static_cast<const SnapshotNamespaceVariant&>(*this) == sn; + } + inline bool operator<(const SnapshotNamespaceVariant& sn) const { + return static_cast<const SnapshotNamespaceVariant&>(*this) < sn; + } +}; +WRITE_CLASS_ENCODER(SnapshotNamespace); + +SnapshotNamespaceType get_snap_namespace_type( + const SnapshotNamespace& snapshot_namespace); + +struct SnapshotInfo { + snapid_t id = CEPH_NOSNAP; + cls::rbd::SnapshotNamespace snapshot_namespace = {UserSnapshotNamespace{}}; + std::string name; + uint64_t image_size = 0; + utime_t timestamp; + uint32_t child_count = 0; + + SnapshotInfo() { + } + SnapshotInfo(snapid_t id, + const cls::rbd::SnapshotNamespace& snapshot_namespace, + const std::string& name, uint64_t image_size, + const utime_t& timestamp, uint32_t child_count) + : id(id), snapshot_namespace(snapshot_namespace), + name(name), image_size(image_size), timestamp(timestamp), + child_count(child_count) { + } + + void encode(bufferlist& bl) const; + void decode(bufferlist::const_iterator& it); + void dump(Formatter *f) const; + + static void generate_test_instances(std::list<SnapshotInfo*> &o); +}; +WRITE_CLASS_ENCODER(SnapshotInfo); + +enum GroupSnapshotState { + GROUP_SNAPSHOT_STATE_INCOMPLETE = 0, + GROUP_SNAPSHOT_STATE_COMPLETE = 1, +}; + +inline void encode(const GroupSnapshotState &state, bufferlist& bl, uint64_t features=0) +{ + using ceph::encode; + encode(static_cast<uint8_t>(state), bl); +} + +inline void decode(GroupSnapshotState &state, bufferlist::const_iterator& it) +{ + using ceph::decode; + uint8_t int_state; + decode(int_state, it); + state = static_cast<GroupSnapshotState>(int_state); +} + +struct ImageSnapshotSpec { + int64_t pool; + string image_id; + snapid_t snap_id; + + ImageSnapshotSpec() {} + ImageSnapshotSpec(int64_t _pool, + string _image_id, + snapid_t _snap_id) : pool(_pool), + image_id(_image_id), + snap_id(_snap_id) {} + + void encode(bufferlist& bl) const; + void decode(bufferlist::const_iterator& it); + + void dump(Formatter *f) const; + + static void generate_test_instances(std::list<ImageSnapshotSpec *> &o); +}; +WRITE_CLASS_ENCODER(ImageSnapshotSpec); + +struct GroupSnapshot { + std::string id; + std::string name; + GroupSnapshotState state = GROUP_SNAPSHOT_STATE_INCOMPLETE; + + GroupSnapshot() {} + GroupSnapshot(std::string _id, + std::string _name, + GroupSnapshotState _state) : id(_id), + name(_name), + state(_state) {} + + vector<ImageSnapshotSpec> snaps; + + void encode(bufferlist& bl) const; + void decode(bufferlist::const_iterator& it); + void dump(Formatter *f) const; + + static void generate_test_instances(std::list<GroupSnapshot *> &o); +}; +WRITE_CLASS_ENCODER(GroupSnapshot); +enum TrashImageSource { + TRASH_IMAGE_SOURCE_USER = 0, + TRASH_IMAGE_SOURCE_MIRRORING = 1, + TRASH_IMAGE_SOURCE_MIGRATION = 2, + TRASH_IMAGE_SOURCE_REMOVING = 3, +}; + +inline std::ostream& operator<<(std::ostream& os, + const TrashImageSource& source) { + switch (source) { + case TRASH_IMAGE_SOURCE_USER: + os << "user"; + break; + case TRASH_IMAGE_SOURCE_MIRRORING: + os << "mirroring"; + break; + case TRASH_IMAGE_SOURCE_MIGRATION: + os << "migration"; + break; + case TRASH_IMAGE_SOURCE_REMOVING: + os << "removing"; + break; + default: + os << "unknown (" << static_cast<uint32_t>(source) << ")"; + break; + } + return os; +} + +inline void encode(const TrashImageSource &source, bufferlist& bl, + uint64_t features=0) +{ + using ceph::encode; + encode(static_cast<uint8_t>(source), bl); +} + +inline void decode(TrashImageSource &source, bufferlist::const_iterator& it) +{ + uint8_t int_source; + using ceph::decode; + decode(int_source, it); + source = static_cast<TrashImageSource>(int_source); +} + +enum TrashImageState { + TRASH_IMAGE_STATE_NORMAL = 0, + TRASH_IMAGE_STATE_MOVING = 1, + TRASH_IMAGE_STATE_REMOVING = 2, + TRASH_IMAGE_STATE_RESTORING = 3 +}; + +inline void encode(const TrashImageState &state, bufferlist &bl) +{ + using ceph::encode; + encode(static_cast<uint8_t>(state), bl); +} + +inline void decode(TrashImageState &state, bufferlist::const_iterator &it) +{ + uint8_t int_state; + using ceph::decode; + decode(int_state, it); + state = static_cast<TrashImageState>(int_state); +} + +struct TrashImageSpec { + TrashImageSource source = TRASH_IMAGE_SOURCE_USER; + std::string name; + utime_t deletion_time; // time of deletion + utime_t deferment_end_time; + TrashImageState state = TRASH_IMAGE_STATE_NORMAL; + + TrashImageSpec() {} + TrashImageSpec(TrashImageSource source, const std::string &name, + const utime_t& deletion_time, + const utime_t& deferment_end_time) + : source(source), name(name), deletion_time(deletion_time), + deferment_end_time(deferment_end_time) { + } + + void encode(bufferlist &bl) const; + void decode(bufferlist::const_iterator& it); + void dump(Formatter *f) const; + + inline bool operator==(const TrashImageSpec& rhs) const { + return (source == rhs.source && + name == rhs.name && + deletion_time == rhs.deletion_time && + deferment_end_time == rhs.deferment_end_time); + } +}; +WRITE_CLASS_ENCODER(TrashImageSpec); + +struct MirrorImageMap { + MirrorImageMap() { + } + + MirrorImageMap(const std::string &instance_id, utime_t mapped_time, + const bufferlist &data) + : instance_id(instance_id), + mapped_time(mapped_time), + data(data) { + } + + std::string instance_id; + utime_t mapped_time; + bufferlist data; + + void encode(bufferlist &bl) const; + void decode(bufferlist::const_iterator &it); + void dump(Formatter *f) const; + + static void generate_test_instances(std::list<MirrorImageMap*> &o); + + bool operator==(const MirrorImageMap &rhs) const; + bool operator<(const MirrorImageMap &rhs) const; +}; + +std::ostream& operator<<(std::ostream& os, const MirrorImageMap &image_map); + +WRITE_CLASS_ENCODER(MirrorImageMap); + +enum MigrationHeaderType { + MIGRATION_HEADER_TYPE_SRC = 1, + MIGRATION_HEADER_TYPE_DST = 2, +}; + +inline void encode(const MigrationHeaderType &type, bufferlist& bl) { + using ceph::encode; + encode(static_cast<uint8_t>(type), bl); +} + +inline void decode(MigrationHeaderType &type, bufferlist::const_iterator& it) { + uint8_t int_type; + using ceph::decode; + decode(int_type, it); + type = static_cast<MigrationHeaderType>(int_type); +} + +enum MigrationState { + MIGRATION_STATE_ERROR = 0, + MIGRATION_STATE_PREPARING = 1, + MIGRATION_STATE_PREPARED = 2, + MIGRATION_STATE_EXECUTING = 3, + MIGRATION_STATE_EXECUTED = 4, + MIGRATION_STATE_ABORTING = 5, +}; + +inline void encode(const MigrationState &state, bufferlist& bl) { + using ceph::encode; + encode(static_cast<uint8_t>(state), bl); +} + +inline void decode(MigrationState &state, bufferlist::const_iterator& it) { + uint8_t int_state; + using ceph::decode; + decode(int_state, it); + state = static_cast<MigrationState>(int_state); +} + +std::ostream& operator<<(std::ostream& os, + const MigrationState& migration_state); + +struct MigrationSpec { + MigrationHeaderType header_type = MIGRATION_HEADER_TYPE_SRC; + int64_t pool_id = -1; + std::string pool_namespace; + std::string image_name; + std::string image_id; + std::map<uint64_t, uint64_t> snap_seqs; + uint64_t overlap = 0; + bool flatten = false; + bool mirroring = false; + MigrationState state = MIGRATION_STATE_ERROR; + std::string state_description; + + MigrationSpec() { + } + MigrationSpec(MigrationHeaderType header_type, int64_t pool_id, + const std::string& pool_namespace, + const std::string &image_name, const std::string &image_id, + const std::map<uint64_t, uint64_t> &snap_seqs, uint64_t overlap, + bool mirroring, bool flatten, MigrationState state, + const std::string &state_description) + : header_type(header_type), pool_id(pool_id), + pool_namespace(pool_namespace), image_name(image_name), + image_id(image_id), snap_seqs(snap_seqs), overlap(overlap), + flatten(flatten), mirroring(mirroring), state(state), + state_description(state_description) { + } + + void encode(bufferlist &bl) const; + void decode(bufferlist::const_iterator& it); + void dump(Formatter *f) const; + + static void generate_test_instances(std::list<MigrationSpec*> &o); + + inline bool operator==(const MigrationSpec& ms) const { + return header_type == ms.header_type && pool_id == ms.pool_id && + pool_namespace == ms.pool_namespace && image_name == ms.image_name && + image_id == ms.image_id && snap_seqs == ms.snap_seqs && + overlap == ms.overlap && flatten == ms.flatten && + mirroring == ms.mirroring && state == ms.state && + state_description == ms.state_description; + } +}; + +std::ostream& operator<<(std::ostream& os, const MigrationSpec& migration_spec); + +WRITE_CLASS_ENCODER(MigrationSpec); + +enum AssertSnapcSeqState { + ASSERT_SNAPC_SEQ_GT_SNAPSET_SEQ = 0, + ASSERT_SNAPC_SEQ_LE_SNAPSET_SEQ = 1, +}; + +inline void encode(const AssertSnapcSeqState &state, bufferlist& bl) { + using ceph::encode; + encode(static_cast<uint8_t>(state), bl); +} + +inline void decode(AssertSnapcSeqState &state, bufferlist::const_iterator& it) { + uint8_t int_state; + using ceph::decode; + decode(int_state, it); + state = static_cast<AssertSnapcSeqState>(int_state); +} + +std::ostream& operator<<(std::ostream& os, const AssertSnapcSeqState& state); + +} // namespace rbd +} // namespace cls + +#endif // CEPH_CLS_RBD_TYPES_H |