+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2004-2012 Sage Weil <sage@newdream.net>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
#ifndef CEPH_LIBRADOS_H
#define CEPH_LIBRADOS_H
*/
#define LIBRADOS_LOCK_FLAG_RENEW 0x1
+#define LIBRADOS_CREATE_EXCLUSIVE 1
+#define LIBRADOS_CREATE_IDEMPOTENT 0
+
/**
* @defgroup librados_h_xattr_comp xattr comparison operations
- * @note BUG: there's no way to use these in the C api
* @{
*/
/** @cond TODO_enums_not_yet_in_asphyxiate */
uint64_t num_objects;
};
+/**
+ * @typedef rados_write_op_t
+ *
+ * An object write operation stores a number of operations which can be
+ * executed atomically. For usage, see:
+ * - Creation and deletion: rados_create_write_op() rados_release_write_op()
+ * - Extended attribute manipulation: rados_write_op_cmpxattr()
+ * rados_write_op_cmpxattr(), rados_write_op_setxattr(),
+ * rados_write_op_rmxattr()
+ * - Creating objects: rados_write_op_create()
+ * - IO on objects: rados_write_op_append(), rados_write_op_write(), rados_write_op_zero
+ * rados_write_op_write_full(), rados_write_op_remove, rados_write_op_truncate(),
+ * rados_write_op_zero()
+ * - Performing the operation: rados_write_op_operate(), rados_aio_write_op_operate()
+ */
+typedef void *rados_write_op_t;
+
/**
* Get the version of librados.
*
*/
int rados_notify(rados_ioctx_t io, const char *o, uint64_t ver, const char *buf, int buf_len);
+/** @} Atomic write operations */
+
+/**
+ * Create a new rados_write_op_t write operation. This will store all actions
+ * to be performed atomically. You must call rados_release_write_op when you are
+ * finished with it.
+ *
+ * @returns non-NULL on success, NULL on memory allocation error.
+ */
+rados_write_op_t rados_create_write_op();
+
+/**
+ * Free a rados_write_op_t, must be called when you're done with it.
+ * @param write_op operation to deallocate, created with rados_create_write_op
+ */
+void rados_release_write_op(rados_write_op_t write_op);
+
+/**
+ * Ensure that the object exists before writing
+ * @param write_op operation to add this action to
+ */
+void rados_write_op_assert_exists(rados_write_op_t write_op);
+
+/**
+ * Ensure that given xattr satisfies comparison
+ * @param write_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_write_op_cmpxattr(rados_write_op_t write_op,
+ const char *name,
+ uint8_t comparison_operator,
+ const char *value,
+ size_t value_len);
+
+/**
+ * Set an xattr
+ * @param write_op operation to add this action to
+ * @param name name of the xattr
+ * @param value buffer to set xattr to
+ * @param value_len length of buffer to set xattr to
+ */
+void rados_write_op_setxattr(rados_write_op_t write_op,
+ const char *name,
+ const char *value,
+ size_t value_len);
+
+/**
+ * Remove an xattr
+ * @param write_op operation to add this action to
+ * @param name name of the xattr to remove
+ */
+void rados_write_op_rmxattr(rados_write_op_t write_op, const char *name);
+
+/**
+ * Create the object
+ * @param write_op operation to add this action to
+ * @param exclusive set to either LIBRADOS_CREATE_EXCLUSIVE or
+ LIBRADOS_CREATE_IDEMPOTENT
+ * will error if the object already exists.
+ */
+void rados_write_op_create(rados_write_op_t write_op,
+ int exclusive,
+ const char* category);
+
+/**
+ * Write to offset
+ * @param write_op operation to add this action to
+ * @param offset offset to write to
+ * @param buffer bytes to write
+ * @param len length of buffer
+ */
+void rados_write_op_write(rados_write_op_t write_op,
+ const char *buffer,
+ size_t len,
+ uint64_t offset);
+
+/**
+ * Write whole object, atomically replacing it.
+ * @param write_op operation to add this action to
+ * @param buffer bytes to write
+ * @param len length of buffer
+ */
+void rados_write_op_write_full(rados_write_op_t write_op,
+ const char *buffer,
+ size_t len);
+
+/**
+ * Append to end of object.
+ * @param write_op operation to add this action to
+ * @param buffer bytes to write
+ * @param len length of buffer
+ */
+void rados_write_op_append(rados_write_op_t write_op,
+ const char *buffer,
+ size_t len);
+/**
+ * Remove object
+ * @param write_op operation to add this action to
+ */
+void rados_write_op_remove(rados_write_op_t write_op);
+
+/**
+ * Truncate an object
+ * @param write_op operation to add this action to
+ * @offset Offset to truncate to
+ */
+void rados_write_op_truncate(rados_write_op_t write_op, uint64_t offset);
+
+/**
+ * Zero part of an object
+ * @param write_op operation to add this action to
+ * @offset Offset to zero
+ * @len length to zero
+ */
+void rados_write_op_zero(rados_write_op_t write_op,
+ uint64_t offset,
+ uint64_t len);
+/**
+ * Perform a write operation synchronously
+ * @param write_op operation to perform
+ * @io the ioctx that the object is in
+ * @oid the object id
+ * @mtime the time to set the mtime to, NULL for the current time
+ */
+int rados_write_op_operate(rados_write_op_t write_op,
+ rados_ioctx_t io,
+ const char *oid,
+ time_t *mtime);
+/**
+ * Perform a write operation asynchronously
+ * @param write_op operation to perform
+ * @io the ioctx that the object is in
+ * @param completion what to do when operation has been attempted
+ * @oid the object id
+ * @mtime the time to set the mtime to, NULL for the current time
+ */
+int rados_aio_write_op_operate(rados_write_op_t write_op,
+ rados_ioctx_t io,
+ rados_completion_t completion,
+ const char *oid,
+ time_t *mtime);
+
+
/** @} Watch/Notify */
/**
return ctx.break_lock(o, name, client, cookie);
}
+
+extern "C" rados_write_op_t rados_create_write_op()
+{
+ return new (std::nothrow)::ObjectOperation;
+}
+
+extern "C" void rados_release_write_op(rados_write_op_t write_op)
+{
+ delete (::ObjectOperation*)write_op;
+}
+
+extern "C" void rados_write_op_assert_exists(rados_write_op_t write_op)
+{
+ ((::ObjectOperation *)write_op)->stat(NULL, (utime_t *)NULL, NULL);
+}
+
+extern "C" void rados_write_op_cmpxattr(rados_write_op_t write_op,
+ const char *name,
+ uint8_t comparison_operator,
+ const char *value,
+ size_t value_len)
+{
+ bufferlist bl;
+ bl.append(value, value_len);
+ ((::ObjectOperation *)write_op)->cmpxattr(name,
+ comparison_operator,
+ CEPH_OSD_CMPXATTR_MODE_STRING,
+ bl);
+}
+
+extern "C" void rados_write_op_setxattr(rados_write_op_t write_op,
+ const char *name,
+ const char *value,
+ size_t value_len)
+{
+ bufferlist bl;
+ bl.append(value, value_len);
+ ((::ObjectOperation *)write_op)->setxattr(name, bl);
+}
+
+extern "C" void rados_write_op_rmxattr(rados_write_op_t write_op,
+ const char *name)
+{
+ bufferlist bl;
+ ((::ObjectOperation *)write_op)->rmxattr(name);
+}
+
+extern "C" void rados_write_op_create(rados_write_op_t write_op,
+ int exclusive,
+ const char* category)
+{
+ ::ObjectOperation *oo = (::ObjectOperation *) write_op;
+ if(category) {
+ std::string cpp_category = category;
+ oo->create(exclusive, category);
+ } else {
+ oo->create(!!exclusive);
+ }
+}
+
+extern "C" void rados_write_op_write(rados_write_op_t write_op,
+ const char *buffer,
+ size_t len,
+ uint64_t offset)
+{
+ bufferlist bl;
+ bl.append(buffer,len);
+ ((::ObjectOperation *)write_op)->write(offset, bl);
+}
+
+extern "C" void rados_write_op_write_full(rados_write_op_t write_op,
+ const char *buffer,
+ size_t len)
+{
+ bufferlist bl;
+ bl.append(buffer,len);
+ ((::ObjectOperation *)write_op)->write_full(bl);
+}
+
+extern "C" void rados_write_op_append(rados_write_op_t write_op,
+ const char *buffer,
+ size_t len)
+{
+ bufferlist bl;
+ bl.append(buffer,len);
+ ((::ObjectOperation *)write_op)->append(bl);
+}
+
+extern "C" void rados_write_op_remove(rados_write_op_t write_op)
+{
+ ((::ObjectOperation *)write_op)->remove();
+}
+
+extern "C" void rados_write_op_truncate(rados_write_op_t write_op,
+ uint64_t offset)
+{
+ ((::ObjectOperation *)write_op)->truncate(offset);
+}
+
+extern "C" void rados_write_op_zero(rados_write_op_t write_op,
+ uint64_t offset,
+ uint64_t len)
+{
+ ((::ObjectOperation *)write_op)->zero(offset, len);
+}
+
+extern "C" int rados_write_op_operate(rados_write_op_t write_op,
+ rados_ioctx_t io,
+ const char *oid,
+ time_t *mtime)
+{
+ object_t obj(oid);
+ ::ObjectOperation *oo = (::ObjectOperation *) write_op;
+ librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+ return ctx->operate(obj, oo, mtime);
+}
+
+extern "C" int rados_aio_write_op_operate(rados_write_op_t write_op,
+ rados_ioctx_t io,
+ rados_completion_t completion,
+ const char *oid,
+ time_t *mtime)
+{
+ object_t obj(oid);
+ ::ObjectOperation *oo = (::ObjectOperation *) write_op;
+ librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+ librados::AioCompletionImpl *c = (librados::AioCompletionImpl*)completion;
+ return ctx->aio_operate(obj, oo, c, ctx->snapc, 0);
+}
ceph_test_rados_api_io_CXXFLAGS = $(UNITTEST_CXXFLAGS)
bin_DEBUGPROGRAMS += ceph_test_rados_api_io
+ceph_test_rados_api_c_write_operations_SOURCES = \
+ test/librados/c_write_operations.cc \
+ test/librados/test.cc
+ceph_test_rados_api_c_write_operations_LDADD = $(LIBRADOS) $(UNITTEST_LDADD)
+ceph_test_rados_api_c_write_operations_CXXFLAGS = $(UNITTEST_CXXFLAGS)
+bin_DEBUGPROGRAMS += ceph_test_rados_api_c_write_operations
+
ceph_test_rados_api_aio_SOURCES = \
test/librados/aio.cc \
test/librados/test.cc
--- /dev/null
+// Tests for the C API coverage of atomic write operations
+
+#include "include/rados/librados.h"
+#include "test/librados/test.h"
+#include "gtest/gtest.h"
+
+TEST(LibradosCWriteOps, NewDelete) {
+ rados_write_op_t op = rados_create_write_op();
+ ASSERT_TRUE(op);
+ rados_release_write_op(op);
+}
+
+TEST(LibRadosCWriteOps, assertExists) {
+ rados_t cluster;
+ rados_ioctx_t ioctx;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool(pool_name, &cluster));
+ rados_ioctx_create(cluster, pool_name.c_str(), &ioctx);
+
+ rados_write_op_t op = rados_create_write_op();
+ ASSERT_TRUE(op);
+ rados_write_op_assert_exists(op);
+
+ // -2, ENOENT
+ ASSERT_EQ(-2, rados_write_op_operate(op, ioctx, "test", NULL));
+ rados_release_write_op(op);
+
+ rados_write_op_t op2 = rados_create_write_op();
+ ASSERT_TRUE(op2);
+ rados_write_op_assert_exists(op2);
+
+ rados_completion_t completion;
+ ASSERT_EQ(0, rados_aio_create_completion(NULL, NULL, NULL, &completion));
+ ASSERT_EQ(0, rados_aio_write_op_operate(op2, ioctx, completion, "test", NULL));
+ rados_aio_wait_for_complete(completion);
+ ASSERT_EQ(-2, rados_aio_get_return_value(completion));
+
+ rados_ioctx_destroy(ioctx);
+ rados_release_write_op(op2);
+ ASSERT_EQ(0, destroy_one_pool(pool_name, &cluster));
+}
+
+TEST(LibRadosCWriteOps, Xattrs) {
+ rados_t cluster;
+ rados_ioctx_t ioctx;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool(pool_name, &cluster));
+ rados_ioctx_create(cluster, pool_name.c_str(), &ioctx);
+
+ // Create an object with an xattr
+ rados_write_op_t op = rados_create_write_op();
+ ASSERT_TRUE(op);
+ rados_write_op_create(op, LIBRADOS_CREATE_EXCLUSIVE, NULL);
+ rados_write_op_setxattr(op, "key", "value", 5);
+ ASSERT_EQ(0, rados_write_op_operate(op, ioctx, "test", NULL));
+ rados_release_write_op(op);
+
+ // Check that xattr exists, if it does, delete it.
+ op = rados_create_write_op();
+ ASSERT_TRUE(op);
+ rados_write_op_create(op, LIBRADOS_CREATE_IDEMPOTENT, NULL);
+ rados_write_op_cmpxattr(op, "key", LIBRADOS_CMPXATTR_OP_EQ, "value", 5);
+ rados_write_op_rmxattr(op, "key");
+ ASSERT_EQ(0, rados_write_op_operate(op, ioctx, "test", NULL));
+ rados_release_write_op(op);
+
+ // Check the xattr exits, if it does, add it again (will fail) with -125
+ // (ECANCELED)
+ op = rados_create_write_op();
+ ASSERT_TRUE(op);
+ rados_write_op_cmpxattr(op, "key", LIBRADOS_CMPXATTR_OP_EQ, "value", 5);
+ rados_write_op_setxattr(op, "key", "value", 5);
+ ASSERT_EQ(-125, rados_write_op_operate(op, ioctx, "test", NULL));
+
+ rados_release_write_op(op);
+ ASSERT_EQ(0, destroy_one_pool(pool_name, &cluster));
+}
+
+TEST(LibRadosCWriteOps, Write) {
+ rados_t cluster;
+ rados_ioctx_t ioctx;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool(pool_name, &cluster));
+ rados_ioctx_create(cluster, pool_name.c_str(), &ioctx);
+
+ // Create an object, write and write full to it
+ rados_write_op_t op = rados_create_write_op();
+ ASSERT_TRUE(op);
+ rados_write_op_create(op, LIBRADOS_CREATE_EXCLUSIVE, NULL);
+ rados_write_op_write(op, "four", 4, 0);
+ rados_write_op_write_full(op, "hi", 2);
+ ASSERT_EQ(0, rados_write_op_operate(op, ioctx, "test", NULL));
+ char hi[4];
+ ASSERT_EQ(2, rados_read(ioctx, "test", hi, 4, 0));
+ rados_release_write_op(op);
+
+ // Truncate and append
+ op = rados_create_write_op();
+ ASSERT_TRUE(op);
+ rados_write_op_truncate(op, 1);
+ rados_write_op_append(op, "hi", 2);
+ ASSERT_EQ(0, rados_write_op_operate(op, ioctx, "test", NULL));
+ ASSERT_EQ(3, rados_read(ioctx, "test", hi, 4, 0));
+ rados_release_write_op(op);
+
+ // zero and remove
+ op = rados_create_write_op();
+ ASSERT_TRUE(op);
+ rados_write_op_zero(op, 0, 3);
+ rados_write_op_remove(op);
+ ASSERT_EQ(0, rados_write_op_operate(op, ioctx, "test", NULL));
+ // ENOENT
+ ASSERT_EQ(-2, rados_read(ioctx, "test", hi, 4, 0));
+ rados_release_write_op(op);
+
+ ASSERT_EQ(0, destroy_one_pool(pool_name, &cluster));
+}