X-Git-Url: https://gerrit.opnfv.org/gerrit/gitweb?a=blobdiff_plain;f=src%2Fceph%2Fsrc%2Ftest%2Flibrados%2Fc_read_operations.cc;fp=src%2Fceph%2Fsrc%2Ftest%2Flibrados%2Fc_read_operations.cc;h=605fe4ff3ddc45e404785fa5ef10f661baf4155f;hb=812ff6ca9fcd3e629e49d4328905f33eee8ca3f5;hp=0000000000000000000000000000000000000000;hpb=15280273faafb77777eab341909a3f495cf248d9;p=stor4nfv.git diff --git a/src/ceph/src/test/librados/c_read_operations.cc b/src/ceph/src/test/librados/c_read_operations.cc new file mode 100644 index 0000000..605fe4f --- /dev/null +++ b/src/ceph/src/test/librados/c_read_operations.cc @@ -0,0 +1,708 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// Tests for the C API coverage of atomic read operations + +#include +#include + +#include "include/err.h" +#include "include/rados/librados.h" +#include "test/librados/test.h" +#include "test/librados/TestCase.h" + +const char *data = "testdata"; +const char *obj = "testobj"; +const size_t len = strlen(data); + +class CReadOpsTest : public RadosTest { +protected: + void write_object() { + // Create an object and write to it + ASSERT_EQ(0, rados_write(ioctx, obj, data, len, 0)); + } + 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 fetch_and_verify_omap_vals(char const* const* keys, + char const* const* vals, + const size_t *lens, + size_t len) + { + rados_omap_iter_t iter_vals, iter_keys, iter_vals_by_key; + int r_vals, r_keys, r_vals_by_key; + rados_read_op_t op = rados_create_read_op(); + rados_read_op_omap_get_vals2(op, NULL, NULL, 100, &iter_vals, NULL, &r_vals); + rados_read_op_omap_get_keys2(op, NULL, 100, &iter_keys, NULL, &r_keys); + rados_read_op_omap_get_vals_by_keys(op, keys, len, + &iter_vals_by_key, &r_vals_by_key); + ASSERT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0)); + rados_release_read_op(op); + ASSERT_EQ(0, r_vals); + ASSERT_EQ(0, r_keys); + ASSERT_EQ(0, r_vals_by_key); + + const char *zeros[len]; + size_t zero_lens[len]; + memset(zeros, 0, sizeof(zeros)); + memset(zero_lens, 0, sizeof(zero_lens)); + compare_omap_vals(keys, vals, lens, len, iter_vals); + compare_omap_vals(keys, zeros, zero_lens, len, iter_keys); + compare_omap_vals(keys, vals, lens, len, iter_vals_by_key); + } + + void compare_omap_vals(char const* const* keys, + char const* const* vals, + const size_t *lens, + size_t len, + rados_omap_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_omap_get_next(iter, &key, &val, &val_len)); + if (val_len == 0 && key == NULL && val == NULL) + break; + if (key) + EXPECT_EQ(std::string(keys[i]), std::string(key)); + else + EXPECT_EQ(keys[i], key); + ASSERT_EQ(0, memcmp(vals[i], val, val_len)); + ASSERT_EQ(lens[i], val_len); + ++i; + } + ASSERT_EQ(i, len); + ASSERT_EQ(0, rados_omap_get_next(iter, &key, &val, &val_len)); + ASSERT_EQ((char*)NULL, key); + ASSERT_EQ((char*)NULL, val); + ASSERT_EQ(0u, val_len); + rados_omap_get_end(iter); + } + + 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 (key == NULL) + break; + EXPECT_EQ(std::string(keys[i]), std::string(key)); + if (val != NULL) { + 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) { + rados_read_op_t op = rados_create_read_op(); + ASSERT_TRUE(op); + rados_release_read_op(op); +} + +TEST_F(CReadOpsTest, SetOpFlags) { + write_object(); + + rados_read_op_t op = rados_create_read_op(); + size_t bytes_read = 0; + char *out = NULL; + int rval = 0; + rados_read_op_exec(op, "rbd", "get_id", NULL, 0, &out, + &bytes_read, &rval); + rados_read_op_set_flags(op, LIBRADOS_OP_FLAG_FAILOK); + EXPECT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0)); + EXPECT_EQ(-EIO, rval); + EXPECT_EQ(0u, bytes_read); + EXPECT_EQ((char*)NULL, out); + rados_release_read_op(op); + + remove_object(); +} + +TEST_F(CReadOpsTest, AssertExists) { + rados_read_op_t op = rados_create_read_op(); + rados_read_op_assert_exists(op); + + ASSERT_EQ(-ENOENT, rados_read_op_operate(op, ioctx, obj, 0)); + rados_release_read_op(op); + + op = rados_create_read_op(); + rados_read_op_assert_exists(op); + + rados_completion_t completion; + ASSERT_EQ(0, rados_aio_create_completion(NULL, NULL, NULL, &completion)); + ASSERT_EQ(0, rados_aio_read_op_operate(op, ioctx, completion, obj, 0)); + rados_aio_wait_for_complete(completion); + ASSERT_EQ(-ENOENT, rados_aio_get_return_value(completion)); + rados_aio_release(completion); + rados_release_read_op(op); + + write_object(); + + op = rados_create_read_op(); + rados_read_op_assert_exists(op); + ASSERT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0)); + rados_release_read_op(op); + + remove_object(); +} + +TEST_F(CReadOpsTest, AssertVersion) { + write_object(); + // Write to the object a second time to guarantee that its + // version number is greater than 0 + write_object(); + uint64_t v = rados_get_last_version(ioctx); + + rados_read_op_t op = rados_create_read_op(); + rados_read_op_assert_version(op, v+1); + ASSERT_EQ(-EOVERFLOW, rados_read_op_operate(op, ioctx, obj, 0)); + rados_release_read_op(op); + + op = rados_create_read_op(); + rados_read_op_assert_version(op, v-1); + ASSERT_EQ(-ERANGE, rados_read_op_operate(op, ioctx, obj, 0)); + rados_release_read_op(op); + + op = rados_create_read_op(); + rados_read_op_assert_version(op, v); + ASSERT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0)); + rados_release_read_op(op); + + 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)); + + // check that null bytes are compared correctly + rados_setxattr(ioctx, obj, xattr, "\0\0", 2); + buf[0] = '\0'; + EXPECT_EQ(-ECANCELED, cmp_xattr(xattr, buf, 2, LIBRADOS_CMPXATTR_OP_EQ)); + EXPECT_EQ(1, cmp_xattr(xattr, buf, 2, LIBRADOS_CMPXATTR_OP_NE)); + EXPECT_EQ(1, 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(-ECANCELED, 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(); + + char buf[len]; + // check that using read_ops returns the same data with + // or without bytes_read and rval out params + { + rados_read_op_t op = rados_create_read_op(); + rados_read_op_read(op, 0, len, buf, NULL, NULL); + ASSERT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0)); + ASSERT_EQ(0, memcmp(data, buf, len)); + rados_release_read_op(op); + } + + { + rados_read_op_t op = rados_create_read_op(); + int rval; + rados_read_op_read(op, 0, len, buf, NULL, &rval); + ASSERT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0)); + ASSERT_EQ(0, rval); + ASSERT_EQ(0, memcmp(data, buf, len)); + rados_release_read_op(op); + } + + { + rados_read_op_t op = rados_create_read_op(); + size_t bytes_read = 0; + rados_read_op_read(op, 0, len, buf, &bytes_read, NULL); + ASSERT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0)); + ASSERT_EQ(len, bytes_read); + ASSERT_EQ(0, memcmp(data, buf, len)); + rados_release_read_op(op); + } + + { + rados_read_op_t op = rados_create_read_op(); + size_t bytes_read = 0; + int rval; + rados_read_op_read(op, 0, len, buf, &bytes_read, &rval); + ASSERT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0)); + ASSERT_EQ(len, bytes_read); + ASSERT_EQ(0, rval); + ASSERT_EQ(0, memcmp(data, buf, len)); + rados_release_read_op(op); + } + + { + rados_read_op_t op = rados_create_read_op(); + size_t bytes_read = 0; + int rval; + rados_read_op_read(op, 0, len, buf, &bytes_read, &rval); + rados_read_op_set_flags(op, LIBRADOS_OP_FLAG_FADVISE_DONTNEED); + ASSERT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0)); + ASSERT_EQ(len, bytes_read); + ASSERT_EQ(0, rval); + ASSERT_EQ(0, memcmp(data, buf, len)); + rados_release_read_op(op); + } + + remove_object(); +} + +TEST_F(CReadOpsTest, Checksum) { + write_object(); + + { + rados_read_op_t op = rados_create_read_op(); + uint64_t init_value = -1; + rados_read_op_checksum(op, LIBRADOS_CHECKSUM_TYPE_XXHASH64, + reinterpret_cast(&init_value), + sizeof(init_value), 0, len, 0, NULL, 0, NULL); + ASSERT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0)); + rados_release_read_op(op); + } + + { + uint32_t init_value = -1; + uint32_t crc[2]; + rados_read_op_t op = rados_create_read_op(); + rados_read_op_checksum(op, LIBRADOS_CHECKSUM_TYPE_CRC32C, + reinterpret_cast(&init_value), + sizeof(init_value), 0, len, 0, + reinterpret_cast(&crc), sizeof(crc), + nullptr); + ASSERT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0)); + ASSERT_EQ(1U, crc[0]); + uint32_t expected_crc = ceph_crc32c( + -1, reinterpret_cast(data), static_cast(len)); + ASSERT_EQ(expected_crc, crc[1]); + rados_release_read_op(op); + } + + { + uint32_t init_value = -1; + int rval; + rados_read_op_t op = rados_create_read_op(); + rados_read_op_checksum(op, LIBRADOS_CHECKSUM_TYPE_XXHASH32, + reinterpret_cast(&init_value), + sizeof(init_value), 0, len, 0, nullptr, 0, &rval); + ASSERT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0)); + ASSERT_EQ(0, rval); + rados_release_read_op(op); + } + + { + uint32_t init_value = -1; + uint32_t crc[3]; + int rval; + rados_read_op_t op = rados_create_read_op(); + rados_read_op_checksum(op, LIBRADOS_CHECKSUM_TYPE_CRC32C, + reinterpret_cast(&init_value), + sizeof(init_value), 0, len, 4, + reinterpret_cast(&crc), sizeof(crc), &rval); + ASSERT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0)); + ASSERT_EQ(2U, crc[0]); + uint32_t expected_crc[2]; + expected_crc[0] = ceph_crc32c( + -1, reinterpret_cast(data), 4U); + expected_crc[1] = ceph_crc32c( + -1, reinterpret_cast(data + 4), 4U); + ASSERT_EQ(expected_crc[0], crc[1]); + ASSERT_EQ(expected_crc[1], crc[2]); + ASSERT_EQ(0, rval); + rados_release_read_op(op); + } + + remove_object(); +} + +TEST_F(CReadOpsTest, RWOrderedRead) { + write_object(); + + char buf[len]; + rados_read_op_t op = rados_create_read_op(); + size_t bytes_read = 0; + int rval; + rados_read_op_read(op, 0, len, buf, &bytes_read, &rval); + rados_read_op_set_flags(op, LIBRADOS_OP_FLAG_FADVISE_DONTNEED); + ASSERT_EQ(0, rados_read_op_operate(op, ioctx, obj, + LIBRADOS_OPERATION_ORDER_READS_WRITES)); + ASSERT_EQ(len, bytes_read); + ASSERT_EQ(0, rval); + ASSERT_EQ(0, memcmp(data, buf, len)); + rados_release_read_op(op); + + remove_object(); +} + +TEST_F(CReadOpsTest, ShortRead) { + write_object(); + + char buf[len * 2]; + // check that using read_ops returns the same data with + // or without bytes_read and rval out params + { + rados_read_op_t op = rados_create_read_op(); + rados_read_op_read(op, 0, len * 2, buf, NULL, NULL); + ASSERT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0)); + ASSERT_EQ(0, memcmp(data, buf, len)); + rados_release_read_op(op); + } + + { + rados_read_op_t op = rados_create_read_op(); + int rval; + rados_read_op_read(op, 0, len * 2, buf, NULL, &rval); + ASSERT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0)); + ASSERT_EQ(0, rval); + ASSERT_EQ(0, memcmp(data, buf, len)); + rados_release_read_op(op); + } + + { + rados_read_op_t op = rados_create_read_op(); + size_t bytes_read = 0; + rados_read_op_read(op, 0, len * 2, buf, &bytes_read, NULL); + ASSERT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0)); + ASSERT_EQ(len, bytes_read); + ASSERT_EQ(0, memcmp(data, buf, len)); + rados_release_read_op(op); + } + + { + rados_read_op_t op = rados_create_read_op(); + size_t bytes_read = 0; + int rval; + rados_read_op_read(op, 0, len * 2, buf, &bytes_read, &rval); + ASSERT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0)); + ASSERT_EQ(len, bytes_read); + ASSERT_EQ(0, rval); + ASSERT_EQ(0, memcmp(data, buf, len)); + rados_release_read_op(op); + } + + remove_object(); +} + +TEST_F(CReadOpsTest, Exec) { + // create object so we don't get -ENOENT + write_object(); + + rados_read_op_t op = rados_create_read_op(); + ASSERT_TRUE(op); + size_t bytes_read = 0; + char *out = NULL; + int rval = 0; + rados_read_op_exec(op, "rbd", "get_all_features", NULL, 0, &out, + &bytes_read, &rval); + ASSERT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0)); + rados_release_read_op(op); + EXPECT_EQ(0, rval); + EXPECT_TRUE(out); + uint64_t features; + EXPECT_EQ(sizeof(features), bytes_read); + // make sure buffer is at least as long as it claims + ASSERT_TRUE(out[bytes_read-1] == out[bytes_read-1]); + rados_buffer_free(out); + + remove_object(); +} + +TEST_F(CReadOpsTest, ExecUserBuf) { + // create object so we don't get -ENOENT + write_object(); + + rados_read_op_t op = rados_create_read_op(); + size_t bytes_read = 0; + uint64_t features; + char out[sizeof(features)]; + int rval = 0; + rados_read_op_exec_user_buf(op, "rbd", "get_all_features", NULL, 0, out, + sizeof(out), &bytes_read, &rval); + ASSERT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0)); + rados_release_read_op(op); + EXPECT_EQ(0, rval); + EXPECT_EQ(sizeof(features), bytes_read); + + // buffer too short + bytes_read = 1024; + op = rados_create_read_op(); + rados_read_op_exec_user_buf(op, "rbd", "get_all_features", NULL, 0, out, + sizeof(features) - 1, &bytes_read, &rval); + ASSERT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0)); + rados_release_read_op(op); + EXPECT_EQ(0u, bytes_read); + EXPECT_EQ(-ERANGE, rval); + + // input buffer and no rval or bytes_read + op = rados_create_read_op(); + rados_read_op_exec_user_buf(op, "rbd", "get_all_features", out, sizeof(out), + out, sizeof(out), NULL, NULL); + ASSERT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0)); + rados_release_read_op(op); + + remove_object(); +} + +TEST_F(CReadOpsTest, Stat) { + rados_read_op_t op = rados_create_read_op(); + uint64_t size = 1; + int rval; + rados_read_op_stat(op, &size, NULL, &rval); + EXPECT_EQ(-ENOENT, rados_read_op_operate(op, ioctx, obj, 0)); + EXPECT_EQ(-EIO, rval); + EXPECT_EQ(1u, size); + rados_release_read_op(op); + + write_object(); + + op = rados_create_read_op(); + rados_read_op_stat(op, &size, NULL, &rval); + EXPECT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0)); + EXPECT_EQ(0, rval); + EXPECT_EQ(len, size); + rados_release_read_op(op); + + op = rados_create_read_op(); + rados_read_op_stat(op, NULL, NULL, NULL); + EXPECT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0)); + rados_release_read_op(op); + + remove_object(); + + op = rados_create_read_op(); + rados_read_op_stat(op, NULL, NULL, NULL); + EXPECT_EQ(-ENOENT, rados_read_op_operate(op, ioctx, obj, 0)); + rados_release_read_op(op); +} + +TEST_F(CReadOpsTest, Omap) { + 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}; + + // check for -ENOENT before the object exists and when it exists + // with no omap entries + rados_omap_iter_t iter_vals; + rados_read_op_t rop = rados_create_read_op(); + rados_read_op_omap_get_vals2(rop, "", "", 10, &iter_vals, NULL, NULL); + ASSERT_EQ(-ENOENT, rados_read_op_operate(rop, ioctx, obj, 0)); + rados_release_read_op(rop); + compare_omap_vals(NULL, NULL, NULL, 0, iter_vals); + + write_object(); + + fetch_and_verify_omap_vals(NULL, NULL, NULL, 0); + + // write and check for the k/v pairs + rados_write_op_t op = rados_create_write_op(); + rados_write_op_omap_set(op, keys, vals, lens, 4); + ASSERT_EQ(0, rados_write_op_operate(op, ioctx, obj, NULL, 0)); + rados_release_write_op(op); + + fetch_and_verify_omap_vals(keys, vals, lens, 4); + + rados_omap_iter_t iter_keys; + int r_vals = -1, r_keys = -1; + rop = rados_create_read_op(); + rados_read_op_omap_get_vals2(rop, "", "test", 1, &iter_vals, NULL, &r_vals); + rados_read_op_omap_get_keys2(rop, "test", 1, &iter_keys, NULL, &r_keys); + ASSERT_EQ(0, rados_read_op_operate(rop, ioctx, obj, 0)); + rados_release_read_op(rop); + EXPECT_EQ(0, r_vals); + EXPECT_EQ(0, r_keys); + + compare_omap_vals(&keys[2], &vals[2], &lens[2], 1, iter_vals); + compare_omap_vals(&keys[2], &vals[0], &lens[0], 1, iter_keys); + + // check omap_cmp finds all expected values + rop = rados_create_read_op(); + int rvals[4]; + for (int i = 0; i < 4; ++i) + rados_read_op_omap_cmp(rop, keys[i], LIBRADOS_CMPXATTR_OP_EQ, + vals[i], lens[i], &rvals[i]); + EXPECT_EQ(0, rados_read_op_operate(rop, ioctx, obj, 0)); + rados_release_read_op(rop); + for (int i = 0; i < 4; ++i) + EXPECT_EQ(0, rvals[i]); + + // try to remove keys with a guard that should fail + op = rados_create_write_op(); + rados_write_op_omap_cmp(op, keys[2], LIBRADOS_CMPXATTR_OP_LT, + vals[2], lens[2], &r_vals); + rados_write_op_omap_rm_keys(op, keys, 2); + EXPECT_EQ(-ECANCELED, rados_write_op_operate(op, ioctx, obj, NULL, 0)); + rados_release_write_op(op); + + // see http://tracker.ceph.com/issues/19518 + //ASSERT_EQ(-ECANCELED, r_vals); + + // verifying the keys are still there, and then remove them + op = rados_create_write_op(); + rados_write_op_omap_cmp(op, keys[0], LIBRADOS_CMPXATTR_OP_EQ, + vals[0], lens[0], NULL); + rados_write_op_omap_cmp(op, keys[1], LIBRADOS_CMPXATTR_OP_EQ, + vals[1], lens[1], NULL); + rados_write_op_omap_rm_keys(op, keys, 2); + EXPECT_EQ(0, rados_write_op_operate(op, ioctx, obj, NULL, 0)); + rados_release_write_op(op); + + fetch_and_verify_omap_vals(&keys[2], &vals[2], &lens[2], 2); + + // clear the rest and check there are none left + op = rados_create_write_op(); + rados_write_op_omap_clear(op); + EXPECT_EQ(0, rados_write_op_operate(op, ioctx, obj, NULL, 0)); + rados_release_write_op(op); + + fetch_and_verify_omap_vals(NULL, NULL, NULL, 0); + + remove_object(); +} + +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(); +} + +TEST_F(CReadOpsTest, CmpExt) { + char buf[len]; + size_t bytes_read = 0; + int cmpext_val = 0; + int read_val = 0; + + write_object(); + + // cmpext with match should ensure that the following read is successful + rados_read_op_t op = rados_create_read_op(); + ASSERT_TRUE(op); + // @obj, @data and @len correspond to object initialised by write_object() + rados_read_op_cmpext(op, data, len, 0, &cmpext_val); + rados_read_op_read(op, 0, len, buf, &bytes_read, &read_val); + ASSERT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0)); + ASSERT_EQ(len, bytes_read); + ASSERT_EQ(0, memcmp(data, buf, len)); + ASSERT_EQ(cmpext_val, 0); + rados_release_read_op(op); + + // cmpext with mismatch should fail and fill mismatch_buf accordingly + memset(buf, 0, sizeof(buf)); + bytes_read = 0; + cmpext_val = 0; + read_val = 0; + op = rados_create_read_op(); + ASSERT_TRUE(op); + // @obj, @data and @len correspond to object initialised by write_object() + rados_read_op_cmpext(op, "mismatch", strlen("mismatch"), 0, &cmpext_val); + rados_read_op_read(op, 0, len, buf, &bytes_read, &read_val); + ASSERT_EQ(-MAX_ERRNO, rados_read_op_operate(op, ioctx, obj, 0)); + rados_release_read_op(op); + + ASSERT_EQ(-MAX_ERRNO, cmpext_val); + + remove_object(); +}