summaryrefslogtreecommitdiffstats
path: root/src/librbd/internal.cc
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/librbd/internal.cc2021
1 files changed, 2021 insertions, 0 deletions
diff --git a/src/librbd/internal.cc b/src/librbd/internal.cc
new file mode 100644
index 00000000..ebf2603f
--- /dev/null
+++ b/src/librbd/internal.cc
@@ -0,0 +1,2021 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+#include "include/int_types.h"
+
+#include <errno.h>
+#include <limits.h>
+
+#include "include/types.h"
+#include "include/uuid.h"
+#include "common/ceph_context.h"
+#include "common/dout.h"
+#include "common/errno.h"
+#include "common/Throttle.h"
+#include "common/event_socket.h"
+#include "common/perf_counters.h"
+#include "osdc/Striper.h"
+#include "include/stringify.h"
+
+#include "cls/lock/cls_lock_client.h"
+#include "cls/rbd/cls_rbd.h"
+#include "cls/rbd/cls_rbd_types.h"
+#include "cls/rbd/cls_rbd_client.h"
+#include "cls/journal/cls_journal_types.h"
+#include "cls/journal/cls_journal_client.h"
+
+#include "librbd/ExclusiveLock.h"
+#include "librbd/ImageCtx.h"
+#include "librbd/ImageState.h"
+#include "librbd/internal.h"
+#include "librbd/Journal.h"
+#include "librbd/ObjectMap.h"
+#include "librbd/Operations.h"
+#include "librbd/Types.h"
+#include "librbd/Utils.h"
+#include "librbd/api/Config.h"
+#include "librbd/api/Image.h"
+#include "librbd/exclusive_lock/AutomaticPolicy.h"
+#include "librbd/exclusive_lock/StandardPolicy.h"
+#include "librbd/image/CloneRequest.h"
+#include "librbd/image/CreateRequest.h"
+#include "librbd/io/AioCompletion.h"
+#include "librbd/io/ImageRequest.h"
+#include "librbd/io/ImageRequestWQ.h"
+#include "librbd/io/ObjectDispatcher.h"
+#include "librbd/io/ObjectDispatchSpec.h"
+#include "librbd/io/ObjectRequest.h"
+#include "librbd/io/ReadResult.h"
+#include "librbd/journal/Types.h"
+#include "librbd/managed_lock/Types.h"
+#include "librbd/mirror/EnableRequest.h"
+#include "librbd/operation/TrimRequest.h"
+
+#include "journal/Journaler.h"
+
+#include <boost/scope_exit.hpp>
+#include <boost/variant.hpp>
+#include "include/ceph_assert.h"
+
+#define dout_subsys ceph_subsys_rbd
+#undef dout_prefix
+#define dout_prefix *_dout << "librbd: "
+
+#define rbd_howmany(x, y) (((x) + (y) - 1) / (y))
+
+using std::map;
+using std::pair;
+using std::set;
+using std::string;
+using std::vector;
+// list binds to list() here, so std::list is explicitly used below
+
+using ceph::bufferlist;
+using librados::snap_t;
+using librados::IoCtx;
+using librados::Rados;
+
+namespace librbd {
+
+namespace {
+
+int validate_pool(IoCtx &io_ctx, CephContext *cct) {
+ if (!cct->_conf.get_val<bool>("rbd_validate_pool")) {
+ return 0;
+ }
+
+ int r = io_ctx.stat(RBD_DIRECTORY, NULL, NULL);
+ if (r == 0) {
+ return 0;
+ } else if (r < 0 && r != -ENOENT) {
+ lderr(cct) << "failed to stat RBD directory: " << cpp_strerror(r) << dendl;
+ return r;
+ }
+
+ // allocate a self-managed snapshot id if this a new pool to force
+ // self-managed snapshot mode
+ uint64_t snap_id;
+ r = io_ctx.selfmanaged_snap_create(&snap_id);
+ if (r == -EINVAL) {
+ lderr(cct) << "pool not configured for self-managed RBD snapshot support"
+ << dendl;
+ return r;
+ } else if (r < 0) {
+ lderr(cct) << "failed to allocate self-managed snapshot: "
+ << cpp_strerror(r) << dendl;
+ return r;
+ }
+
+ r = io_ctx.selfmanaged_snap_remove(snap_id);
+ if (r < 0) {
+ lderr(cct) << "failed to release self-managed snapshot " << snap_id
+ << ": " << cpp_strerror(r) << dendl;
+ }
+ return 0;
+}
+
+} // anonymous namespace
+
+ int detect_format(IoCtx &io_ctx, const string &name,
+ bool *old_format, uint64_t *size)
+ {
+ CephContext *cct = (CephContext *)io_ctx.cct();
+ if (old_format)
+ *old_format = true;
+ int r = io_ctx.stat(util::old_header_name(name), size, NULL);
+ if (r == -ENOENT) {
+ if (old_format)
+ *old_format = false;
+ r = io_ctx.stat(util::id_obj_name(name), size, NULL);
+ if (r < 0)
+ return r;
+ } else if (r < 0) {
+ return r;
+ }
+
+ ldout(cct, 20) << "detect format of " << name << " : "
+ << (old_format ? (*old_format ? "old" : "new") :
+ "don't care") << dendl;
+ return 0;
+ }
+
+ bool has_parent(int64_t parent_pool_id, uint64_t off, uint64_t overlap)
+ {
+ return (parent_pool_id != -1 && off <= overlap);
+ }
+
+ void init_rbd_header(struct rbd_obj_header_ondisk& ondisk,
+ uint64_t size, int order, uint64_t bid)
+ {
+ uint32_t hi = bid >> 32;
+ uint32_t lo = bid & 0xFFFFFFFF;
+ uint32_t extra = rand() % 0xFFFFFFFF;
+ // FIPS zeroization audit 20191117: this memset is not security related.
+ memset(&ondisk, 0, sizeof(ondisk));
+
+ memcpy(&ondisk.text, RBD_HEADER_TEXT, sizeof(RBD_HEADER_TEXT));
+ memcpy(&ondisk.signature, RBD_HEADER_SIGNATURE,
+ sizeof(RBD_HEADER_SIGNATURE));
+ memcpy(&ondisk.version, RBD_HEADER_VERSION, sizeof(RBD_HEADER_VERSION));
+
+ snprintf(ondisk.block_name, sizeof(ondisk.block_name), "rb.%x.%x.%x",
+ hi, lo, extra);
+
+ ondisk.image_size = size;
+ ondisk.options.order = order;
+ ondisk.options.crypt_type = RBD_CRYPT_NONE;
+ ondisk.options.comp_type = RBD_COMP_NONE;
+ ondisk.snap_seq = 0;
+ ondisk.snap_count = 0;
+ ondisk.reserved = 0;
+ ondisk.snap_names_len = 0;
+ }
+
+ void image_info(ImageCtx *ictx, image_info_t& info, size_t infosize)
+ {
+ int obj_order = ictx->order;
+ ictx->snap_lock.get_read();
+ info.size = ictx->get_image_size(ictx->snap_id);
+ ictx->snap_lock.put_read();
+ info.obj_size = 1ULL << obj_order;
+ info.num_objs = Striper::get_num_objects(ictx->layout, info.size);
+ info.order = obj_order;
+ strncpy(info.block_name_prefix, ictx->object_prefix.c_str(),
+ RBD_MAX_BLOCK_NAME_SIZE);
+ info.block_name_prefix[RBD_MAX_BLOCK_NAME_SIZE - 1] = '\0';
+
+ // clear deprecated fields
+ info.parent_pool = -1L;
+ info.parent_name[0] = '\0';
+ }
+
+ uint64_t oid_to_object_no(const string& oid, const string& object_prefix)
+ {
+ istringstream iss(oid);
+ // skip object prefix and separator
+ iss.ignore(object_prefix.length() + 1);
+ uint64_t num;
+ iss >> std::hex >> num;
+ return num;
+ }
+
+ void trim_image(ImageCtx *ictx, uint64_t newsize, ProgressContext& prog_ctx)
+ {
+ ceph_assert(ictx->owner_lock.is_locked());
+ ceph_assert(ictx->exclusive_lock == nullptr ||
+ ictx->exclusive_lock->is_lock_owner());
+
+ C_SaferCond ctx;
+ ictx->snap_lock.get_read();
+ operation::TrimRequest<> *req = operation::TrimRequest<>::create(
+ *ictx, &ctx, ictx->size, newsize, prog_ctx);
+ ictx->snap_lock.put_read();
+ req->send();
+
+ int r = ctx.wait();
+ if (r < 0) {
+ lderr(ictx->cct) << "warning: failed to remove some object(s): "
+ << cpp_strerror(r) << dendl;
+ }
+ }
+
+ int read_header_bl(IoCtx& io_ctx, const string& header_oid,
+ bufferlist& header, uint64_t *ver)
+ {
+ int r;
+ uint64_t off = 0;
+#define READ_SIZE 4096
+ do {
+ bufferlist bl;
+ r = io_ctx.read(header_oid, bl, READ_SIZE, off);
+ if (r < 0)
+ return r;
+ header.claim_append(bl);
+ off += r;
+ } while (r == READ_SIZE);
+
+ static_assert(sizeof(RBD_HEADER_TEXT) == sizeof(RBD_MIGRATE_HEADER_TEXT),
+ "length of rbd headers must be the same");
+
+ if (header.length() < sizeof(RBD_HEADER_TEXT) ||
+ (memcmp(RBD_HEADER_TEXT, header.c_str(),
+ sizeof(RBD_HEADER_TEXT)) != 0 &&
+ memcmp(RBD_MIGRATE_HEADER_TEXT, header.c_str(),
+ sizeof(RBD_MIGRATE_HEADER_TEXT)) != 0)) {
+ CephContext *cct = (CephContext *)io_ctx.cct();
+ lderr(cct) << "unrecognized header format" << dendl;
+ return -ENXIO;
+ }
+
+ if (ver)
+ *ver = io_ctx.get_last_version();
+
+ return 0;
+ }
+
+ int read_header(IoCtx& io_ctx, const string& header_oid,
+ struct rbd_obj_header_ondisk *header, uint64_t *ver)
+ {
+ bufferlist header_bl;
+ int r = read_header_bl(io_ctx, header_oid, header_bl, ver);
+ if (r < 0)
+ return r;
+ if (header_bl.length() < (int)sizeof(*header))
+ return -EIO;
+ memcpy(header, header_bl.c_str(), sizeof(*header));
+
+ return 0;
+ }
+
+ int tmap_set(IoCtx& io_ctx, const string& imgname)
+ {
+ bufferlist cmdbl, emptybl;
+ __u8 c = CEPH_OSD_TMAP_SET;
+ encode(c, cmdbl);
+ encode(imgname, cmdbl);
+ encode(emptybl, cmdbl);
+ return io_ctx.tmap_update(RBD_DIRECTORY, cmdbl);
+ }
+
+ int tmap_rm(IoCtx& io_ctx, const string& imgname)
+ {
+ bufferlist cmdbl;
+ __u8 c = CEPH_OSD_TMAP_RM;
+ encode(c, cmdbl);
+ encode(imgname, cmdbl);
+ return io_ctx.tmap_update(RBD_DIRECTORY, cmdbl);
+ }
+
+ typedef boost::variant<std::string,uint64_t> image_option_value_t;
+ typedef std::map<int,image_option_value_t> image_options_t;
+ typedef std::shared_ptr<image_options_t> image_options_ref;
+
+ enum image_option_type_t {
+ STR,
+ UINT64,
+ };
+
+ const std::map<int, image_option_type_t> IMAGE_OPTIONS_TYPE_MAPPING = {
+ {RBD_IMAGE_OPTION_FORMAT, UINT64},
+ {RBD_IMAGE_OPTION_FEATURES, UINT64},
+ {RBD_IMAGE_OPTION_ORDER, UINT64},
+ {RBD_IMAGE_OPTION_STRIPE_UNIT, UINT64},
+ {RBD_IMAGE_OPTION_STRIPE_COUNT, UINT64},
+ {RBD_IMAGE_OPTION_JOURNAL_ORDER, UINT64},
+ {RBD_IMAGE_OPTION_JOURNAL_SPLAY_WIDTH, UINT64},
+ {RBD_IMAGE_OPTION_JOURNAL_POOL, STR},
+ {RBD_IMAGE_OPTION_FEATURES_SET, UINT64},
+ {RBD_IMAGE_OPTION_FEATURES_CLEAR, UINT64},
+ {RBD_IMAGE_OPTION_DATA_POOL, STR},
+ {RBD_IMAGE_OPTION_FLATTEN, UINT64},
+ {RBD_IMAGE_OPTION_CLONE_FORMAT, UINT64},
+ };
+
+ std::string image_option_name(int optname) {
+ switch (optname) {
+ case RBD_IMAGE_OPTION_FORMAT:
+ return "format";
+ case RBD_IMAGE_OPTION_FEATURES:
+ return "features";
+ case RBD_IMAGE_OPTION_ORDER:
+ return "order";
+ case RBD_IMAGE_OPTION_STRIPE_UNIT:
+ return "stripe_unit";
+ case RBD_IMAGE_OPTION_STRIPE_COUNT:
+ return "stripe_count";
+ case RBD_IMAGE_OPTION_JOURNAL_ORDER:
+ return "journal_order";
+ case RBD_IMAGE_OPTION_JOURNAL_SPLAY_WIDTH:
+ return "journal_splay_width";
+ case RBD_IMAGE_OPTION_JOURNAL_POOL:
+ return "journal_pool";
+ case RBD_IMAGE_OPTION_FEATURES_SET:
+ return "features_set";
+ case RBD_IMAGE_OPTION_FEATURES_CLEAR:
+ return "features_clear";
+ case RBD_IMAGE_OPTION_DATA_POOL:
+ return "data_pool";
+ case RBD_IMAGE_OPTION_FLATTEN:
+ return "flatten";
+ case RBD_IMAGE_OPTION_CLONE_FORMAT:
+ return "clone_format";
+ default:
+ return "unknown (" + stringify(optname) + ")";
+ }
+ }
+
+ void image_options_create(rbd_image_options_t* opts)
+ {
+ image_options_ref* opts_ = new image_options_ref(new image_options_t());
+
+ *opts = static_cast<rbd_image_options_t>(opts_);
+ }
+
+ void image_options_create_ref(rbd_image_options_t* opts,
+ rbd_image_options_t orig)
+ {
+ image_options_ref* orig_ = static_cast<image_options_ref*>(orig);
+ image_options_ref* opts_ = new image_options_ref(*orig_);
+
+ *opts = static_cast<rbd_image_options_t>(opts_);
+ }
+
+ void image_options_copy(rbd_image_options_t* opts,
+ const ImageOptions &orig)
+ {
+ image_options_ref* opts_ = new image_options_ref(new image_options_t());
+
+ *opts = static_cast<rbd_image_options_t>(opts_);
+
+ std::string str_val;
+ uint64_t uint64_val;
+ for (auto &i : IMAGE_OPTIONS_TYPE_MAPPING) {
+ switch (i.second) {
+ case STR:
+ if (orig.get(i.first, &str_val) == 0) {
+ image_options_set(*opts, i.first, str_val);
+ }
+ continue;
+ case UINT64:
+ if (orig.get(i.first, &uint64_val) == 0) {
+ image_options_set(*opts, i.first, uint64_val);
+ }
+ continue;
+ }
+ }
+ }
+
+ void image_options_destroy(rbd_image_options_t opts)
+ {
+ image_options_ref* opts_ = static_cast<image_options_ref*>(opts);
+
+ delete opts_;
+ }
+
+ int image_options_set(rbd_image_options_t opts, int optname,
+ const std::string& optval)
+ {
+ image_options_ref* opts_ = static_cast<image_options_ref*>(opts);
+
+ std::map<int, image_option_type_t>::const_iterator i =
+ IMAGE_OPTIONS_TYPE_MAPPING.find(optname);
+
+ if (i == IMAGE_OPTIONS_TYPE_MAPPING.end() || i->second != STR) {
+ return -EINVAL;
+ }
+
+ (*opts_->get())[optname] = optval;
+ return 0;
+ }
+
+ int image_options_set(rbd_image_options_t opts, int optname, uint64_t optval)
+ {
+ image_options_ref* opts_ = static_cast<image_options_ref*>(opts);
+
+ std::map<int, image_option_type_t>::const_iterator i =
+ IMAGE_OPTIONS_TYPE_MAPPING.find(optname);
+
+ if (i == IMAGE_OPTIONS_TYPE_MAPPING.end() || i->second != UINT64) {
+ return -EINVAL;
+ }
+
+ (*opts_->get())[optname] = optval;
+ return 0;
+ }
+
+ int image_options_get(rbd_image_options_t opts, int optname,
+ std::string* optval)
+ {
+ image_options_ref* opts_ = static_cast<image_options_ref*>(opts);
+
+ std::map<int, image_option_type_t>::const_iterator i =
+ IMAGE_OPTIONS_TYPE_MAPPING.find(optname);
+
+ if (i == IMAGE_OPTIONS_TYPE_MAPPING.end() || i->second != STR) {
+ return -EINVAL;
+ }
+
+ image_options_t::const_iterator j = (*opts_)->find(optname);
+
+ if (j == (*opts_)->end()) {
+ return -ENOENT;
+ }
+
+ *optval = boost::get<std::string>(j->second);
+ return 0;
+ }
+
+ int image_options_get(rbd_image_options_t opts, int optname, uint64_t* optval)
+ {
+ image_options_ref* opts_ = static_cast<image_options_ref*>(opts);
+
+ std::map<int, image_option_type_t>::const_iterator i =
+ IMAGE_OPTIONS_TYPE_MAPPING.find(optname);
+
+ if (i == IMAGE_OPTIONS_TYPE_MAPPING.end() || i->second != UINT64) {
+ return -EINVAL;
+ }
+
+ image_options_t::const_iterator j = (*opts_)->find(optname);
+
+ if (j == (*opts_)->end()) {
+ return -ENOENT;
+ }
+
+ *optval = boost::get<uint64_t>(j->second);
+ return 0;
+ }
+
+ int image_options_is_set(rbd_image_options_t opts, int optname,
+ bool* is_set)
+ {
+ if (IMAGE_OPTIONS_TYPE_MAPPING.find(optname) ==
+ IMAGE_OPTIONS_TYPE_MAPPING.end()) {
+ return -EINVAL;
+ }
+
+ image_options_ref* opts_ = static_cast<image_options_ref*>(opts);
+ *is_set = ((*opts_)->find(optname) != (*opts_)->end());
+ return 0;
+ }
+
+ int image_options_unset(rbd_image_options_t opts, int optname)
+ {
+ image_options_ref* opts_ = static_cast<image_options_ref*>(opts);
+
+ std::map<int, image_option_type_t>::const_iterator i =
+ IMAGE_OPTIONS_TYPE_MAPPING.find(optname);
+
+ if (i == IMAGE_OPTIONS_TYPE_MAPPING.end()) {
+ ceph_assert((*opts_)->find(optname) == (*opts_)->end());
+ return -EINVAL;
+ }
+
+ image_options_t::const_iterator j = (*opts_)->find(optname);
+
+ if (j == (*opts_)->end()) {
+ return -ENOENT;
+ }
+
+ (*opts_)->erase(j);
+ return 0;
+ }
+
+ void image_options_clear(rbd_image_options_t opts)
+ {
+ image_options_ref* opts_ = static_cast<image_options_ref*>(opts);
+
+ (*opts_)->clear();
+ }
+
+ bool image_options_is_empty(rbd_image_options_t opts)
+ {
+ image_options_ref* opts_ = static_cast<image_options_ref*>(opts);
+
+ return (*opts_)->empty();
+ }
+
+ int flatten_children(ImageCtx *ictx, const char* snap_name,
+ ProgressContext& pctx)
+ {
+ CephContext *cct = ictx->cct;
+ ldout(cct, 20) << "children flatten " << ictx->name << dendl;
+
+ int r = ictx->state->refresh_if_required();
+ if (r < 0) {
+ return r;
+ }
+
+ RWLock::RLocker l(ictx->snap_lock);
+ snap_t snap_id = ictx->get_snap_id(cls::rbd::UserSnapshotNamespace(),
+ snap_name);
+
+ cls::rbd::ParentImageSpec parent_spec{ictx->md_ctx.get_id(),
+ ictx->md_ctx.get_namespace(),
+ ictx->id, snap_id};
+ std::vector<librbd::linked_image_spec_t> child_images;
+ r = api::Image<>::list_children(ictx, parent_spec, &child_images);
+ if (r < 0) {
+ return r;
+ }
+
+ size_t size = child_images.size();
+ if (size == 0) {
+ return 0;
+ }
+
+ librados::IoCtx child_io_ctx;
+ int64_t child_pool_id = -1;
+ size_t i = 0;
+ for (auto &child_image : child_images){
+ std::string pool = child_image.pool_name;
+ if (child_pool_id == -1 ||
+ child_pool_id != child_image.pool_id ||
+ child_io_ctx.get_namespace() != child_image.pool_namespace) {
+ r = util::create_ioctx(ictx->md_ctx, "child image",
+ child_image.pool_id, child_image.pool_namespace,
+ &child_io_ctx);
+ if (r < 0) {
+ return r;
+ }
+
+ child_pool_id = child_image.pool_id;
+ }
+
+ ImageCtx *imctx = new ImageCtx("", child_image.image_id, nullptr,
+ child_io_ctx, false);
+ r = imctx->state->open(0);
+ if (r < 0) {
+ lderr(cct) << "error opening image: " << cpp_strerror(r) << dendl;
+ return r;
+ }
+
+ if ((imctx->features & RBD_FEATURE_DEEP_FLATTEN) == 0 &&
+ !imctx->snaps.empty()) {
+ lderr(cct) << "snapshot in-use by " << pool << "/" << imctx->name
+ << dendl;
+ imctx->state->close();
+ return -EBUSY;
+ }
+
+ librbd::NoOpProgressContext prog_ctx;
+ r = imctx->operations->flatten(prog_ctx);
+ if (r < 0) {
+ lderr(cct) << "error flattening image: " << pool << "/"
+ << (child_image.pool_namespace.empty() ?
+ "" : "/" + child_image.pool_namespace)
+ << child_image.image_name << cpp_strerror(r) << dendl;
+ imctx->state->close();
+ return r;
+ }
+
+ r = imctx->state->close();
+ if (r < 0) {
+ lderr(cct) << "failed to close image: " << cpp_strerror(r) << dendl;
+ return r;
+ }
+
+ pctx.update_progress(++i, size);
+ ceph_assert(i <= size);
+ }
+
+ return 0;
+ }
+
+ int get_snap_namespace(ImageCtx *ictx,
+ const char *snap_name,
+ cls::rbd::SnapshotNamespace *snap_namespace) {
+ ldout(ictx->cct, 20) << "get_snap_namespace " << ictx << " " << snap_name
+ << dendl;
+
+ int r = ictx->state->refresh_if_required();
+ if (r < 0)
+ return r;
+ RWLock::RLocker l(ictx->snap_lock);
+ snap_t snap_id = ictx->get_snap_id(*snap_namespace, snap_name);
+ if (snap_id == CEPH_NOSNAP)
+ return -ENOENT;
+ r = ictx->get_snap_namespace(snap_id, snap_namespace);
+ return r;
+ }
+
+ int snap_is_protected(ImageCtx *ictx, const char *snap_name, bool *is_protected)
+ {
+ ldout(ictx->cct, 20) << "snap_is_protected " << ictx << " " << snap_name
+ << dendl;
+
+ int r = ictx->state->refresh_if_required();
+ if (r < 0)
+ return r;
+
+ RWLock::RLocker l(ictx->snap_lock);
+ snap_t snap_id = ictx->get_snap_id(cls::rbd::UserSnapshotNamespace(), snap_name);
+ if (snap_id == CEPH_NOSNAP)
+ return -ENOENT;
+ bool is_unprotected;
+ r = ictx->is_snap_unprotected(snap_id, &is_unprotected);
+ // consider both PROTECTED or UNPROTECTING to be 'protected',
+ // since in either state they can't be deleted
+ *is_protected = !is_unprotected;
+ return r;
+ }
+
+ int create_v1(IoCtx& io_ctx, const char *imgname, uint64_t size, int order)
+ {
+ CephContext *cct = (CephContext *)io_ctx.cct();
+
+ ldout(cct, 20) << __func__ << " " << &io_ctx << " name = " << imgname
+ << " size = " << size << " order = " << order << dendl;
+ int r = validate_pool(io_ctx, cct);
+ if (r < 0) {
+ return r;
+ }
+
+ if (!io_ctx.get_namespace().empty()) {
+ lderr(cct) << "attempting to add v1 image to namespace" << dendl;
+ return -EINVAL;
+ }
+
+ ldout(cct, 2) << "adding rbd image to directory..." << dendl;
+ r = tmap_set(io_ctx, imgname);
+ if (r < 0) {
+ lderr(cct) << "error adding image to directory: " << cpp_strerror(r)
+ << dendl;
+ return r;
+ }
+
+ Rados rados(io_ctx);
+ uint64_t bid = rados.get_instance_id();
+
+ ldout(cct, 2) << "creating rbd image..." << dendl;
+ struct rbd_obj_header_ondisk header;
+ init_rbd_header(header, size, order, bid);
+
+ bufferlist bl;
+ bl.append((const char *)&header, sizeof(header));
+
+ string header_oid = util::old_header_name(imgname);
+ r = io_ctx.write(header_oid, bl, bl.length(), 0);
+ if (r < 0) {
+ lderr(cct) << "Error writing image header: " << cpp_strerror(r)
+ << dendl;
+ int remove_r = tmap_rm(io_ctx, imgname);
+ if (remove_r < 0) {
+ lderr(cct) << "Could not remove image from directory after "
+ << "header creation failed: "
+ << cpp_strerror(remove_r) << dendl;
+ }
+ return r;
+ }
+
+ ldout(cct, 2) << "done." << dendl;
+ return 0;
+ }
+
+ int create(librados::IoCtx& io_ctx, const char *imgname, uint64_t size,
+ int *order)
+ {
+ uint64_t order_ = *order;
+ ImageOptions opts;
+
+ int r = opts.set(RBD_IMAGE_OPTION_ORDER, order_);
+ ceph_assert(r == 0);
+
+ r = create(io_ctx, imgname, "", size, opts, "", "", false);
+
+ int r1 = opts.get(RBD_IMAGE_OPTION_ORDER, &order_);
+ ceph_assert(r1 == 0);
+ *order = order_;
+
+ return r;
+ }
+
+ int create(IoCtx& io_ctx, const char *imgname, uint64_t size,
+ bool old_format, uint64_t features, int *order,
+ uint64_t stripe_unit, uint64_t stripe_count)
+ {
+ if (!order)
+ return -EINVAL;
+
+ uint64_t order_ = *order;
+ uint64_t format = old_format ? 1 : 2;
+ ImageOptions opts;
+ int r;
+
+ r = opts.set(RBD_IMAGE_OPTION_FORMAT, format);
+ ceph_assert(r == 0);
+ r = opts.set(RBD_IMAGE_OPTION_FEATURES, features);
+ ceph_assert(r == 0);
+ r = opts.set(RBD_IMAGE_OPTION_ORDER, order_);
+ ceph_assert(r == 0);
+ r = opts.set(RBD_IMAGE_OPTION_STRIPE_UNIT, stripe_unit);
+ ceph_assert(r == 0);
+ r = opts.set(RBD_IMAGE_OPTION_STRIPE_COUNT, stripe_count);
+ ceph_assert(r == 0);
+
+ r = create(io_ctx, imgname, "", size, opts, "", "", false);
+
+ int r1 = opts.get(RBD_IMAGE_OPTION_ORDER, &order_);
+ ceph_assert(r1 == 0);
+ *order = order_;
+
+ return r;
+ }
+
+ int create(IoCtx& io_ctx, const std::string &image_name,
+ const std::string &image_id, uint64_t size,
+ ImageOptions& opts,
+ const std::string &non_primary_global_image_id,
+ const std::string &primary_mirror_uuid,
+ bool skip_mirror_enable)
+ {
+ std::string id(image_id);
+ if (id.empty()) {
+ id = util::generate_image_id(io_ctx);
+ }
+
+ CephContext *cct = (CephContext *)io_ctx.cct();
+ uint64_t option;
+ if (opts.get(RBD_IMAGE_OPTION_FLATTEN, &option) == 0) {
+ lderr(cct) << "create does not support 'flatten' image option" << dendl;
+ return -EINVAL;
+ }
+ if (opts.get(RBD_IMAGE_OPTION_CLONE_FORMAT, &option) == 0) {
+ lderr(cct) << "create does not support 'clone_format' image option"
+ << dendl;
+ return -EINVAL;
+ }
+
+ ldout(cct, 10) << __func__ << " name=" << image_name << ", "
+ << "id= " << id << ", "
+ << "size=" << size << ", opts=" << opts << dendl;
+
+ uint64_t format;
+ if (opts.get(RBD_IMAGE_OPTION_FORMAT, &format) != 0)
+ format = cct->_conf.get_val<uint64_t>("rbd_default_format");
+ bool old_format = format == 1;
+
+ // make sure it doesn't already exist, in either format
+ int r = detect_format(io_ctx, image_name, NULL, NULL);
+ if (r != -ENOENT) {
+ if (r) {
+ lderr(cct) << "Could not tell if " << image_name << " already exists"
+ << dendl;
+ return r;
+ }
+ lderr(cct) << "rbd image " << image_name << " already exists" << dendl;
+ return -EEXIST;
+ }
+
+ uint64_t order = 0;
+ if (opts.get(RBD_IMAGE_OPTION_ORDER, &order) != 0 || order == 0) {
+ order = cct->_conf.get_val<uint64_t>("rbd_default_order");
+ }
+ r = image::CreateRequest<>::validate_order(cct, order);
+ if (r < 0) {
+ return r;
+ }
+
+ if (old_format) {
+ if ( !getenv("RBD_FORCE_ALLOW_V1") ) {
+ lderr(cct) << "Format 1 image creation unsupported. " << dendl;
+ return -EINVAL;
+ }
+ lderr(cct) << "Forced V1 image creation. " << dendl;
+ r = create_v1(io_ctx, image_name.c_str(), size, order);
+ } else {
+ ThreadPool *thread_pool;
+ ContextWQ *op_work_queue;
+ ImageCtx::get_thread_pool_instance(cct, &thread_pool, &op_work_queue);
+
+ ConfigProxy config{cct->_conf};
+ api::Config<>::apply_pool_overrides(io_ctx, &config);
+
+ C_SaferCond cond;
+ image::CreateRequest<> *req = image::CreateRequest<>::create(
+ config, io_ctx, image_name, id, size, opts, non_primary_global_image_id,
+ primary_mirror_uuid, skip_mirror_enable, op_work_queue, &cond);
+ req->send();
+
+ r = cond.wait();
+ }
+
+ int r1 = opts.set(RBD_IMAGE_OPTION_ORDER, order);
+ ceph_assert(r1 == 0);
+
+ return r;
+ }
+
+ /*
+ * Parent may be in different pool, hence different IoCtx
+ */
+ int clone(IoCtx& p_ioctx, const char *p_name, const char *p_snap_name,
+ IoCtx& c_ioctx, const char *c_name,
+ uint64_t features, int *c_order,
+ uint64_t stripe_unit, int stripe_count)
+ {
+ uint64_t order = *c_order;
+
+ ImageOptions opts;
+ opts.set(RBD_IMAGE_OPTION_FEATURES, features);
+ opts.set(RBD_IMAGE_OPTION_ORDER, order);
+ opts.set(RBD_IMAGE_OPTION_STRIPE_UNIT, stripe_unit);
+ opts.set(RBD_IMAGE_OPTION_STRIPE_COUNT, stripe_count);
+
+ int r = clone(p_ioctx, nullptr, p_name, p_snap_name, c_ioctx, nullptr,
+ c_name, opts, "", "");
+ opts.get(RBD_IMAGE_OPTION_ORDER, &order);
+ *c_order = order;
+ return r;
+ }
+
+ int clone(IoCtx& p_ioctx, const char *p_id, const char *p_name,
+ const char *p_snap_name, IoCtx& c_ioctx, const char *c_id,
+ const char *c_name, ImageOptions& c_opts,
+ const std::string &non_primary_global_image_id,
+ const std::string &primary_mirror_uuid)
+ {
+ ceph_assert((p_id == nullptr) ^ (p_name == nullptr));
+
+ CephContext *cct = (CephContext *)p_ioctx.cct();
+ if (p_snap_name == nullptr) {
+ lderr(cct) << "image to be cloned must be a snapshot" << dendl;
+ return -EINVAL;
+ }
+
+ uint64_t flatten;
+ if (c_opts.get(RBD_IMAGE_OPTION_FLATTEN, &flatten) == 0) {
+ lderr(cct) << "clone does not support 'flatten' image option" << dendl;
+ return -EINVAL;
+ }
+
+ int r;
+ std::string parent_id;
+ if (p_id == nullptr) {
+ r = cls_client::dir_get_id(&p_ioctx, RBD_DIRECTORY, p_name,
+ &parent_id);
+ if (r < 0) {
+ if (r != -ENOENT) {
+ lderr(cct) << "failed to retrieve parent image id: "
+ << cpp_strerror(r) << dendl;
+ }
+ return r;
+ }
+ } else {
+ parent_id = p_id;
+ }
+
+ std::string clone_id;
+ if (c_id == nullptr) {
+ clone_id = util::generate_image_id(c_ioctx);
+ } else {
+ clone_id = c_id;
+ }
+
+ ldout(cct, 10) << __func__ << " "
+ << "c_name=" << c_name << ", "
+ << "c_id= " << clone_id << ", "
+ << "c_opts=" << c_opts << dendl;
+
+ ConfigProxy config{reinterpret_cast<CephContext *>(c_ioctx.cct())->_conf};
+ api::Config<>::apply_pool_overrides(c_ioctx, &config);
+
+ ThreadPool *thread_pool;
+ ContextWQ *op_work_queue;
+ ImageCtx::get_thread_pool_instance(cct, &thread_pool, &op_work_queue);
+
+ C_SaferCond cond;
+ auto *req = image::CloneRequest<>::create(
+ config, p_ioctx, parent_id, p_snap_name, CEPH_NOSNAP, c_ioctx, c_name,
+ clone_id, c_opts, non_primary_global_image_id, primary_mirror_uuid,
+ op_work_queue, &cond);
+ req->send();
+
+ r = cond.wait();
+ if (r < 0) {
+ return r;
+ }
+
+ return 0;
+ }
+
+ int rename(IoCtx& io_ctx, const char *srcname, const char *dstname)
+ {
+ CephContext *cct = (CephContext *)io_ctx.cct();
+ ldout(cct, 20) << "rename " << &io_ctx << " " << srcname << " -> "
+ << dstname << dendl;
+
+ ImageCtx *ictx = new ImageCtx(srcname, "", "", io_ctx, false);
+ int r = ictx->state->open(0);
+ if (r < 0) {
+ lderr(cct) << "error opening source image: " << cpp_strerror(r) << dendl;
+ return r;
+ }
+ BOOST_SCOPE_EXIT((ictx)) {
+ ictx->state->close();
+ } BOOST_SCOPE_EXIT_END
+
+ return ictx->operations->rename(dstname);
+ }
+
+ int info(ImageCtx *ictx, image_info_t& info, size_t infosize)
+ {
+ ldout(ictx->cct, 20) << "info " << ictx << dendl;
+
+ int r = ictx->state->refresh_if_required();
+ if (r < 0)
+ return r;
+
+ image_info(ictx, info, infosize);
+ return 0;
+ }
+
+ int get_old_format(ImageCtx *ictx, uint8_t *old)
+ {
+ int r = ictx->state->refresh_if_required();
+ if (r < 0)
+ return r;
+ *old = ictx->old_format;
+ return 0;
+ }
+
+ int get_size(ImageCtx *ictx, uint64_t *size)
+ {
+ int r = ictx->state->refresh_if_required();
+ if (r < 0)
+ return r;
+ RWLock::RLocker l2(ictx->snap_lock);
+ *size = ictx->get_image_size(ictx->snap_id);
+ return 0;
+ }
+
+ int get_features(ImageCtx *ictx, uint64_t *features)
+ {
+ int r = ictx->state->refresh_if_required();
+ if (r < 0)
+ return r;
+ RWLock::RLocker l(ictx->snap_lock);
+ *features = ictx->features;
+ return 0;
+ }
+
+ int get_overlap(ImageCtx *ictx, uint64_t *overlap)
+ {
+ int r = ictx->state->refresh_if_required();
+ if (r < 0)
+ return r;
+ RWLock::RLocker l(ictx->snap_lock);
+ RWLock::RLocker l2(ictx->parent_lock);
+ return ictx->get_parent_overlap(ictx->snap_id, overlap);
+ }
+
+ int get_flags(ImageCtx *ictx, uint64_t *flags)
+ {
+ int r = ictx->state->refresh_if_required();
+ if (r < 0) {
+ return r;
+ }
+
+ RWLock::RLocker l2(ictx->snap_lock);
+ return ictx->get_flags(ictx->snap_id, flags);
+ }
+
+ int set_image_notification(ImageCtx *ictx, int fd, int type)
+ {
+ CephContext *cct = ictx->cct;
+ ldout(cct, 20) << __func__ << " " << ictx << " fd " << fd << " type" << type << dendl;
+
+ int r = ictx->state->refresh_if_required();
+ if (r < 0) {
+ return r;
+ }
+
+ if (ictx->event_socket.is_valid())
+ return -EINVAL;
+ return ictx->event_socket.init(fd, type);
+ }
+
+ int is_exclusive_lock_owner(ImageCtx *ictx, bool *is_owner)
+ {
+ CephContext *cct = ictx->cct;
+ ldout(cct, 20) << __func__ << ": ictx=" << ictx << dendl;
+ *is_owner = false;
+
+ RWLock::RLocker owner_locker(ictx->owner_lock);
+ if (ictx->exclusive_lock == nullptr) {
+ return 0;
+ }
+
+ // might have been blacklisted by peer -- ensure we still own
+ // the lock by pinging the OSD
+ int r = ictx->exclusive_lock->assert_header_locked();
+ if (r == -EBUSY || r == -ENOENT) {
+ return 0;
+ } else if (r < 0) {
+ return r;
+ }
+
+ *is_owner = true;
+ return 0;
+ }
+
+ int lock_acquire(ImageCtx *ictx, rbd_lock_mode_t lock_mode)
+ {
+ CephContext *cct = ictx->cct;
+ ldout(cct, 20) << __func__ << ": ictx=" << ictx << ", "
+ << "lock_mode=" << lock_mode << dendl;
+
+ if (lock_mode != RBD_LOCK_MODE_EXCLUSIVE) {
+ return -EOPNOTSUPP;
+ }
+
+ C_SaferCond lock_ctx;
+ {
+ RWLock::WLocker l(ictx->owner_lock);
+
+ if (ictx->exclusive_lock == nullptr) {
+ lderr(cct) << "exclusive-lock feature is not enabled" << dendl;
+ return -EINVAL;
+ }
+
+ if (ictx->get_exclusive_lock_policy()->may_auto_request_lock()) {
+ ictx->set_exclusive_lock_policy(
+ new exclusive_lock::StandardPolicy(ictx));
+ }
+
+ if (ictx->exclusive_lock->is_lock_owner()) {
+ return 0;
+ }
+
+ ictx->exclusive_lock->acquire_lock(&lock_ctx);
+ }
+
+ int r = lock_ctx.wait();
+ if (r < 0) {
+ lderr(cct) << "failed to request exclusive lock: " << cpp_strerror(r)
+ << dendl;
+ return r;
+ }
+
+ RWLock::RLocker l(ictx->owner_lock);
+ if (ictx->exclusive_lock == nullptr) {
+ return -EINVAL;
+ } else if (!ictx->exclusive_lock->is_lock_owner()) {
+ lderr(cct) << "failed to acquire exclusive lock" << dendl;
+ return ictx->exclusive_lock->get_unlocked_op_error();
+ }
+
+ return 0;
+ }
+
+ int lock_release(ImageCtx *ictx)
+ {
+ CephContext *cct = ictx->cct;
+ ldout(cct, 20) << __func__ << ": ictx=" << ictx << dendl;
+
+ C_SaferCond lock_ctx;
+ {
+ RWLock::WLocker l(ictx->owner_lock);
+
+ if (ictx->exclusive_lock == nullptr ||
+ !ictx->exclusive_lock->is_lock_owner()) {
+ lderr(cct) << "not exclusive lock owner" << dendl;
+ return -EINVAL;
+ }
+
+ ictx->exclusive_lock->release_lock(&lock_ctx);
+ }
+
+ int r = lock_ctx.wait();
+ if (r < 0) {
+ lderr(cct) << "failed to release exclusive lock: " << cpp_strerror(r)
+ << dendl;
+ return r;
+ }
+ return 0;
+ }
+
+ int lock_get_owners(ImageCtx *ictx, rbd_lock_mode_t *lock_mode,
+ std::list<std::string> *lock_owners)
+ {
+ CephContext *cct = ictx->cct;
+ ldout(cct, 20) << __func__ << ": ictx=" << ictx << dendl;
+
+ if (!ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ lderr(cct) << "exclusive-lock feature is not enabled" << dendl;
+ return -EINVAL;
+ }
+
+ managed_lock::Locker locker;
+ C_SaferCond get_owner_ctx;
+ ExclusiveLock<>(*ictx).get_locker(&locker, &get_owner_ctx);
+ int r = get_owner_ctx.wait();
+ if (r == -ENOENT) {
+ return r;
+ } else if (r < 0) {
+ lderr(cct) << "failed to determine current lock owner: "
+ << cpp_strerror(r) << dendl;
+ return r;
+ }
+
+ *lock_mode = RBD_LOCK_MODE_EXCLUSIVE;
+ lock_owners->clear();
+ lock_owners->emplace_back(locker.address);
+ return 0;
+ }
+
+ int lock_break(ImageCtx *ictx, rbd_lock_mode_t lock_mode,
+ const std::string &lock_owner) {
+ CephContext *cct = ictx->cct;
+ ldout(cct, 20) << __func__ << ": ictx=" << ictx << ", "
+ << "lock_mode=" << lock_mode << ", "
+ << "lock_owner=" << lock_owner << dendl;
+
+ if (lock_mode != RBD_LOCK_MODE_EXCLUSIVE) {
+ return -EOPNOTSUPP;
+ }
+
+ if (ictx->read_only) {
+ return -EROFS;
+ }
+
+ managed_lock::Locker locker;
+ C_SaferCond get_owner_ctx;
+ {
+ RWLock::RLocker l(ictx->owner_lock);
+
+ if (ictx->exclusive_lock == nullptr) {
+ lderr(cct) << "exclusive-lock feature is not enabled" << dendl;
+ return -EINVAL;
+ }
+
+ ictx->exclusive_lock->get_locker(&locker, &get_owner_ctx);
+ }
+ int r = get_owner_ctx.wait();
+ if (r == -ENOENT) {
+ return r;
+ } else if (r < 0) {
+ lderr(cct) << "failed to determine current lock owner: "
+ << cpp_strerror(r) << dendl;
+ return r;
+ }
+
+ if (locker.address != lock_owner) {
+ return -EBUSY;
+ }
+
+ C_SaferCond break_ctx;
+ {
+ RWLock::RLocker l(ictx->owner_lock);
+
+ if (ictx->exclusive_lock == nullptr) {
+ lderr(cct) << "exclusive-lock feature is not enabled" << dendl;
+ return -EINVAL;
+ }
+
+ ictx->exclusive_lock->break_lock(locker, true, &break_ctx);
+ }
+ r = break_ctx.wait();
+ if (r == -ENOENT) {
+ return r;
+ } else if (r < 0) {
+ lderr(cct) << "failed to break lock: " << cpp_strerror(r) << dendl;
+ return r;
+ }
+ return 0;
+ }
+
+ int snap_list(ImageCtx *ictx, vector<snap_info_t>& snaps)
+ {
+ ldout(ictx->cct, 20) << "snap_list " << ictx << dendl;
+
+ int r = ictx->state->refresh_if_required();
+ if (r < 0)
+ return r;
+
+ RWLock::RLocker l(ictx->snap_lock);
+ for (map<snap_t, SnapInfo>::iterator it = ictx->snap_info.begin();
+ it != ictx->snap_info.end(); ++it) {
+ snap_info_t info;
+ info.name = it->second.name;
+ info.id = it->first;
+ info.size = it->second.size;
+ snaps.push_back(info);
+ }
+
+ return 0;
+ }
+
+ int snap_exists(ImageCtx *ictx, const cls::rbd::SnapshotNamespace& snap_namespace,
+ const char *snap_name, bool *exists)
+ {
+ ldout(ictx->cct, 20) << "snap_exists " << ictx << " " << snap_name << dendl;
+
+ int r = ictx->state->refresh_if_required();
+ if (r < 0)
+ return r;
+
+ RWLock::RLocker l(ictx->snap_lock);
+ *exists = ictx->get_snap_id(snap_namespace, snap_name) != CEPH_NOSNAP;
+ return 0;
+ }
+
+ int snap_remove(ImageCtx *ictx, const char *snap_name, uint32_t flags,
+ ProgressContext& pctx)
+ {
+ ldout(ictx->cct, 20) << "snap_remove " << ictx << " " << snap_name << " flags: " << flags << dendl;
+
+ int r = 0;
+
+ r = ictx->state->refresh_if_required();
+ if (r < 0)
+ return r;
+
+ if (flags & RBD_SNAP_REMOVE_FLATTEN) {
+ r = flatten_children(ictx, snap_name, pctx);
+ if (r < 0) {
+ return r;
+ }
+ }
+
+ bool is_protected;
+ r = snap_is_protected(ictx, snap_name, &is_protected);
+ if (r < 0) {
+ return r;
+ }
+
+ if (is_protected && flags & RBD_SNAP_REMOVE_UNPROTECT) {
+ r = ictx->operations->snap_unprotect(cls::rbd::UserSnapshotNamespace(), snap_name);
+ if (r < 0) {
+ lderr(ictx->cct) << "failed to unprotect snapshot: " << snap_name << dendl;
+ return r;
+ }
+
+ r = snap_is_protected(ictx, snap_name, &is_protected);
+ if (r < 0) {
+ return r;
+ }
+ if (is_protected) {
+ lderr(ictx->cct) << "snapshot is still protected after unprotection" << dendl;
+ ceph_abort();
+ }
+ }
+
+ C_SaferCond ctx;
+ ictx->operations->snap_remove(cls::rbd::UserSnapshotNamespace(), snap_name, &ctx);
+
+ r = ctx.wait();
+ return r;
+ }
+
+ int snap_get_timestamp(ImageCtx *ictx, uint64_t snap_id, struct timespec *timestamp)
+ {
+ std::map<librados::snap_t, SnapInfo>::iterator snap_it = ictx->snap_info.find(snap_id);
+ ceph_assert(snap_it != ictx->snap_info.end());
+ utime_t time = snap_it->second.timestamp;
+ time.to_timespec(timestamp);
+ return 0;
+ }
+
+ int snap_get_limit(ImageCtx *ictx, uint64_t *limit)
+ {
+ int r = cls_client::snapshot_get_limit(&ictx->md_ctx, ictx->header_oid,
+ limit);
+ if (r == -EOPNOTSUPP) {
+ *limit = UINT64_MAX;
+ r = 0;
+ }
+ return r;
+ }
+
+ int snap_set_limit(ImageCtx *ictx, uint64_t limit)
+ {
+ return ictx->operations->snap_set_limit(limit);
+ }
+
+ struct CopyProgressCtx {
+ explicit CopyProgressCtx(ProgressContext &p)
+ : destictx(NULL), src_size(0), prog_ctx(p)
+ { }
+
+ ImageCtx *destictx;
+ uint64_t src_size;
+ ProgressContext &prog_ctx;
+ };
+
+ int copy(ImageCtx *src, IoCtx& dest_md_ctx, const char *destname,
+ ImageOptions& opts, ProgressContext &prog_ctx, size_t sparse_size)
+ {
+ CephContext *cct = (CephContext *)dest_md_ctx.cct();
+ uint64_t option;
+ if (opts.get(RBD_IMAGE_OPTION_FLATTEN, &option) == 0) {
+ lderr(cct) << "copy does not support 'flatten' image option" << dendl;
+ return -EINVAL;
+ }
+ if (opts.get(RBD_IMAGE_OPTION_CLONE_FORMAT, &option) == 0) {
+ lderr(cct) << "copy does not support 'clone_format' image option"
+ << dendl;
+ return -EINVAL;
+ }
+
+ ldout(cct, 20) << "copy " << src->name
+ << (src->snap_name.length() ? "@" + src->snap_name : "")
+ << " -> " << destname << " opts = " << opts << dendl;
+
+ src->snap_lock.get_read();
+ uint64_t features = src->features;
+ uint64_t src_size = src->get_image_size(src->snap_id);
+ src->snap_lock.put_read();
+ uint64_t format = 2;
+ if (opts.get(RBD_IMAGE_OPTION_FORMAT, &format) != 0) {
+ opts.set(RBD_IMAGE_OPTION_FORMAT, format);
+ }
+ uint64_t stripe_unit = src->stripe_unit;
+ if (opts.get(RBD_IMAGE_OPTION_STRIPE_UNIT, &stripe_unit) != 0) {
+ opts.set(RBD_IMAGE_OPTION_STRIPE_UNIT, stripe_unit);
+ }
+ uint64_t stripe_count = src->stripe_count;
+ if (opts.get(RBD_IMAGE_OPTION_STRIPE_COUNT, &stripe_count) != 0) {
+ opts.set(RBD_IMAGE_OPTION_STRIPE_COUNT, stripe_count);
+ }
+ uint64_t order = src->order;
+ if (opts.get(RBD_IMAGE_OPTION_ORDER, &order) != 0) {
+ opts.set(RBD_IMAGE_OPTION_ORDER, order);
+ }
+ if (opts.get(RBD_IMAGE_OPTION_FEATURES, &features) != 0) {
+ opts.set(RBD_IMAGE_OPTION_FEATURES, features);
+ }
+ if (features & ~RBD_FEATURES_ALL) {
+ lderr(cct) << "librbd does not support requested features" << dendl;
+ return -ENOSYS;
+ }
+
+ int r = create(dest_md_ctx, destname, "", src_size, opts, "", "", false);
+ if (r < 0) {
+ lderr(cct) << "header creation failed" << dendl;
+ return r;
+ }
+ opts.set(RBD_IMAGE_OPTION_ORDER, static_cast<uint64_t>(order));
+
+ ImageCtx *dest = new librbd::ImageCtx(destname, "", nullptr, dest_md_ctx,
+ false);
+ r = dest->state->open(0);
+ if (r < 0) {
+ lderr(cct) << "failed to read newly created header" << dendl;
+ return r;
+ }
+
+ r = copy(src, dest, prog_ctx, sparse_size);
+
+ int close_r = dest->state->close();
+ if (r == 0 && close_r < 0) {
+ r = close_r;
+ }
+ return r;
+ }
+
+ class C_CopyWrite : public Context {
+ public:
+ C_CopyWrite(bufferlist *bl, Context* ctx)
+ : m_bl(bl), m_ctx(ctx) {}
+ void finish(int r) override {
+ delete m_bl;
+ m_ctx->complete(r);
+ }
+ private:
+ bufferlist *m_bl;
+ Context *m_ctx;
+ };
+
+ class C_CopyRead : public Context {
+ public:
+ C_CopyRead(SimpleThrottle *throttle, ImageCtx *dest, uint64_t offset,
+ bufferlist *bl, size_t sparse_size)
+ : m_throttle(throttle), m_dest(dest), m_offset(offset), m_bl(bl),
+ m_sparse_size(sparse_size) {
+ m_throttle->start_op();
+ }
+ void finish(int r) override {
+ if (r < 0) {
+ lderr(m_dest->cct) << "error reading from source image at offset "
+ << m_offset << ": " << cpp_strerror(r) << dendl;
+ delete m_bl;
+ m_throttle->end_op(r);
+ return;
+ }
+ ceph_assert(m_bl->length() == (size_t)r);
+
+ if (m_bl->is_zero()) {
+ delete m_bl;
+ m_throttle->end_op(r);
+ return;
+ }
+
+ if (!m_sparse_size) {
+ m_sparse_size = (1 << m_dest->order);
+ }
+
+ auto *throttle = m_throttle;
+ auto *end_op_ctx = new FunctionContext([throttle](int r) {
+ throttle->end_op(r);
+ });
+ auto gather_ctx = new C_Gather(m_dest->cct, end_op_ctx);
+
+ m_bl->rebuild(buffer::ptr_node::create(m_bl->length()));
+ size_t write_offset = 0;
+ size_t write_length = 0;
+ size_t offset = 0;
+ size_t length = m_bl->length();
+ const auto& m_ptr = m_bl->front();
+ while (offset < length) {
+ if (util::calc_sparse_extent(m_ptr,
+ m_sparse_size,
+ length,
+ &write_offset,
+ &write_length,
+ &offset)) {
+ bufferlist *write_bl = new bufferlist();
+ write_bl->push_back(
+ buffer::ptr_node::create(m_ptr, write_offset, write_length));
+ Context *ctx = new C_CopyWrite(write_bl, gather_ctx->new_sub());
+ auto comp = io::AioCompletion::create(ctx);
+
+ // coordinate through AIO WQ to ensure lock is acquired if needed
+ m_dest->io_work_queue->aio_write(comp, m_offset + write_offset,
+ write_length,
+ std::move(*write_bl),
+ LIBRADOS_OP_FLAG_FADVISE_DONTNEED,
+ std::move(read_trace));
+ write_offset = offset;
+ write_length = 0;
+ }
+ }
+ delete m_bl;
+ ceph_assert(gather_ctx->get_sub_created_count() > 0);
+ gather_ctx->activate();
+ }
+
+ ZTracer::Trace read_trace;
+
+ private:
+ SimpleThrottle *m_throttle;
+ ImageCtx *m_dest;
+ uint64_t m_offset;
+ bufferlist *m_bl;
+ size_t m_sparse_size;
+ };
+
+ int copy(ImageCtx *src, ImageCtx *dest, ProgressContext &prog_ctx, size_t sparse_size)
+ {
+ src->snap_lock.get_read();
+ uint64_t src_size = src->get_image_size(src->snap_id);
+ src->snap_lock.put_read();
+
+ dest->snap_lock.get_read();
+ uint64_t dest_size = dest->get_image_size(dest->snap_id);
+ dest->snap_lock.put_read();
+
+ CephContext *cct = src->cct;
+ if (dest_size < src_size) {
+ lderr(cct) << " src size " << src_size << " > dest size "
+ << dest_size << dendl;
+ return -EINVAL;
+ }
+ int r;
+ const uint32_t MAX_KEYS = 64;
+ map<string, bufferlist> pairs;
+ std::string last_key = "";
+ bool more_results = true;
+
+ while (more_results) {
+ r = cls_client::metadata_list(&src->md_ctx, src->header_oid, last_key, 0, &pairs);
+ if (r < 0 && r != -EOPNOTSUPP && r != -EIO) {
+ lderr(cct) << "couldn't list metadata: " << cpp_strerror(r) << dendl;
+ return r;
+ } else if (r == 0 && !pairs.empty()) {
+ r = cls_client::metadata_set(&dest->md_ctx, dest->header_oid, pairs);
+ if (r < 0) {
+ lderr(cct) << "couldn't set metadata: " << cpp_strerror(r) << dendl;
+ return r;
+ }
+
+ last_key = pairs.rbegin()->first;
+ }
+
+ more_results = (pairs.size() == MAX_KEYS);
+ pairs.clear();
+ }
+
+ ZTracer::Trace trace;
+ if (src->blkin_trace_all) {
+ trace.init("copy", &src->trace_endpoint);
+ }
+
+ RWLock::RLocker owner_lock(src->owner_lock);
+ SimpleThrottle throttle(src->config.get_val<uint64_t>("rbd_concurrent_management_ops"), false);
+ uint64_t period = src->get_stripe_period();
+ unsigned fadvise_flags = LIBRADOS_OP_FLAG_FADVISE_SEQUENTIAL |
+ LIBRADOS_OP_FLAG_FADVISE_NOCACHE;
+ uint64_t object_id = 0;
+ for (uint64_t offset = 0; offset < src_size; offset += period) {
+ if (throttle.pending_error()) {
+ return throttle.wait_for_ret();
+ }
+
+ {
+ RWLock::RLocker snap_locker(src->snap_lock);
+ if (src->object_map != nullptr) {
+ bool skip = true;
+ // each period is related to src->stripe_count objects, check them all
+ for (uint64_t i=0; i < src->stripe_count; i++) {
+ if (object_id < src->object_map->size() &&
+ src->object_map->object_may_exist(object_id)) {
+ skip = false;
+ }
+ ++object_id;
+ }
+
+ if (skip) continue;
+ } else {
+ object_id += src->stripe_count;
+ }
+ }
+
+ uint64_t len = min(period, src_size - offset);
+ bufferlist *bl = new bufferlist();
+ auto ctx = new C_CopyRead(&throttle, dest, offset, bl, sparse_size);
+ auto comp = io::AioCompletion::create_and_start<Context>(
+ ctx, src, io::AIO_TYPE_READ);
+
+ io::ImageReadRequest<> req(*src, comp, {{offset, len}},
+ io::ReadResult{bl}, fadvise_flags,
+ std::move(trace));
+ ctx->read_trace = req.get_trace();
+
+ req.send();
+ prog_ctx.update_progress(offset, src_size);
+ }
+
+ r = throttle.wait_for_ret();
+ if (r >= 0)
+ prog_ctx.update_progress(src_size, src_size);
+ return r;
+ }
+
+ int list_lockers(ImageCtx *ictx,
+ std::list<locker_t> *lockers,
+ bool *exclusive,
+ string *tag)
+ {
+ ldout(ictx->cct, 20) << "list_locks on image " << ictx << dendl;
+
+ int r = ictx->state->refresh_if_required();
+ if (r < 0)
+ return r;
+
+ RWLock::RLocker locker(ictx->md_lock);
+ if (exclusive)
+ *exclusive = ictx->exclusive_locked;
+ if (tag)
+ *tag = ictx->lock_tag;
+ if (lockers) {
+ lockers->clear();
+ map<rados::cls::lock::locker_id_t,
+ rados::cls::lock::locker_info_t>::const_iterator it;
+ for (it = ictx->lockers.begin(); it != ictx->lockers.end(); ++it) {
+ locker_t locker;
+ locker.client = stringify(it->first.locker);
+ locker.cookie = it->first.cookie;
+ locker.address = it->second.addr.get_legacy_str();
+ lockers->push_back(locker);
+ }
+ }
+
+ return 0;
+ }
+
+ int lock(ImageCtx *ictx, bool exclusive, const string& cookie,
+ const string& tag)
+ {
+ ldout(ictx->cct, 20) << "lock image " << ictx << " exclusive=" << exclusive
+ << " cookie='" << cookie << "' tag='" << tag << "'"
+ << dendl;
+
+ int r = ictx->state->refresh_if_required();
+ if (r < 0)
+ return r;
+
+ /**
+ * If we wanted we could do something more intelligent, like local
+ * checks that we think we will succeed. But for now, let's not
+ * duplicate that code.
+ */
+ {
+ RWLock::RLocker locker(ictx->md_lock);
+ r = rados::cls::lock::lock(&ictx->md_ctx, ictx->header_oid, RBD_LOCK_NAME,
+ exclusive ? LOCK_EXCLUSIVE : LOCK_SHARED,
+ cookie, tag, "", utime_t(), 0);
+ if (r < 0) {
+ return r;
+ }
+ }
+
+ ictx->notify_update();
+ return 0;
+ }
+
+ int unlock(ImageCtx *ictx, const string& cookie)
+ {
+ ldout(ictx->cct, 20) << "unlock image " << ictx
+ << " cookie='" << cookie << "'" << dendl;
+
+ int r = ictx->state->refresh_if_required();
+ if (r < 0)
+ return r;
+
+ {
+ RWLock::RLocker locker(ictx->md_lock);
+ r = rados::cls::lock::unlock(&ictx->md_ctx, ictx->header_oid,
+ RBD_LOCK_NAME, cookie);
+ if (r < 0) {
+ return r;
+ }
+ }
+
+ ictx->notify_update();
+ return 0;
+ }
+
+ int break_lock(ImageCtx *ictx, const string& client,
+ const string& cookie)
+ {
+ ldout(ictx->cct, 20) << "break_lock image " << ictx << " client='" << client
+ << "' cookie='" << cookie << "'" << dendl;
+
+ int r = ictx->state->refresh_if_required();
+ if (r < 0)
+ return r;
+
+ entity_name_t lock_client;
+ if (!lock_client.parse(client)) {
+ lderr(ictx->cct) << "Unable to parse client '" << client
+ << "'" << dendl;
+ return -EINVAL;
+ }
+
+ if (ictx->config.get_val<bool>("rbd_blacklist_on_break_lock")) {
+ typedef std::map<rados::cls::lock::locker_id_t,
+ rados::cls::lock::locker_info_t> Lockers;
+ Lockers lockers;
+ ClsLockType lock_type;
+ std::string lock_tag;
+ r = rados::cls::lock::get_lock_info(&ictx->md_ctx, ictx->header_oid,
+ RBD_LOCK_NAME, &lockers, &lock_type,
+ &lock_tag);
+ if (r < 0) {
+ lderr(ictx->cct) << "unable to retrieve lock info: " << cpp_strerror(r)
+ << dendl;
+ return r;
+ }
+
+ std::string client_address;
+ for (Lockers::iterator it = lockers.begin();
+ it != lockers.end(); ++it) {
+ if (it->first.locker == lock_client) {
+ client_address = it->second.addr.get_legacy_str();
+ break;
+ }
+ }
+ if (client_address.empty()) {
+ return -ENOENT;
+ }
+
+ RWLock::RLocker locker(ictx->md_lock);
+ librados::Rados rados(ictx->md_ctx);
+ r = rados.blacklist_add(
+ client_address,
+ ictx->config.get_val<uint64_t>("rbd_blacklist_expire_seconds"));
+ if (r < 0) {
+ lderr(ictx->cct) << "unable to blacklist client: " << cpp_strerror(r)
+ << dendl;
+ return r;
+ }
+ }
+
+ r = rados::cls::lock::break_lock(&ictx->md_ctx, ictx->header_oid,
+ RBD_LOCK_NAME, cookie, lock_client);
+ if (r < 0)
+ return r;
+ ictx->notify_update();
+ return 0;
+ }
+
+ void rbd_ctx_cb(completion_t cb, void *arg)
+ {
+ Context *ctx = reinterpret_cast<Context *>(arg);
+ auto comp = reinterpret_cast<io::AioCompletion *>(cb);
+ ctx->complete(comp->get_return_value());
+ comp->release();
+ }
+
+ int64_t read_iterate(ImageCtx *ictx, uint64_t off, uint64_t len,
+ int (*cb)(uint64_t, size_t, const char *, void *),
+ void *arg)
+ {
+ coarse_mono_time start_time;
+ ceph::timespan elapsed;
+
+ ldout(ictx->cct, 20) << "read_iterate " << ictx << " off = " << off
+ << " len = " << len << dendl;
+
+ int r = ictx->state->refresh_if_required();
+ if (r < 0)
+ return r;
+
+ uint64_t mylen = len;
+ ictx->snap_lock.get_read();
+ r = clip_io(ictx, off, &mylen);
+ ictx->snap_lock.put_read();
+ if (r < 0)
+ return r;
+
+ int64_t total_read = 0;
+ uint64_t period = ictx->get_stripe_period();
+ uint64_t left = mylen;
+
+ ZTracer::Trace trace;
+ if (ictx->blkin_trace_all) {
+ trace.init("read_iterate", &ictx->trace_endpoint);
+ }
+
+ RWLock::RLocker owner_locker(ictx->owner_lock);
+ start_time = coarse_mono_clock::now();
+ while (left > 0) {
+ uint64_t period_off = off - (off % period);
+ uint64_t read_len = min(period_off + period - off, left);
+
+ bufferlist bl;
+
+ C_SaferCond ctx;
+ auto c = io::AioCompletion::create_and_start(&ctx, ictx,
+ io::AIO_TYPE_READ);
+ io::ImageRequest<>::aio_read(ictx, c, {{off, read_len}},
+ io::ReadResult{&bl}, 0, std::move(trace));
+
+ int ret = ctx.wait();
+ if (ret < 0) {
+ return ret;
+ }
+
+ r = cb(total_read, ret, bl.c_str(), arg);
+ if (r < 0) {
+ return r;
+ }
+
+ total_read += ret;
+ left -= ret;
+ off += ret;
+ }
+
+ elapsed = coarse_mono_clock::now() - start_time;
+ ictx->perfcounter->tinc(l_librbd_rd_latency, elapsed);
+ ictx->perfcounter->inc(l_librbd_rd);
+ ictx->perfcounter->inc(l_librbd_rd_bytes, mylen);
+ return total_read;
+ }
+
+ // validate extent against image size; clip to image size if necessary
+ int clip_io(ImageCtx *ictx, uint64_t off, uint64_t *len)
+ {
+ ceph_assert(ictx->snap_lock.is_locked());
+ uint64_t image_size = ictx->get_image_size(ictx->snap_id);
+ bool snap_exists = ictx->snap_exists;
+
+ if (!snap_exists)
+ return -ENOENT;
+
+ // special-case "len == 0" requests: always valid
+ if (*len == 0)
+ return 0;
+
+ // can't start past end
+ if (off >= image_size)
+ return -EINVAL;
+
+ // clip requests that extend past end to just end
+ if ((off + *len) > image_size)
+ *len = (size_t)(image_size - off);
+
+ return 0;
+ }
+
+ int invalidate_cache(ImageCtx *ictx)
+ {
+ CephContext *cct = ictx->cct;
+ ldout(cct, 20) << "invalidate_cache " << ictx << dendl;
+
+ int r = ictx->state->refresh_if_required();
+ if (r < 0) {
+ return r;
+ }
+
+ C_SaferCond ctx;
+ {
+ RWLock::RLocker owner_locker(ictx->owner_lock);
+ ictx->io_object_dispatcher->invalidate_cache(&ctx);
+ }
+ r = ctx.wait();
+ ictx->perfcounter->inc(l_librbd_invalidate_cache);
+ return r;
+ }
+
+ int poll_io_events(ImageCtx *ictx, io::AioCompletion **comps, int numcomp)
+ {
+ if (numcomp <= 0)
+ return -EINVAL;
+ CephContext *cct = ictx->cct;
+ ldout(cct, 20) << __func__ << " " << ictx << " numcomp = " << numcomp
+ << dendl;
+ int i = 0;
+ Mutex::Locker l(ictx->completed_reqs_lock);
+ numcomp = std::min(numcomp, (int)ictx->completed_reqs.size());
+ while (i < numcomp) {
+ comps[i++] = ictx->completed_reqs.front();
+ ictx->completed_reqs.pop_front();
+ }
+ return i;
+ }
+
+ int metadata_get(ImageCtx *ictx, const string &key, string *value)
+ {
+ CephContext *cct = ictx->cct;
+ ldout(cct, 20) << "metadata_get " << ictx << " key=" << key << dendl;
+
+ int r = ictx->state->refresh_if_required();
+ if (r < 0) {
+ return r;
+ }
+
+ return cls_client::metadata_get(&ictx->md_ctx, ictx->header_oid, key, value);
+ }
+
+ int metadata_list(ImageCtx *ictx, const string &start, uint64_t max, map<string, bufferlist> *pairs)
+ {
+ CephContext *cct = ictx->cct;
+ ldout(cct, 20) << "metadata_list " << ictx << dendl;
+
+ int r = ictx->state->refresh_if_required();
+ if (r < 0) {
+ return r;
+ }
+
+ return cls_client::metadata_list(&ictx->md_ctx, ictx->header_oid, start, max, pairs);
+ }
+
+ struct C_RBD_Readahead : public Context {
+ ImageCtx *ictx;
+ object_t oid;
+ uint64_t offset;
+ uint64_t length;
+
+ bufferlist read_data;
+ io::ExtentMap extent_map;
+
+ C_RBD_Readahead(ImageCtx *ictx, object_t oid, uint64_t offset, uint64_t length)
+ : ictx(ictx), oid(oid), offset(offset), length(length) {
+ ictx->readahead.inc_pending();
+ }
+
+ void finish(int r) override {
+ ldout(ictx->cct, 20) << "C_RBD_Readahead on " << oid << ": "
+ << offset << "~" << length << dendl;
+ ictx->readahead.dec_pending();
+ }
+ };
+
+ void readahead(ImageCtx *ictx,
+ const vector<pair<uint64_t,uint64_t> >& image_extents)
+ {
+ uint64_t total_bytes = 0;
+ for (vector<pair<uint64_t,uint64_t> >::const_iterator p = image_extents.begin();
+ p != image_extents.end();
+ ++p) {
+ total_bytes += p->second;
+ }
+
+ ictx->md_lock.get_write();
+ bool abort = ictx->readahead_disable_after_bytes != 0 &&
+ ictx->total_bytes_read > ictx->readahead_disable_after_bytes;
+ if (abort) {
+ ictx->md_lock.put_write();
+ return;
+ }
+ ictx->total_bytes_read += total_bytes;
+ ictx->snap_lock.get_read();
+ uint64_t image_size = ictx->get_image_size(ictx->snap_id);
+ auto snap_id = ictx->snap_id;
+ ictx->snap_lock.put_read();
+ ictx->md_lock.put_write();
+
+ pair<uint64_t, uint64_t> readahead_extent = ictx->readahead.update(image_extents, image_size);
+ uint64_t readahead_offset = readahead_extent.first;
+ uint64_t readahead_length = readahead_extent.second;
+
+ if (readahead_length > 0) {
+ ldout(ictx->cct, 20) << "(readahead logical) " << readahead_offset << "~" << readahead_length << dendl;
+ map<object_t,vector<ObjectExtent> > readahead_object_extents;
+ Striper::file_to_extents(ictx->cct, ictx->format_string, &ictx->layout,
+ readahead_offset, readahead_length, 0, readahead_object_extents);
+ for (map<object_t,vector<ObjectExtent> >::iterator p = readahead_object_extents.begin(); p != readahead_object_extents.end(); ++p) {
+ for (vector<ObjectExtent>::iterator q = p->second.begin(); q != p->second.end(); ++q) {
+ ldout(ictx->cct, 20) << "(readahead) oid " << q->oid << " " << q->offset << "~" << q->length << dendl;
+
+ auto req_comp = new C_RBD_Readahead(ictx, q->oid, q->offset,
+ q->length);
+ auto req = io::ObjectDispatchSpec::create_read(
+ ictx, io::OBJECT_DISPATCH_LAYER_NONE, q->oid.name, q->objectno,
+ q->offset, q->length, snap_id, 0, {}, &req_comp->read_data,
+ &req_comp->extent_map, req_comp);
+ req->send();
+ }
+ }
+ ictx->perfcounter->inc(l_librbd_readahead);
+ ictx->perfcounter->inc(l_librbd_readahead_bytes, readahead_length);
+ }
+ }
+
+ int list_watchers(ImageCtx *ictx,
+ std::list<librbd::image_watcher_t> &watchers)
+ {
+ int r;
+ std::string header_oid;
+ std::list<obj_watch_t> obj_watchers;
+
+ if (ictx->old_format) {
+ header_oid = util::old_header_name(ictx->name);
+ } else {
+ header_oid = util::header_name(ictx->id);
+ }
+
+ r = ictx->md_ctx.list_watchers(header_oid, &obj_watchers);
+ if (r < 0) {
+ return r;
+ }
+
+ for (auto i = obj_watchers.begin(); i != obj_watchers.end(); ++i) {
+ librbd::image_watcher_t watcher;
+ watcher.addr = i->addr;
+ watcher.id = i->watcher_id;
+ watcher.cookie = i->cookie;
+
+ watchers.push_back(watcher);
+ }
+
+ return 0;
+ }
+
+}
+
+std::ostream &operator<<(std::ostream &os, const librbd::ImageOptions &opts) {
+ os << "[";
+
+ const char *delimiter = "";
+ for (auto &i : librbd::IMAGE_OPTIONS_TYPE_MAPPING) {
+ if (i.second == librbd::STR) {
+ std::string val;
+ if (opts.get(i.first, &val) == 0) {
+ os << delimiter << librbd::image_option_name(i.first) << "=" << val;
+ delimiter = ", ";
+ }
+ } else if (i.second == librbd::UINT64) {
+ uint64_t val;
+ if (opts.get(i.first, &val) == 0) {
+ os << delimiter << librbd::image_option_name(i.first) << "=" << val;
+ delimiter = ", ";
+ }
+ }
+ }
+
+ os << "]";
+
+ return os;
+}