cls_method_handle_t h_get_info;
cls_method_handle_t h_list_locks;
cls_method_handle_t h_assert_locked;
+cls_method_handle_t h_set_cookie;
#define LOCK_PREFIX "lock."
return 0;
}
+/**
+ * Update the cookie associated with an object lock
+ *
+ * Input:
+ * @param cls_lock_set_cookie_op request input
+ *
+ * Output:
+ * @param none
+ *
+ * @return 0 on success, -errno on failure.
+ */
+int set_cookie(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
+{
+ CLS_LOG(20, "set_cookie");
+
+ cls_lock_set_cookie_op op;
+ try {
+ bufferlist::iterator iter = in->begin();
+ ::decode(op, iter);
+ } catch (const buffer::error& err) {
+ return -EINVAL;
+ }
+
+ if (op.type != LOCK_EXCLUSIVE && op.type != LOCK_SHARED) {
+ return -EINVAL;
+ }
+
+ if (op.name.empty()) {
+ return -EINVAL;
+ }
+
+ // see if there's already a locker
+ lock_info_t linfo;
+ int r = read_lock(hctx, op.name, &linfo);
+ if (r < 0) {
+ CLS_ERR("Could not read lock info: %s", cpp_strerror(r).c_str());
+ return r;
+ }
+
+ if (linfo.lockers.empty()) {
+ CLS_LOG(20, "object not locked");
+ return -EBUSY;
+ }
+
+ if (linfo.lock_type != op.type) {
+ CLS_LOG(20, "lock type mismatch: current=%s, assert=%s",
+ cls_lock_type_str(linfo.lock_type), cls_lock_type_str(op.type));
+ return -EBUSY;
+ }
+
+ if (linfo.tag != op.tag) {
+ CLS_LOG(20, "lock tag mismatch: current=%s, assert=%s", linfo.tag.c_str(),
+ op.tag.c_str());
+ return -EBUSY;
+ }
+
+ entity_inst_t inst;
+ r = cls_get_request_origin(hctx, &inst);
+ assert(r == 0);
+
+ locker_id_t id;
+ id.cookie = op.cookie;
+ id.locker = inst.name;
+
+ map<locker_id_t, locker_info_t>::iterator iter = linfo.lockers.find(id);
+ if (iter == linfo.lockers.end()) {
+ CLS_LOG(20, "not locked by client");
+ return -EBUSY;
+ }
+
+ id.cookie = op.new_cookie;
+ if (linfo.lockers.count(id) != 0) {
+ CLS_LOG(20, "lock cookie in-use");
+ return -EBUSY;
+ }
+
+ locker_info_t locker_info(iter->second);
+ linfo.lockers.erase(iter);
+
+ linfo.lockers[id] = locker_info;
+ r = write_lock(hctx, op.name, linfo);
+ if (r < 0) {
+ CLS_ERR("Could not update lock info: %s", cpp_strerror(r).c_str());
+ return r;
+ }
+ return 0;
+}
+
void __cls_init()
{
CLS_LOG(20, "Loaded lock class!");
cls_register_cxx_method(h_class, "assert_locked",
CLS_METHOD_RD | CLS_METHOD_PROMOTE,
assert_locked, &h_assert_locked);
+ cls_register_cxx_method(h_class, "set_cookie",
+ CLS_METHOD_RD | CLS_METHOD_WR | CLS_METHOD_PROMOTE,
+ set_cookie, &h_set_cookie);
return;
}
rados_op->exec("lock", "assert_locked", in);
}
+ void set_cookie(librados::ObjectWriteOperation *rados_op,
+ const std::string& name, ClsLockType type,
+ const std::string& cookie, const std::string& tag,
+ const std::string& new_cookie)
+ {
+ cls_lock_set_cookie_op op;
+ op.name = name;
+ op.type = type;
+ op.cookie = cookie;
+ op.tag = tag;
+ op.new_cookie = new_cookie;
+ bufferlist in;
+ ::encode(op, in);
+ rados_op->exec("lock", "set_cookie", in);
+ }
+
void Lock::assert_locked_exclusive(ObjectOperation *op)
{
assert_locked(op, name, LOCK_EXCLUSIVE, cookie, tag);
const std::string& cookie,
const std::string& tag);
+ extern void set_cookie(librados::ObjectWriteOperation *rados_op,
+ const std::string& name, ClsLockType type,
+ const std::string& cookie, const std::string& tag,
+ const std::string& new_cookie);
+
class Lock {
std::string name;
std::string cookie;
o.push_back(new cls_lock_assert_op);
}
+void cls_lock_set_cookie_op::dump(Formatter *f) const
+{
+ f->dump_string("name", name);
+ f->dump_string("type", cls_lock_type_str(type));
+ f->dump_string("cookie", cookie);
+ f->dump_string("tag", tag);
+ f->dump_string("new_cookie", new_cookie);
+}
+
+void cls_lock_set_cookie_op::generate_test_instances(list<cls_lock_set_cookie_op*>& o)
+{
+ cls_lock_set_cookie_op *i = new cls_lock_set_cookie_op;
+ i->name = "name";
+ i->type = LOCK_SHARED;
+ i->cookie = "cookie";
+ i->tag = "tag";
+ i->new_cookie = "new cookie";
+ o.push_back(i);
+ o.push_back(new cls_lock_set_cookie_op);
+}
+
};
WRITE_CLASS_ENCODER(cls_lock_assert_op)
+struct cls_lock_set_cookie_op
+{
+ string name;
+ ClsLockType type;
+ string cookie;
+ string tag;
+ string new_cookie;
+
+ cls_lock_set_cookie_op() : type(LOCK_NONE) {}
+
+ void encode(bufferlist &bl) const {
+ ENCODE_START(1, 1, bl);
+ ::encode(name, bl);
+ uint8_t t = (uint8_t)type;
+ ::encode(t, bl);
+ ::encode(cookie, bl);
+ ::encode(tag, bl);
+ ::encode(new_cookie, bl);
+ ENCODE_FINISH(bl);
+ }
+ void decode(bufferlist::iterator &bl) {
+ DECODE_START_LEGACY_COMPAT_LEN(1, 1, 1, bl);
+ ::decode(name, bl);
+ uint8_t t;
+ ::decode(t, bl);
+ type = (ClsLockType)t;
+ ::decode(cookie, bl);
+ ::decode(tag, bl);
+ ::decode(new_cookie, bl);
+ DECODE_FINISH(bl);
+ }
+ void dump(Formatter *f) const;
+ static void generate_test_instances(list<cls_lock_set_cookie_op*>& o);
+};
+WRITE_CLASS_ENCODER(cls_lock_set_cookie_op)
+
#endif
ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster));
}
+
+TEST(ClsLock, TestSetCookie) {
+ Rados cluster;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+ IoCtx ioctx;
+ cluster.ioctx_create(pool_name.c_str(), ioctx);
+
+ string oid = "foo";
+ string name = "name";
+ string tag = "tag";
+ string cookie = "cookie";
+ string new_cookie = "new cookie";
+ librados::ObjectWriteOperation op1;
+ set_cookie(&op1, name, LOCK_SHARED, cookie, tag, new_cookie);
+ ASSERT_EQ(-ENOENT, ioctx.operate(oid, &op1));
+
+ librados::ObjectWriteOperation op2;
+ lock(&op2, name, LOCK_SHARED, cookie, tag, "", utime_t{}, 0);
+ ASSERT_EQ(0, ioctx.operate(oid, &op2));
+
+ librados::ObjectWriteOperation op3;
+ lock(&op3, name, LOCK_SHARED, "cookie 2", tag, "", utime_t{}, 0);
+ ASSERT_EQ(0, ioctx.operate(oid, &op3));
+
+ librados::ObjectWriteOperation op4;
+ set_cookie(&op4, name, LOCK_SHARED, cookie, tag, cookie);
+ ASSERT_EQ(-EBUSY, ioctx.operate(oid, &op4));
+
+ librados::ObjectWriteOperation op5;
+ set_cookie(&op5, name, LOCK_SHARED, cookie, "wrong tag", new_cookie);
+ ASSERT_EQ(-EBUSY, ioctx.operate(oid, &op5));
+
+ librados::ObjectWriteOperation op6;
+ set_cookie(&op6, name, LOCK_SHARED, "wrong cookie", tag, new_cookie);
+ ASSERT_EQ(-EBUSY, ioctx.operate(oid, &op6));
+
+ librados::ObjectWriteOperation op7;
+ set_cookie(&op7, name, LOCK_EXCLUSIVE, cookie, tag, new_cookie);
+ ASSERT_EQ(-EBUSY, ioctx.operate(oid, &op7));
+
+ librados::ObjectWriteOperation op8;
+ set_cookie(&op8, name, LOCK_SHARED, cookie, tag, "cookie 2");
+ ASSERT_EQ(-EBUSY, ioctx.operate(oid, &op8));
+
+ librados::ObjectWriteOperation op9;
+ set_cookie(&op9, name, LOCK_SHARED, cookie, tag, new_cookie);
+ ASSERT_EQ(0, ioctx.operate(oid, &op9));
+
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster));
+}
TYPE_FEATUREFUL(cls_lock_get_info_reply)
TYPE(cls_lock_list_locks_reply)
TYPE(cls_lock_assert_op)
+TYPE(cls_lock_set_cookie_op)
#include "cls/replica_log/cls_replica_log_types.h"
TYPE(cls_replica_log_item_marker)