summaryrefslogtreecommitdiffstats
path: root/src/cls/version
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/cls/version/cls_version.cc238
-rw-r--r--src/cls/version/cls_version_client.cc104
-rw-r--r--src/cls/version/cls_version_client.h32
-rw-r--r--src/cls/version/cls_version_ops.h92
-rw-r--r--src/cls/version/cls_version_types.cc19
-rw-r--r--src/cls/version/cls_version_types.h98
6 files changed, 583 insertions, 0 deletions
diff --git a/src/cls/version/cls_version.cc b/src/cls/version/cls_version.cc
new file mode 100644
index 000000000..2e8ec91ed
--- /dev/null
+++ b/src/cls/version/cls_version.cc
@@ -0,0 +1,238 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include <errno.h>
+
+#include "objclass/objclass.h"
+
+#include "cls/version/cls_version_ops.h"
+
+#include "include/compat.h"
+
+using std::list;
+
+using ceph::bufferlist;
+
+CLS_VER(1,0)
+CLS_NAME(version)
+
+
+#define VERSION_ATTR "ceph.objclass.version"
+
+static int set_version(cls_method_context_t hctx, struct obj_version *objv)
+{
+ bufferlist bl;
+
+ encode(*objv, bl);
+
+ CLS_LOG(20, "cls_version: set_version %s:%d", objv->tag.c_str(), (int)objv->ver);
+
+ int ret = cls_cxx_setxattr(hctx, VERSION_ATTR, &bl);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int init_version(cls_method_context_t hctx, struct obj_version *objv)
+{
+#define TAG_LEN 24
+ char buf[TAG_LEN + 1];
+
+ int ret = cls_gen_rand_base64(buf, sizeof(buf));
+ if (ret < 0)
+ return ret;
+
+ objv->ver = 1;
+ objv->tag = buf;
+
+ CLS_LOG(20, "cls_version: init_version %s:%d", objv->tag.c_str(), (int)objv->ver);
+
+ return set_version(hctx, objv);
+}
+
+/* implicit create should be true only if called from a write operation (set, inc), never from a read operation (read, check) */
+static int read_version(cls_method_context_t hctx, obj_version *objv, bool implicit_create)
+{
+ bufferlist bl;
+ int ret = cls_cxx_getxattr(hctx, VERSION_ATTR, &bl);
+ if (ret == -ENOENT || ret == -ENODATA) {
+ objv->ver = 0;
+
+ if (implicit_create) {
+ return init_version(hctx, objv);
+ }
+ return 0;
+ }
+ if (ret < 0)
+ return ret;
+
+ try {
+ auto iter = bl.cbegin();
+ decode(*objv, iter);
+ } catch (ceph::buffer::error& err) {
+ CLS_LOG(0, "ERROR: read_version(): failed to decode version entry\n");
+ return -EIO;
+ }
+ CLS_LOG(20, "cls_version: read_version %s:%d", objv->tag.c_str(), (int)objv->ver);
+
+ return 0;
+}
+
+static int cls_version_set(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
+{
+ auto in_iter = in->cbegin();
+
+ cls_version_set_op op;
+ try {
+ decode(op, in_iter);
+ } catch (ceph::buffer::error& err) {
+ CLS_LOG(1, "ERROR: cls_version_get(): failed to decode entry\n");
+ return -EINVAL;
+ }
+
+ int ret = set_version(hctx, &op.objv);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static bool check_conds(list<obj_version_cond>& conds, obj_version& objv)
+{
+ if (conds.empty())
+ return true;
+
+ for (list<obj_version_cond>::iterator iter = conds.begin(); iter != conds.end(); ++iter) {
+ obj_version_cond& cond = *iter;
+ obj_version& v = cond.ver;
+ CLS_LOG(20, "cls_version: check_version %s:%d (cond=%d)", v.tag.c_str(), (int)v.ver, (int)cond.cond);
+
+ switch (cond.cond) {
+ case VER_COND_NONE:
+ break;
+ case VER_COND_EQ:
+ if (!objv.compare(&v))
+ return false;
+ break;
+ case VER_COND_GT:
+ if (!(objv.ver > v.ver))
+ return false;
+ break;
+ case VER_COND_GE:
+ if (!(objv.ver >= v.ver))
+ return false;
+ break;
+ case VER_COND_LT:
+ if (!(objv.ver < v.ver))
+ return false;
+ break;
+ case VER_COND_LE:
+ if (!(objv.ver <= v.ver))
+ return false;
+ break;
+ case VER_COND_TAG_EQ:
+ if (objv.tag.compare(v.tag) != 0)
+ return false;
+ break;
+ case VER_COND_TAG_NE:
+ if (objv.tag.compare(v.tag) == 0)
+ return false;
+ break;
+ }
+ }
+
+ return true;
+}
+
+static int cls_version_inc(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
+{
+ auto in_iter = in->cbegin();
+
+ cls_version_inc_op op;
+ try {
+ decode(op, in_iter);
+ } catch (ceph::buffer::error& err) {
+ CLS_LOG(1, "ERROR: cls_version_get(): failed to decode entry\n");
+ return -EINVAL;
+ }
+
+ obj_version objv;
+ int ret = read_version(hctx, &objv, true);
+ if (ret < 0)
+ return ret;
+
+ if (!check_conds(op.conds, objv)) {
+ return -ECANCELED;
+ }
+ objv.inc();
+
+ ret = set_version(hctx, &objv);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int cls_version_check(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
+{
+ auto in_iter = in->cbegin();
+
+ cls_version_check_op op;
+ try {
+ decode(op, in_iter);
+ } catch (ceph::buffer::error& err) {
+ CLS_LOG(1, "ERROR: cls_version_get(): failed to decode entry\n");
+ return -EINVAL;
+ }
+
+ obj_version objv;
+ int ret = read_version(hctx, &objv, false);
+ if (ret < 0)
+ return ret;
+
+ if (!check_conds(op.conds, objv)) {
+ CLS_LOG(20, "cls_version: failed condition check");
+ return -ECANCELED;
+ }
+
+ return 0;
+}
+
+static int cls_version_read(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
+{
+ obj_version objv;
+
+ cls_version_read_ret read_ret;
+ int ret = read_version(hctx, &read_ret.objv, false);
+ if (ret < 0)
+ return ret;
+
+ encode(read_ret, *out);
+
+ return 0;
+}
+
+CLS_INIT(version)
+{
+ CLS_LOG(1, "Loaded version class!");
+
+ cls_handle_t h_class;
+ cls_method_handle_t h_version_set;
+ cls_method_handle_t h_version_inc;
+ cls_method_handle_t h_version_inc_conds;
+ cls_method_handle_t h_version_read;
+ cls_method_handle_t h_version_check_conds;
+
+ cls_register("version", &h_class);
+
+ /* version */
+ cls_register_cxx_method(h_class, "set", CLS_METHOD_RD | CLS_METHOD_WR, cls_version_set, &h_version_set);
+ cls_register_cxx_method(h_class, "inc", CLS_METHOD_RD | CLS_METHOD_WR, cls_version_inc, &h_version_inc);
+ cls_register_cxx_method(h_class, "inc_conds", CLS_METHOD_RD | CLS_METHOD_WR, cls_version_inc, &h_version_inc_conds);
+ cls_register_cxx_method(h_class, "read", CLS_METHOD_RD, cls_version_read, &h_version_read);
+ cls_register_cxx_method(h_class, "check_conds", CLS_METHOD_RD, cls_version_check, &h_version_check_conds);
+
+ return;
+}
+
diff --git a/src/cls/version/cls_version_client.cc b/src/cls/version/cls_version_client.cc
new file mode 100644
index 000000000..769a7b77b
--- /dev/null
+++ b/src/cls/version/cls_version_client.cc
@@ -0,0 +1,104 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include <errno.h>
+
+#include "cls/version/cls_version_client.h"
+#include "include/rados/librados.hpp"
+
+
+using namespace librados;
+
+
+void cls_version_set(librados::ObjectWriteOperation& op, obj_version& objv)
+{
+ bufferlist in;
+ cls_version_set_op call;
+ call.objv = objv;
+ encode(call, in);
+ op.exec("version", "set", in);
+}
+
+void cls_version_inc(librados::ObjectWriteOperation& op)
+{
+ bufferlist in;
+ cls_version_inc_op call;
+ encode(call, in);
+ op.exec("version", "inc", in);
+}
+
+void cls_version_inc(librados::ObjectWriteOperation& op, obj_version& objv, VersionCond cond)
+{
+ bufferlist in;
+ cls_version_inc_op call;
+ call.objv = objv;
+
+ obj_version_cond c;
+ c.cond = cond;
+ c.ver = objv;
+
+ call.conds.push_back(c);
+
+ encode(call, in);
+ op.exec("version", "inc_conds", in);
+}
+
+void cls_version_check(librados::ObjectOperation& op, obj_version& objv, VersionCond cond)
+{
+ bufferlist in;
+ cls_version_check_op call;
+ call.objv = objv;
+
+ obj_version_cond c;
+ c.cond = cond;
+ c.ver = objv;
+
+ call.conds.push_back(c);
+
+ encode(call, in);
+ op.exec("version", "check_conds", in);
+}
+
+class VersionReadCtx : public ObjectOperationCompletion {
+ obj_version *objv;
+public:
+ explicit VersionReadCtx(obj_version *_objv) : objv(_objv) {}
+ void handle_completion(int r, bufferlist& outbl) override {
+ if (r >= 0) {
+ cls_version_read_ret ret;
+ try {
+ auto iter = outbl.cbegin();
+ decode(ret, iter);
+ *objv = ret.objv;
+ } catch (ceph::buffer::error& err) {
+ // nothing we can do about it atm
+ }
+ }
+ }
+};
+
+void cls_version_read(librados::ObjectReadOperation& op, obj_version *objv)
+{
+ bufferlist inbl;
+ op.exec("version", "read", inbl, new VersionReadCtx(objv));
+}
+
+int cls_version_read(librados::IoCtx& io_ctx, std::string& oid, obj_version *ver)
+{
+ bufferlist in, out;
+ int r = io_ctx.exec(oid, "version", "read", in, out);
+ if (r < 0)
+ return r;
+
+ cls_version_read_ret ret;
+ try {
+ auto iter = out.cbegin();
+ decode(ret, iter);
+ } catch (ceph::buffer::error& err) {
+ return -EIO;
+ }
+
+ *ver = ret.objv;
+
+ return r;
+}
diff --git a/src/cls/version/cls_version_client.h b/src/cls/version/cls_version_client.h
new file mode 100644
index 000000000..19457855a
--- /dev/null
+++ b/src/cls/version/cls_version_client.h
@@ -0,0 +1,32 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_CLS_VERSION_CLIENT_H
+#define CEPH_CLS_VERSION_CLIENT_H
+
+#include "include/rados/librados_fwd.hpp"
+#include "cls_version_ops.h"
+
+/*
+ * version objclass
+ */
+
+void cls_version_set(librados::ObjectWriteOperation& op, obj_version& ver);
+
+/* increase anyway */
+void cls_version_inc(librados::ObjectWriteOperation& op);
+
+/* conditional increase, return -EAGAIN if condition fails */
+void cls_version_inc(librados::ObjectWriteOperation& op, obj_version& ver, VersionCond cond);
+
+void cls_version_read(librados::ObjectReadOperation& op, obj_version *objv);
+
+// these overloads which call io_ctx.operate() or io_ctx.exec() should not be called in the rgw.
+// rgw_rados_operate() should be called after the overloads w/o calls to io_ctx.operate()/exec()
+#ifndef CLS_CLIENT_HIDE_IOCTX
+int cls_version_read(librados::IoCtx& io_ctx, std::string& oid, obj_version *ver);
+#endif
+
+void cls_version_check(librados::ObjectOperation& op, obj_version& ver, VersionCond cond);
+
+#endif
diff --git a/src/cls/version/cls_version_ops.h b/src/cls/version/cls_version_ops.h
new file mode 100644
index 000000000..62cd11729
--- /dev/null
+++ b/src/cls/version/cls_version_ops.h
@@ -0,0 +1,92 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_CLS_VERSION_OPS_H
+#define CEPH_CLS_VERSION_OPS_H
+
+#include "cls_version_types.h"
+
+struct cls_version_set_op {
+ obj_version objv;
+
+ cls_version_set_op() {}
+
+ void encode(ceph::buffer::list& bl) const {
+ ENCODE_START(1, 1, bl);
+ encode(objv, bl);
+ ENCODE_FINISH(bl);
+ }
+
+ void decode(ceph::buffer::list::const_iterator& bl) {
+ DECODE_START(1, bl);
+ decode(objv, bl);
+ DECODE_FINISH(bl);
+ }
+};
+WRITE_CLASS_ENCODER(cls_version_set_op)
+
+struct cls_version_inc_op {
+ obj_version objv;
+ std::list<obj_version_cond> conds;
+
+ cls_version_inc_op() {}
+
+ void encode(ceph::buffer::list& bl) const {
+ ENCODE_START(1, 1, bl);
+ encode(objv, bl);
+ encode(conds, bl);
+ ENCODE_FINISH(bl);
+ }
+
+ void decode(ceph::buffer::list::const_iterator& bl) {
+ DECODE_START(1, bl);
+ decode(objv, bl);
+ decode(conds, bl);
+ DECODE_FINISH(bl);
+ }
+};
+WRITE_CLASS_ENCODER(cls_version_inc_op)
+
+struct cls_version_check_op {
+ obj_version objv;
+ std::list<obj_version_cond> conds;
+
+ cls_version_check_op() {}
+
+ void encode(ceph::buffer::list& bl) const {
+ ENCODE_START(1, 1, bl);
+ encode(objv, bl);
+ encode(conds, bl);
+ ENCODE_FINISH(bl);
+ }
+
+ void decode(ceph::buffer::list::const_iterator& bl) {
+ DECODE_START(1, bl);
+ decode(objv, bl);
+ decode(conds, bl);
+ DECODE_FINISH(bl);
+ }
+};
+WRITE_CLASS_ENCODER(cls_version_check_op)
+
+struct cls_version_read_ret {
+ obj_version objv;
+
+ cls_version_read_ret() {}
+
+ void encode(ceph::buffer::list& bl) const {
+ ENCODE_START(1, 1, bl);
+ encode(objv, bl);
+ ENCODE_FINISH(bl);
+ }
+
+ void decode(ceph::buffer::list::const_iterator& bl) {
+ DECODE_START(1, bl);
+ decode(objv, bl);
+ DECODE_FINISH(bl);
+ }
+};
+WRITE_CLASS_ENCODER(cls_version_read_ret)
+
+
+#endif
diff --git a/src/cls/version/cls_version_types.cc b/src/cls/version/cls_version_types.cc
new file mode 100644
index 000000000..b82f6aa8a
--- /dev/null
+++ b/src/cls/version/cls_version_types.cc
@@ -0,0 +1,19 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "cls/version/cls_version_types.h"
+#include "common/Formatter.h"
+#include "common/ceph_json.h"
+
+
+void obj_version::dump(ceph::Formatter *f) const
+{
+ f->dump_int("ver", ver);
+ f->dump_string("tag", tag);
+}
+
+void obj_version::decode_json(JSONObj *obj)
+{
+ JSONDecoder::decode_json("ver", ver, obj);
+ JSONDecoder::decode_json("tag", tag, obj);
+}
diff --git a/src/cls/version/cls_version_types.h b/src/cls/version/cls_version_types.h
new file mode 100644
index 000000000..62cc16e33
--- /dev/null
+++ b/src/cls/version/cls_version_types.h
@@ -0,0 +1,98 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_CLS_VERSION_TYPES_H
+#define CEPH_CLS_VERSION_TYPES_H
+
+#include "include/encoding.h"
+#include "include/types.h"
+
+class JSONObj;
+
+
+struct obj_version {
+ uint64_t ver;
+ std::string tag;
+
+ obj_version() : ver(0) {}
+
+ void encode(ceph::buffer::list& bl) const {
+ ENCODE_START(1, 1, bl);
+ encode(ver, bl);
+ encode(tag, bl);
+ ENCODE_FINISH(bl);
+ }
+
+ void decode(ceph::buffer::list::const_iterator& bl) {
+ DECODE_START(1, bl);
+ decode(ver, bl);
+ decode(tag, bl);
+ DECODE_FINISH(bl);
+ }
+
+ void inc() {
+ ver++;
+ }
+
+ void clear() {
+ ver = 0;
+ tag.clear();
+ }
+
+ bool empty() const {
+ return tag.empty();
+ }
+
+ bool compare(struct obj_version *v) const {
+ return (ver == v->ver &&
+ tag.compare(v->tag) == 0);
+ }
+
+ bool operator==(const struct obj_version& v) const {
+ return (ver == v.ver &&
+ tag.compare(v.tag) == 0);
+ }
+
+ void dump(ceph::Formatter *f) const;
+ void decode_json(JSONObj *obj);
+ static void generate_test_instances(std::list<obj_version*>& o);
+};
+WRITE_CLASS_ENCODER(obj_version)
+
+enum VersionCond {
+ VER_COND_NONE = 0,
+ VER_COND_EQ, /* equal */
+ VER_COND_GT, /* greater than */
+ VER_COND_GE, /* greater or equal */
+ VER_COND_LT, /* less than */
+ VER_COND_LE, /* less or equal */
+ VER_COND_TAG_EQ,
+ VER_COND_TAG_NE,
+};
+
+struct obj_version_cond {
+ struct obj_version ver;
+ VersionCond cond;
+
+ void encode(ceph::buffer::list& bl) const {
+ ENCODE_START(1, 1, bl);
+ encode(ver, bl);
+ uint32_t c = (uint32_t)cond;
+ encode(c, bl);
+ ENCODE_FINISH(bl);
+ }
+
+ void decode(ceph::buffer::list::const_iterator& bl) {
+ DECODE_START(1, bl);
+ decode(ver, bl);
+ uint32_t c;
+ decode(c, bl);
+ cond = (VersionCond)c;
+ DECODE_FINISH(bl);
+ }
+
+};
+WRITE_CLASS_ENCODER(obj_version_cond)
+
+
+#endif