ldout(m_image_ctx.cct, 10) << this << " remote rename request: "
<< payload.image_name << dendl;
- // TODO
- ::encode(ResponseMessage(-EOPNOTSUPP), *out);
+ C_SaferCond cond_ctx;
+ librbd::rename_helper(&m_image_ctx, &cond_ctx,
+ payload.image_name.c_str());
+
+ int r = cond_ctx.wait();
+ ::encode(ResponseMessage(r), *out);
}
}
librbd/object_map/UpdateRequest.cc \
librbd/operation/FlattenRequest.cc \
librbd/operation/RebuildObjectMapRequest.cc \
+ librbd/operation/RenameRequest.cc \
librbd/operation/Request.cc \
librbd/operation/ResizeRequest.cc \
librbd/operation/SnapshotCreateRequest.cc \
librbd/object_map/UpdateRequest.h \
librbd/operation/FlattenRequest.h \
librbd/operation/RebuildObjectMapRequest.h \
+ librbd/operation/RenameRequest.h \
librbd/operation/Request.h \
librbd/operation/ResizeRequest.h \
librbd/operation/SnapshotCreateRequest.h \
#include "librbd/parent_types.h"
#include "librbd/operation/FlattenRequest.h"
#include "librbd/operation/RebuildObjectMapRequest.h"
+#include "librbd/operation/RenameRequest.h"
#include "librbd/operation/ResizeRequest.h"
#include "librbd/operation/SnapshotCreateRequest.h"
#include "librbd/operation/SnapshotProtectRequest.h"
ldout(cct, 20) << "rename " << &io_ctx << " " << srcname << " -> "
<< dstname << dendl;
- bool old_format;
- uint64_t src_size;
- int r = detect_format(io_ctx, srcname, &old_format, &src_size);
+ ImageCtx *ictx = new ImageCtx(srcname, "", "", io_ctx, false);
+ int r = open_image(ictx);
if (r < 0) {
- lderr(cct) << "error finding source object: " << cpp_strerror(r) << dendl;
+ lderr(ictx->cct) << "error opening source image: " << cpp_strerror(r)
+ << dendl;
return r;
}
+ BOOST_SCOPE_EXIT((ictx)) {
+ close_image(ictx);
+ } BOOST_SCOPE_EXIT_END
r = detect_format(io_ctx, dstname, NULL, NULL);
if (r < 0 && r != -ENOENT) {
return -EEXIST;
}
- string src_oid =
- old_format ? old_header_name(srcname) : id_obj_name(srcname);
- string dst_oid =
- old_format ? old_header_name(dstname) : id_obj_name(dstname);
-
- string id;
- if (!old_format) {
- r = cls_client::get_id(&io_ctx, src_oid, &id);
- if (r < 0) {
- lderr(cct) << "error reading image id: " << cpp_strerror(r) << dendl;
- return r;
+ if (ictx->test_features(RBD_FEATURE_JOURNALING)) {
+ r = invoke_async_request(ictx, "rename", true,
+ boost::bind(&rename_helper, ictx, _1,
+ dstname),
+ boost::bind(&ImageWatcher::notify_rename,
+ ictx->image_watcher, dstname));
+ if (r < 0 && r != -EEXIST) {
+ return r;
}
- }
-
- bufferlist databl;
- map<string, bufferlist> omap_values;
- r = io_ctx.read(src_oid, databl, src_size, 0);
- if (r < 0) {
- lderr(cct) << "error reading source object: " << src_oid << ": "
- << cpp_strerror(r) << dendl;
- return r;
- }
+ } else {
+ RWLock::RLocker owner_lock(ictx->owner_lock);
+ C_SaferCond cond_ctx;
+ rename_helper(ictx, &cond_ctx, dstname);
- int MAX_READ = 1024;
- string last_read = "";
- do {
- map<string, bufferlist> outbl;
- r = io_ctx.omap_get_vals(src_oid, last_read, MAX_READ, &outbl);
+ r = cond_ctx.wait();
if (r < 0) {
- lderr(cct) << "error reading source object omap values: "
- << cpp_strerror(r) << dendl;
- return r;
+ return r;
}
- omap_values.insert(outbl.begin(), outbl.end());
- if (!outbl.empty())
- last_read = outbl.rbegin()->first;
- } while (r == MAX_READ);
-
- librados::ObjectWriteOperation op;
- op.create(true);
- op.write_full(databl);
- if (!omap_values.empty())
- op.omap_set(omap_values);
- r = io_ctx.operate(dst_oid, &op);
- if (r < 0) {
- lderr(cct) << "error writing destination object: " << dst_oid << ": "
- << cpp_strerror(r) << dendl;
- return r;
}
- if (old_format) {
- r = tmap_set(io_ctx, dstname);
- if (r < 0) {
- io_ctx.remove(dst_oid);
- lderr(cct) << "couldn't add " << dstname << " to directory: "
- << cpp_strerror(r) << dendl;
- return r;
- }
- r = tmap_rm(io_ctx, srcname);
- if (r < 0) {
- lderr(cct) << "warning: couldn't remove old entry from directory ("
- << srcname << ")" << dendl;
- }
- } else {
- r = cls_client::dir_rename_image(&io_ctx, RBD_DIRECTORY,
- srcname, dstname, id);
- if (r < 0) {
- lderr(cct) << "error updating directory: " << cpp_strerror(r) << dendl;
- return r;
- }
+ if (ictx->old_format) {
+ notify_change(ictx->md_ctx, ictx->header_oid, ictx);
}
+ return 0;
+ }
- r = io_ctx.remove(src_oid);
- if (r < 0 && r != -ENOENT) {
- lderr(cct) << "warning: couldn't remove old source object ("
- << src_oid << ")" << dendl;
+ void rename_helper(ImageCtx *ictx, Context *ctx, const char *dstname)
+ {
+ assert(ictx->owner_lock.is_locked());
+ if (ictx->test_features(RBD_FEATURE_JOURNALING)) {
+ assert(!ictx->image_watcher->is_lock_supported() ||
+ ictx->image_watcher->is_lock_owner());
}
- if (old_format) {
- notify_change(io_ctx, old_header_name(srcname), NULL);
+ ldout(ictx->cct, 20) << "rename_helper " << ictx << " " << dstname
+ << dendl;
+
+ int r = ictx_check(ictx, ictx->owner_lock);
+ if (r < 0) {
+ ctx->complete(r);
+ return;
}
- return 0;
+ operation::RenameRequest *req =
+ new operation::RenameRequest(*ictx, ctx, dstname);
+ req->send();
}
-
int info(ImageCtx *ictx, image_info_t& info, size_t infosize)
{
ldout(ictx->cct, 20) << "info " << ictx << dendl;
int clone(IoCtx& p_ioctx, const char *p_name, const char *p_snap_name,
IoCtx& c_ioctx, const char *c_name, ImageOptions& c_opts);
int rename(librados::IoCtx& io_ctx, const char *srcname, const char *dstname);
+ void rename_helper(ImageCtx *ictx, Context *ctx, const char *dstname);
int info(ImageCtx *ictx, image_info_t& info, size_t image_size);
int get_old_format(ImageCtx *ictx, uint8_t *old);
int get_size(ImageCtx *ictx, uint64_t *size);
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "librbd/operation/RenameRequest.h"
+#include "common/dout.h"
+#include "common/errno.h"
+#include "include/rados/librados.hpp"
+#include "librbd/ImageCtx.h"
+#include "librbd/internal.h"
+
+#define dout_subsys ceph_subsys_rbd
+#undef dout_prefix
+#define dout_prefix *_dout << "librbd::operation::RenameRequest: "
+
+namespace librbd {
+namespace operation {
+
+namespace {
+
+std::ostream& operator<<(std::ostream& os,
+ const RenameRequest::State& state) {
+ switch(state) {
+ case RenameRequest::STATE_READ_SOURCE_HEADER:
+ os << "READ_SOURCE_HEADER";
+ break;
+ case RenameRequest::STATE_WRITE_DEST_HEADER:
+ os << "WRITE_DEST_HEADER";
+ break;
+ case RenameRequest::STATE_UPDATE_DIRECTORY:
+ os << "UPDATE_DIRECTORY";
+ break;
+ case RenameRequest::STATE_REMOVE_SOURCE_HEADER:
+ os << "REMOVE_SOURCE_HEADER";
+ break;
+ default:
+ os << "UNKNOWN (" << static_cast<uint32_t>(state) << ")";
+ break;
+ }
+ return os;
+}
+
+} // anonymous namespace
+
+RenameRequest::RenameRequest(ImageCtx &image_ctx, Context *on_finish,
+ const std::string &dest_name)
+ : Request(image_ctx, on_finish), m_dest_name(dest_name),
+ m_source_oid(image_ctx.old_format ? old_header_name(image_ctx.name) :
+ id_obj_name(image_ctx.name)),
+ m_dest_oid(image_ctx.old_format ? old_header_name(dest_name) :
+ id_obj_name(dest_name)) {
+}
+
+void RenameRequest::send_op() {
+ send_read_source_header();
+}
+
+bool RenameRequest::should_complete(int r) {
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 5) << this << " " << __func__ << ": state=" << m_state << ", "
+ << "r=" << r << dendl;
+ r = filter_state_return_code(r);
+ if (r < 0) {
+ lderr(cct) << "encountered error: " << cpp_strerror(r) << dendl;
+ return true;
+ }
+
+ RWLock::RLocker owner_lock(m_image_ctx.owner_lock);
+ bool finished = false;
+ switch (m_state) {
+ case STATE_READ_SOURCE_HEADER:
+ send_write_destination_header();
+ break;
+ case STATE_WRITE_DEST_HEADER:
+ send_update_directory();
+ break;
+ case STATE_UPDATE_DIRECTORY:
+ send_remove_source_header();
+ break;
+ case STATE_REMOVE_SOURCE_HEADER:
+ finished = true;
+ break;
+ default:
+ assert(false);
+ break;
+ }
+ return finished;
+}
+
+int RenameRequest::filter_state_return_code(int r) {
+ CephContext *cct = m_image_ctx.cct;
+
+ if (m_state == STATE_REMOVE_SOURCE_HEADER && r < 0) {
+ if (r != -ENOENT) {
+ lderr(cct) << "warning: couldn't remove old source object ("
+ << m_source_oid << ")" << dendl;
+ }
+ return 0;
+ }
+ return r;
+}
+
+void RenameRequest::send_read_source_header() {
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 5) << this << " " << __func__ << dendl;
+ m_state = STATE_READ_SOURCE_HEADER;
+
+ librados::ObjectReadOperation op;
+ op.read(0, 0, NULL, NULL);
+
+ // TODO: old code read omap values but there are no omap values on the
+ // old format header nor the new format id object
+ librados::AioCompletion *rados_completion = create_callback_completion();
+ int r = m_image_ctx.md_ctx.aio_operate(m_source_oid, rados_completion, &op,
+ &m_header_bl);
+ assert(r == 0);
+ rados_completion->release();
+}
+
+void RenameRequest::send_write_destination_header() {
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 5) << this << " " << __func__ << dendl;
+ m_state = STATE_WRITE_DEST_HEADER;
+
+ librados::ObjectWriteOperation op;
+ op.create(true);
+ op.write_full(m_header_bl);
+
+ librados::AioCompletion *rados_completion = create_callback_completion();
+ int r = m_image_ctx.md_ctx.aio_operate(m_dest_oid, rados_completion, &op);
+ assert(r == 0);
+ rados_completion->release();
+}
+
+void RenameRequest::send_update_directory() {
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 5) << this << " " << __func__ << dendl;
+ m_state = STATE_UPDATE_DIRECTORY;
+
+ librados::ObjectWriteOperation op;
+ if (m_image_ctx.old_format) {
+ bufferlist cmd_bl;
+ bufferlist empty_bl;
+ ::encode(static_cast<__u8>(CEPH_OSD_TMAP_SET), cmd_bl);
+ ::encode(m_dest_name, cmd_bl);
+ ::encode(empty_bl, cmd_bl);
+ ::encode(static_cast<__u8>(CEPH_OSD_TMAP_RM), cmd_bl);
+ ::encode(m_image_ctx.name, cmd_bl);
+ op.tmap_update(cmd_bl);
+ } else {
+ cls_client::dir_rename_image(&op, m_image_ctx.name, m_dest_name,
+ m_image_ctx.id);
+ }
+
+ librados::AioCompletion *rados_completion = create_callback_completion();
+ int r = m_image_ctx.md_ctx.aio_operate(RBD_DIRECTORY, rados_completion, &op);
+ assert(r == 0);
+ rados_completion->release();
+}
+
+void RenameRequest::send_remove_source_header() {
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 5) << this << " " << __func__ << dendl;
+ m_state = STATE_REMOVE_SOURCE_HEADER;
+
+ librados::ObjectWriteOperation op;
+ op.remove();
+
+ librados::AioCompletion *rados_completion = create_callback_completion();
+ int r = m_image_ctx.md_ctx.aio_operate(m_source_oid, rados_completion, &op);
+ assert(r == 0);
+ rados_completion->release();
+}
+
+} // namespace operation
+} // namespace librbd
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_LIBRBD_RENAME_REQUEST_HH
+#define CEPH_LIBRBD_RENAME_REQUEST_H
+
+#include "librbd/operation/Request.h"
+#include <iosfwd>
+#include <string>
+
+class Context;
+
+namespace librbd {
+
+class ImageCtx;
+
+namespace operation {
+
+class RenameRequest : public Request
+{
+public:
+ /**
+ * Rename goes through the following state machine:
+ *
+ * @verbatim
+ *
+ * <start>
+ * |
+ * v
+ * STATE_READ_SOURCE_HEADER
+ * |
+ * v
+ * STATE_WRITE_DEST_HEADER
+ * |
+ * v
+ * STATE_UPDATE_DIRECTORY
+ * |
+ * v
+ * STATE_REMOVE_SOURCE_HEADER
+ * |
+ * v
+ * <finish>
+ *
+ * @endverbatim
+ *
+ */
+ enum State {
+ STATE_READ_SOURCE_HEADER,
+ STATE_WRITE_DEST_HEADER,
+ STATE_UPDATE_DIRECTORY,
+ STATE_REMOVE_SOURCE_HEADER
+ };
+
+ RenameRequest(ImageCtx &image_ctx, Context *on_finish,
+ const std::string &dest_name);
+
+protected:
+ virtual void send_op();
+ virtual bool should_complete(int r);
+
+private:
+ std::string m_dest_name;
+
+ std::string m_source_oid;
+ std::string m_dest_oid;
+
+ State m_state;
+
+ bufferlist m_header_bl;
+
+ int filter_state_return_code(int r);
+
+ void send_read_source_header();
+ void send_write_destination_header();
+ void send_update_directory();
+ void send_remove_source_header();
+
+};
+
+} // namespace operation
+} // namespace librbd
+
+#endif // CEPH_LIBRBD_RENAME_REQUEST_H