From 242a6c85239397fdc19064b4a36c338b569892c8 Mon Sep 17 00:00:00 2001 From: Josh Durgin Date: Thu, 6 Feb 2014 20:03:20 -0800 Subject: [PATCH] librados: add read xattr functions for c object operations Only implement string xattr comparison by since integer xattr comparison assumes ceph-encoded integers in little-endian 64 bit format, which should not be exposed to librados users. Add an extra callback for getxattr so that we can initialize the iterator. Fixes: #7193 Signed-off-by: Josh Durgin --- src/include/rados/librados.h | 28 ++++++ src/librados/librados.cc | 33 +++++++ src/test/librados/c_read_operations.cc | 131 +++++++++++++++++++++++++ 3 files changed, 192 insertions(+) diff --git a/src/include/rados/librados.h b/src/include/rados/librados.h index 29d20858cb37f..5143ddc6a9da9 100644 --- a/src/include/rados/librados.h +++ b/src/include/rados/librados.h @@ -239,6 +239,8 @@ typedef void *rados_write_op_t; * An object read operation stores a number of operations which can be * executed atomically. For usage, see: * - Creation and deletion: rados_create_read_op() rados_release_read_op() + * - Extended attribute manipulation: rados_read_op_cmpxattr(), + * rados_read_op_getxattr(), rados_read_op_getxattrs() * - Object properties: rados_read_op_stat(), rados_read_op_assert_exists() * - IO on objects: rados_read_op_read() * - Custom operations: rados_read_op_exec(), rados_read_op_exec_user_buf() @@ -1953,6 +1955,32 @@ void rados_read_op_set_flags(rados_read_op_t read_op, int flags); */ void rados_read_op_assert_exists(rados_read_op_t read_op); +/** + * Ensure that the an xattr satisfies a comparison + * @param read_op operation to add this action to + * @param name name of the xattr to look up + * @param comparison_operator currently undocumented, look for + * LIBRADOS_CMPXATTR_OP_EQ in librados.h + * @param value buffer to compare actual xattr value to + * @param value_len length of buffer to compare actual xattr value to + */ +void rados_read_op_cmpxattr(rados_read_op_t read_op, + const char *name, + uint8_t comparison_operator, + const char *value, + size_t value_len); + +/** + * Start iterating over xattrs on an object. + * + * @param read_op operation to add this action to + * @param iter where to store the iterator + * @param prval where to store the return value of this action + */ +void rados_read_op_getxattrs(rados_read_op_t read_op, + rados_xattrs_iter_t *iter, + int *prval); + /** * Get object size and mtime diff --git a/src/librados/librados.cc b/src/librados/librados.cc index 652994130a844..34a2b1d4eec86 100644 --- a/src/librados/librados.cc +++ b/src/librados/librados.cc @@ -3200,6 +3200,20 @@ extern "C" void rados_read_op_assert_exists(rados_read_op_t read_op) ((::ObjectOperation *)read_op)->stat(NULL, (utime_t *)NULL, NULL); } +extern "C" void rados_read_op_cmpxattr(rados_read_op_t read_op, + const char *name, + uint8_t comparison_operator, + const char *value, + size_t value_len) +{ + bufferlist bl; + bl.append(value, value_len); + ((::ObjectOperation *)read_op)->cmpxattr(name, + comparison_operator, + CEPH_OSD_CMPXATTR_MODE_STRING, + bl); +} + extern "C" void rados_read_op_stat(rados_read_op_t read_op, uint64_t *psize, time_t *pmtime, @@ -3294,6 +3308,25 @@ extern "C" void rados_read_op_exec_user_buf(rados_read_op_t read_op, prval); } +class C_XattrsIter : public Context { + RadosXattrsIter *iter; +public: + C_XattrsIter(RadosXattrsIter *iter) : iter(iter) {} + void finish(int r) { + iter->i = iter->attrset.begin(); + } +}; + +extern "C" void rados_read_op_getxattrs(rados_read_op_t read_op, + rados_xattrs_iter_t *iter, + int *prval) +{ + RadosXattrsIter *xattrs_iter = new RadosXattrsIter; + ((::ObjectOperation *)read_op)->getxattrs(&xattrs_iter->attrset, prval); + ((::ObjectOperation *)read_op)->add_handler(new C_XattrsIter(xattrs_iter)); + *iter = xattrs_iter; +} + extern "C" int rados_read_op_operate(rados_read_op_t read_op, rados_ioctx_t io, const char *oid, diff --git a/src/test/librados/c_read_operations.cc b/src/test/librados/c_read_operations.cc index 9d303acfa0f46..aa058c64ead85 100644 --- a/src/test/librados/c_read_operations.cc +++ b/src/test/librados/c_read_operations.cc @@ -21,6 +21,45 @@ protected: void remove_object() { ASSERT_EQ(0, rados_remove(ioctx, obj)); } + int cmp_xattr(const char *xattr, const char *value, size_t value_len, + uint8_t cmp_op) + { + rados_read_op_t op = rados_create_read_op(); + rados_read_op_cmpxattr(op, xattr, cmp_op, value, value_len); + int r = rados_read_op_operate(op, ioctx, obj, 0); + rados_release_read_op(op); + return r; + } + + + void compare_xattrs(char const* const* keys, + char const* const* vals, + const size_t *lens, + size_t len, + rados_xattrs_iter_t iter) + { + size_t i = 0; + char *key = NULL; + char *val = NULL; + size_t val_len = 0; + while (i < len) { + ASSERT_EQ(0, rados_getxattrs_next(iter, (const char**) &key, + (const char**) &val, &val_len)); + if (len == 0 && key == NULL && val == NULL) + break; + EXPECT_EQ(std::string(keys[i]), std::string(key)); + EXPECT_EQ(0, memcmp(vals[i], val, val_len)); + EXPECT_EQ(lens[i], val_len); + ++i; + } + ASSERT_EQ(i, len); + ASSERT_EQ(0, rados_getxattrs_next(iter, (const char**)&key, + (const char**)&val, &val_len)); + ASSERT_EQ((char*)NULL, key); + ASSERT_EQ((char*)NULL, val); + ASSERT_EQ(0u, val_len); + rados_getxattrs_end(iter); + } }; TEST_F(CReadOpsTest, NewDelete) { @@ -75,6 +114,62 @@ TEST_F(CReadOpsTest, AssertExists) { remove_object(); } +TEST_F(CReadOpsTest, CmpXattr) { + write_object(); + + char buf[len]; + memset(buf, 0xcc, sizeof(buf)); + + const char *xattr = "test"; + rados_setxattr(ioctx, obj, xattr, buf, sizeof(buf)); + + // equal value + EXPECT_EQ(1, cmp_xattr(xattr, buf, sizeof(buf), LIBRADOS_CMPXATTR_OP_EQ)); + EXPECT_EQ(-ECANCELED, cmp_xattr(xattr, buf, sizeof(buf), LIBRADOS_CMPXATTR_OP_NE)); + EXPECT_EQ(-ECANCELED, cmp_xattr(xattr, buf, sizeof(buf), LIBRADOS_CMPXATTR_OP_GT)); + EXPECT_EQ(1, cmp_xattr(xattr, buf, sizeof(buf), LIBRADOS_CMPXATTR_OP_GTE)); + EXPECT_EQ(-ECANCELED, cmp_xattr(xattr, buf, sizeof(buf), LIBRADOS_CMPXATTR_OP_LT)); + EXPECT_EQ(1, cmp_xattr(xattr, buf, sizeof(buf), LIBRADOS_CMPXATTR_OP_LTE)); + + // < value + EXPECT_EQ(-ECANCELED, cmp_xattr(xattr, buf, sizeof(buf) - 1, LIBRADOS_CMPXATTR_OP_EQ)); + EXPECT_EQ(1, cmp_xattr(xattr, buf, sizeof(buf) - 1, LIBRADOS_CMPXATTR_OP_NE)); + EXPECT_EQ(-ECANCELED, cmp_xattr(xattr, buf, sizeof(buf) - 1, LIBRADOS_CMPXATTR_OP_GT)); + EXPECT_EQ(-ECANCELED, cmp_xattr(xattr, buf, sizeof(buf) - 1, LIBRADOS_CMPXATTR_OP_GTE)); + EXPECT_EQ(1, cmp_xattr(xattr, buf, sizeof(buf) - 1, LIBRADOS_CMPXATTR_OP_LT)); + EXPECT_EQ(1, cmp_xattr(xattr, buf, sizeof(buf) - 1, LIBRADOS_CMPXATTR_OP_LTE)); + + // > value + memset(buf, 0xcd, sizeof(buf)); + EXPECT_EQ(-ECANCELED, cmp_xattr(xattr, buf, sizeof(buf), LIBRADOS_CMPXATTR_OP_EQ)); + EXPECT_EQ(1, cmp_xattr(xattr, buf, sizeof(buf), LIBRADOS_CMPXATTR_OP_NE)); + EXPECT_EQ(1, cmp_xattr(xattr, buf, sizeof(buf), LIBRADOS_CMPXATTR_OP_GT)); + EXPECT_EQ(1, cmp_xattr(xattr, buf, sizeof(buf), LIBRADOS_CMPXATTR_OP_GTE)); + EXPECT_EQ(-ECANCELED, cmp_xattr(xattr, buf, sizeof(buf), LIBRADOS_CMPXATTR_OP_LT)); + EXPECT_EQ(-ECANCELED, cmp_xattr(xattr, buf, sizeof(buf), LIBRADOS_CMPXATTR_OP_LTE)); + + // it compares C strings, so NUL at the beginning is the same + // regardless of the following data + rados_setxattr(ioctx, obj, xattr, "\0\0", 1); + buf[0] = '\0'; + EXPECT_EQ(1, cmp_xattr(xattr, buf, 2, LIBRADOS_CMPXATTR_OP_EQ)); + EXPECT_EQ(-ECANCELED, cmp_xattr(xattr, buf, 2, LIBRADOS_CMPXATTR_OP_NE)); + EXPECT_EQ(-ECANCELED, cmp_xattr(xattr, buf, 2, LIBRADOS_CMPXATTR_OP_GT)); + EXPECT_EQ(1, cmp_xattr(xattr, buf, 2, LIBRADOS_CMPXATTR_OP_GTE)); + EXPECT_EQ(-ECANCELED, cmp_xattr(xattr, buf, 2, LIBRADOS_CMPXATTR_OP_LT)); + EXPECT_EQ(1, cmp_xattr(xattr, buf, 2, LIBRADOS_CMPXATTR_OP_LTE)); + + buf[1] = '\0'; + EXPECT_EQ(1, cmp_xattr(xattr, buf, 2, LIBRADOS_CMPXATTR_OP_EQ)); + EXPECT_EQ(-ECANCELED, cmp_xattr(xattr, buf, 2, LIBRADOS_CMPXATTR_OP_NE)); + EXPECT_EQ(-ECANCELED, cmp_xattr(xattr, buf, 2, LIBRADOS_CMPXATTR_OP_GT)); + EXPECT_EQ(1, cmp_xattr(xattr, buf, 2, LIBRADOS_CMPXATTR_OP_GTE)); + EXPECT_EQ(-ECANCELED, cmp_xattr(xattr, buf, 2, LIBRADOS_CMPXATTR_OP_LT)); + EXPECT_EQ(1, cmp_xattr(xattr, buf, 2, LIBRADOS_CMPXATTR_OP_LTE)); + + remove_object(); +} + TEST_F(CReadOpsTest, Read) { write_object(); @@ -263,3 +358,39 @@ TEST_F(CReadOpsTest, Stat) { EXPECT_EQ(-ENOENT, rados_read_op_operate(op, ioctx, obj, 0)); rados_release_read_op(op); } + +TEST_F(CReadOpsTest, GetXattrs) { + write_object(); + + char *keys[] = {(char*)"bar", + (char*)"foo", + (char*)"test1", + (char*)"test2"}; + char *vals[] = {(char*)"", + (char*)"\0", + (char*)"abc", + (char*)"va\0lue"}; + size_t lens[] = {0, 1, 3, 6}; + + int rval = 1; + rados_read_op_t op = rados_create_read_op(); + rados_xattrs_iter_t it; + rados_read_op_getxattrs(op, &it, &rval); + EXPECT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0)); + EXPECT_EQ(0, rval); + rados_release_read_op(op); + compare_xattrs(keys, vals, lens, 0, it); + + for (int i = 0; i < 4; ++i) + rados_setxattr(ioctx, obj, keys[i], vals[i], lens[i]); + + rval = 1; + op = rados_create_read_op(); + rados_read_op_getxattrs(op, &it, &rval); + EXPECT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0)); + EXPECT_EQ(0, rval); + rados_release_read_op(op); + compare_xattrs(keys, vals, lens, 4, it); + + remove_object(); +} -- 2.39.5