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_");
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_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",
return 0;
}
-
int trash_get(librados::IoCtx *ioctx, const std::string &id,
cls::rbd::TrashImageSpec *trash_spec)
{
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)
{
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,
o.push_back(new GroupSnapshot("1018643c9869", "groupsnapshot2", GROUP_SNAPSHOT_STATE_COMPLETE));
}
void TrashImageSpec::encode(bufferlist& bl) const {
- ENCODE_START(1, 1, bl);
+ 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(1, 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);
}
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,
static_cast<cls::rbd::TrashImageSource>(source), ictx->name,
delete_time, deferment_end_time};
+ trash_image_spec.state = cls::rbd::TRASH_IMAGE_STATE_MOVING;
C_SaferCond ctx;
auto req = trash::MoveRequest<>::create(io_ctx, ictx->id, trash_image_spec,
&ctx);
r = ctx.wait();
ictx->state->close();
+ trash_image_spec.state = cls::rbd::TRASH_IMAGE_STATE_NORMAL;
+ int ret = cls_client::trash_state_set(&io_ctx, image_id,
+ trash_image_spec.state,
+ cls::rbd::TRASH_IMAGE_STATE_MOVING);
+ if (ret < 0 && ret != -EOPNOTSUPP) {
+ lderr(cct) << "error setting trash image state: "
+ << cpp_strerror(ret) << dendl;
+ return ret;
+ }
if (r < 0) {
return r;
}
lderr(cct) << "error: deferment time has not expired." << dendl;
return -EPERM;
}
+ if (trash_spec.state != cls::rbd::TRASH_IMAGE_STATE_NORMAL &&
+ trash_spec.state != cls::rbd::TRASH_IMAGE_STATE_REMOVING) {
+ lderr(cct) << "error: image is pending restoration." << dendl;
+ return -EBUSY;
+ }
+
+ r = cls_client::trash_state_set(&io_ctx, image_id,
+ cls::rbd::TRASH_IMAGE_STATE_REMOVING,
+ cls::rbd::TRASH_IMAGE_STATE_NORMAL);
+ if (r < 0 && r != -EOPNOTSUPP) {
+ lderr(cct) << "error setting trash image state: "
+ << cpp_strerror(r) << dendl;
+ return r;
+ }
r = remove(io_ctx, "", image_id, prog_ctx, false, true);
if (r < 0) {
lderr(cct) << "error removing image " << image_id
<< ", which is pending deletion" << dendl;
+ int ret = cls_client::trash_state_set(&io_ctx, image_id,
+ cls::rbd::TRASH_IMAGE_STATE_NORMAL,
+ cls::rbd::TRASH_IMAGE_STATE_REMOVING);
+ if (ret < 0 && ret != -EOPNOTSUPP) {
+ lderr(cct) << "error setting trash image state: "
+ << cpp_strerror(ret) << dendl;
+ }
return r;
}
r = cls_client::trash_remove(&io_ctx, image_id);
}
std::string image_name = image_new_name;
+ if (trash_spec.state != cls::rbd::TRASH_IMAGE_STATE_NORMAL &&
+ trash_spec.state != cls::rbd::TRASH_IMAGE_STATE_RESTORING) {
+ lderr(cct) << "error restoring image id " << image_id
+ << ", which is pending deletion" << dendl;
+ return -EBUSY;
+ }
+ r = cls_client::trash_state_set(&io_ctx, image_id,
+ cls::rbd::TRASH_IMAGE_STATE_RESTORING,
+ cls::rbd::TRASH_IMAGE_STATE_NORMAL);
+ if (r < 0 && r != -EOPNOTSUPP) {
+ lderr(cct) << "error setting trash image state: "
+ << cpp_strerror(r) << dendl;
+ return r;
+ }
+
if (image_name.empty()) {
// if user didn't specify a new name, let's try using the old name
image_name = trash_spec.name;
if (r < 0 && r != -ENOENT) {
lderr(cct) << "error checking if image " << image_name << " exists: "
<< cpp_strerror(r) << dendl;
+ int ret = cls_client::trash_state_set(&io_ctx, image_id,
+ cls::rbd::TRASH_IMAGE_STATE_NORMAL,
+ cls::rbd::TRASH_IMAGE_STATE_RESTORING);
+ if (ret < 0 && ret != -EOPNOTSUPP) {
+ lderr(cct) << "error setting trash image state: "
+ << cpp_strerror(ret) << dendl;
+ }
return r;
} else if (r != -ENOENT){
// checking if we are recovering from an incomplete restore
if (existing_id != image_id) {
ldout(cct, 2) << "an image with the same name already exists" << dendl;
+ int r2 = cls_client::trash_state_set(&io_ctx, image_id,
+ cls::rbd::TRASH_IMAGE_STATE_NORMAL,
+ cls::rbd::TRASH_IMAGE_STATE_RESTORING);
+ if (r2 < 0 && r2 != -EOPNOTSUPP) {
+ lderr(cct) << "error setting trash image state: "
+ << cpp_strerror(r2) << dendl;
+ }
return -EEXIST;
}
create_id_obj = false;