summaryrefslogtreecommitdiffstats
path: root/src/include
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:54:28 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:54:28 +0000
commite6918187568dbd01842d8d1d2c808ce16a894239 (patch)
tree64f88b554b444a49f656b6c656111a145cbbaa28 /src/include
parentInitial commit. (diff)
downloadceph-e6918187568dbd01842d8d1d2c808ce16a894239.tar.xz
ceph-e6918187568dbd01842d8d1d2c808ce16a894239.zip
Adding upstream version 18.2.2.upstream/18.2.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/include')
-rw-r--r--src/include/CMakeLists.txt46
-rw-r--r--src/include/CompatSet.h285
-rw-r--r--src/include/Context.h535
-rw-r--r--src/include/Distribution.h73
-rw-r--r--src/include/addr_parsing.h28
-rw-r--r--src/include/alloc_ptr.h91
-rw-r--r--src/include/any.h704
-rw-r--r--src/include/bitmapper.h48
-rw-r--r--src/include/blobhash.h53
-rw-r--r--src/include/btree_map.h68
-rw-r--r--src/include/buffer.h1294
-rw-r--r--src/include/buffer_fwd.h19
-rw-r--r--src/include/buffer_raw.h120
-rw-r--r--src/include/byteorder.h55
-rw-r--r--src/include/ceph_assert.h147
-rw-r--r--src/include/ceph_features.h280
-rw-r--r--src/include/ceph_frag.h109
-rw-r--r--src/include/ceph_fs.h1137
-rw-r--r--src/include/ceph_fuse.h51
-rw-r--r--src/include/ceph_hash.h14
-rw-r--r--src/include/cephfs/ceph_ll_client.h215
-rw-r--r--src/include/cephfs/libcephfs.h2201
-rw-r--r--src/include/cephfs/metrics/Types.h699
-rw-r--r--src/include/cephfs/types.h970
-rw-r--r--src/include/color.h13
-rw-r--r--src/include/common_fwd.h32
-rw-r--r--src/include/compact_map.h383
-rw-r--r--src/include/compact_set.h305
-rw-r--r--src/include/compat.h420
-rw-r--r--src/include/config-h.in.cmake393
-rw-r--r--src/include/coredumpctl.h105
-rw-r--r--src/include/counter.h56
-rw-r--r--src/include/cpp-btree/btree.h2571
-rw-r--r--src/include/cpp-btree/btree_container.h526
-rw-r--r--src/include/cpp-btree/btree_map.h159
-rw-r--r--src/include/cpp-btree/btree_set.h632
-rw-r--r--src/include/cpp_lib_backport.h30
-rw-r--r--src/include/crc32c.h57
-rw-r--r--src/include/demangle.h48
-rw-r--r--src/include/denc.h1895
-rw-r--r--src/include/dlfcn_compat.h48
-rw-r--r--src/include/elist.h193
-rw-r--r--src/include/encoding.h1548
-rw-r--r--src/include/err.h31
-rw-r--r--src/include/error.h41
-rw-r--r--src/include/event_type.h24
-rw-r--r--src/include/expected.hpp2282
-rw-r--r--src/include/filepath.h250
-rw-r--r--src/include/frag.h615
-rw-r--r--src/include/fs_types.h175
-rw-r--r--src/include/function2.hpp1581
-rw-r--r--src/include/hash.h64
-rw-r--r--src/include/health.h83
-rw-r--r--src/include/inline_memory.h150
-rw-r--r--src/include/int_types.h56
-rw-r--r--src/include/intarith.h93
-rw-r--r--src/include/interval_set.h824
-rw-r--r--src/include/ipaddr.h47
-rw-r--r--src/include/krbd.h97
-rw-r--r--src/include/libcephsqlite.h73
-rw-r--r--src/include/linux_fiemap.h73
-rw-r--r--src/include/lru.h241
-rw-r--r--src/include/mempool.h557
-rw-r--r--src/include/msgr.h255
-rw-r--r--src/include/neorados/RADOS.hpp1150
-rw-r--r--src/include/neorados/RADOS_Decodable.hpp116
l---------src/include/neorados/buffer_fwd.h1
l---------src/include/neorados/completion.h1
-rw-r--r--src/include/object.h189
-rw-r--r--src/include/object_fmt.h29
-rw-r--r--src/include/on_exit.h49
-rw-r--r--src/include/page.h18
-rw-r--r--src/include/rados.h700
l---------src/include/rados/buffer.h1
l---------src/include/rados/buffer_fwd.h1
l---------src/include/rados/crc32c.h1
l---------src/include/rados/inline_memory.h1
-rw-r--r--src/include/rados/librados.h4156
-rw-r--r--src/include/rados/librados.hpp1568
-rw-r--r--src/include/rados/librados_fwd.hpp34
-rw-r--r--src/include/rados/librgw.h36
-rw-r--r--src/include/rados/objclass.h177
l---------src/include/rados/page.h1
-rw-r--r--src/include/rados/rados_types.h41
-rw-r--r--src/include/rados/rados_types.hpp341
-rw-r--r--src/include/rados/rgw_file.h431
-rw-r--r--src/include/radosstriper/libradosstriper.h620
-rw-r--r--src/include/radosstriper/libradosstriper.hpp241
-rw-r--r--src/include/random.h301
-rw-r--r--src/include/rangeset.h250
-rw-r--r--src/include/rbd/features.h121
-rw-r--r--src/include/rbd/librbd.h1549
-rw-r--r--src/include/rbd/librbd.hpp869
-rw-r--r--src/include/rbd/object_map_types.h13
-rw-r--r--src/include/rbd_types.h159
-rw-r--r--src/include/scope_guard.h49
-rw-r--r--src/include/sock_compat.h43
-rw-r--r--src/include/spinlock.h92
-rw-r--r--src/include/stat.h145
-rw-r--r--src/include/statlite.h74
-rw-r--r--src/include/str_list.h97
-rw-r--r--src/include/str_map.h180
-rw-r--r--src/include/stringify.h33
-rw-r--r--src/include/timegm.h79
-rw-r--r--src/include/types.h629
-rw-r--r--src/include/unordered_map.h11
-rw-r--r--src/include/unordered_set.h10
-rw-r--r--src/include/uses_allocator.h266
-rw-r--r--src/include/util.h114
-rw-r--r--src/include/utime.cc31
-rw-r--r--src/include/utime.h602
-rw-r--r--src/include/utime_fmt.h47
-rw-r--r--src/include/uuid.cc36
-rw-r--r--src/include/uuid.h107
-rw-r--r--src/include/win32/arpa/inet.h1
-rw-r--r--src/include/win32/dlfcn.h1
-rw-r--r--src/include/win32/fs_compat.h47
-rw-r--r--src/include/win32/ifaddrs.h39
-rw-r--r--src/include/win32/netdb.h1
-rw-r--r--src/include/win32/netinet/in.h1
-rw-r--r--src/include/win32/netinet/ip.h0
-rw-r--r--src/include/win32/netinet/tcp.h0
-rw-r--r--src/include/win32/poll.h1
-rw-r--r--src/include/win32/sys/errno.h1
-rw-r--r--src/include/win32/sys/select.h0
-rw-r--r--src/include/win32/sys/socket.h1
-rw-r--r--src/include/win32/sys/statvfs.h36
-rw-r--r--src/include/win32/sys/uio.h1
-rw-r--r--src/include/win32/sys/un.h1
-rw-r--r--src/include/win32/syslog.h64
-rw-r--r--src/include/win32/win32_errno.h146
-rw-r--r--src/include/win32/winsock_compat.h39
-rw-r--r--src/include/win32/winsock_wrapper.h27
-rw-r--r--src/include/xlist.h237
134 files changed, 42722 insertions, 0 deletions
diff --git a/src/include/CMakeLists.txt b/src/include/CMakeLists.txt
new file mode 100644
index 000000000..cb9c2fea8
--- /dev/null
+++ b/src/include/CMakeLists.txt
@@ -0,0 +1,46 @@
+install(FILES
+ libcephsqlite.h
+ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
+
+install(FILES
+ rados/librados.h
+ rados/rados_types.h
+ rados/rados_types.hpp
+ rados/librados_fwd.hpp
+ rados/librados.hpp
+ buffer.h
+ buffer_fwd.h
+ inline_memory.h
+ page.h
+ crc32c.h
+ rados/objclass.h
+ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/rados)
+if(WITH_LIBRADOSSTRIPER)
+ install(FILES
+ radosstriper/libradosstriper.h
+ radosstriper/libradosstriper.hpp
+ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/radosstriper)
+endif()
+
+if(WITH_RBD)
+ install(FILES
+ rbd/features.h
+ rbd/librbd.h
+ rbd/librbd.hpp
+ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/rbd)
+endif()
+
+if(WITH_RADOSGW)
+ install(FILES
+ rados/librgw.h
+ rados/rgw_file.h
+ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/rados)
+endif()
+
+if(WITH_LIBCEPHFS)
+ install(FILES
+ cephfs/libcephfs.h
+ cephfs/ceph_ll_client.h
+ cephfs/types.h
+ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/cephfs)
+endif()
diff --git a/src/include/CompatSet.h b/src/include/CompatSet.h
new file mode 100644
index 000000000..35c7a7738
--- /dev/null
+++ b/src/include/CompatSet.h
@@ -0,0 +1,285 @@
+// -*- 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) 2009 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_COMPATSET_H
+#define CEPH_COMPATSET_H
+
+#include <iostream>
+#include <map>
+#include <string>
+
+#include "include/buffer.h"
+#include "include/encoding.h"
+#include "include/types.h"
+#include "common/Formatter.h"
+
+struct CompatSet {
+
+ struct Feature {
+ uint64_t id;
+ std::string name;
+
+ Feature(uint64_t _id, const std::string& _name) : id(_id), name(_name) {}
+ };
+
+ class FeatureSet {
+ uint64_t mask;
+ std::map<uint64_t, std::string> names;
+
+ public:
+ friend struct CompatSet;
+ friend class CephCompatSet_AllSet_Test;
+ friend class CephCompatSet_other_Test;
+ friend class CephCompatSet_merge_Test;
+ friend std::ostream& operator<<(std::ostream& out, const CompatSet::FeatureSet& fs);
+ friend std::ostream& operator<<(std::ostream& out, const CompatSet& compat);
+ FeatureSet() : mask(1), names() {}
+ void insert(const Feature& f) {
+ ceph_assert(f.id > 0);
+ ceph_assert(f.id < 64);
+ mask |= ((uint64_t)1<<f.id);
+ names[f.id] = f.name;
+ }
+
+ bool contains(const Feature& f) const {
+ return names.count(f.id);
+ }
+ bool contains(uint64_t f) const {
+ return names.count(f);
+ }
+ /**
+ * Getter instead of using name[] to be const safe
+ */
+ std::string get_name(uint64_t const f) const {
+ std::map<uint64_t, std::string>::const_iterator i = names.find(f);
+ ceph_assert(i != names.end());
+ return i->second;
+ }
+
+ void remove(uint64_t f) {
+ if (names.count(f)) {
+ names.erase(f);
+ mask &= ~((uint64_t)1<<f);
+ }
+ }
+ void remove(const Feature& f) {
+ remove(f.id);
+ }
+
+ void encode(ceph::buffer::list& bl) const {
+ using ceph::encode;
+ /* See below, mask always has the lowest bit set in memory, but
+ * unset in the encoding */
+ encode(mask & (~(uint64_t)1), bl);
+ encode(names, bl);
+ }
+
+ void decode(ceph::buffer::list::const_iterator& bl) {
+ using ceph::decode;
+ decode(mask, bl);
+ decode(names, bl);
+ /**
+ * Previously, there was a bug where insert did
+ * mask |= f.id rather than mask |= (1 << f.id).
+ * In FeatureSets from those version, mask always
+ * has the lowest bit set. Since then, masks always
+ * have the lowest bit unset.
+ *
+ * When we encounter such a FeatureSet, we have to
+ * reconstruct the mask from the names map.
+ */
+ if (mask & 1) {
+ mask = 1;
+ std::map<uint64_t, std::string> temp_names;
+ temp_names.swap(names);
+ for (auto i = temp_names.begin(); i != temp_names.end(); ++i) {
+ insert(Feature(i->first, i->second));
+ }
+ } else {
+ mask |= 1;
+ }
+ }
+
+ void dump(ceph::Formatter *f) const {
+ for (auto p = names.cbegin(); p != names.cend(); ++p) {
+ char s[18];
+ snprintf(s, sizeof(s), "feature_%llu", (unsigned long long)p->first);
+ f->dump_string(s, p->second);
+ }
+ }
+ };
+
+ // These features have no impact on the read / write status
+ FeatureSet compat;
+ // If any of these features are missing, read is possible ( as long
+ // as no incompat feature is missing ) but it is not possible to write
+ FeatureSet ro_compat;
+ // If any of these features are missing, read or write is not possible
+ FeatureSet incompat;
+
+ CompatSet(FeatureSet& _compat, FeatureSet& _ro_compat, FeatureSet& _incompat) :
+ compat(_compat), ro_compat(_ro_compat), incompat(_incompat) {}
+
+ CompatSet() : compat(), ro_compat(), incompat() { }
+
+
+ /* does this filesystem implementation have the
+ features required to read the other? */
+ bool readable(CompatSet const& other) const {
+ return !((other.incompat.mask ^ incompat.mask) & other.incompat.mask);
+ }
+
+ /* does this filesystem implementation have the
+ features required to write the other? */
+ bool writeable(CompatSet const& other) const {
+ return readable(other) &&
+ !((other.ro_compat.mask ^ ro_compat.mask) & other.ro_compat.mask);
+ }
+
+ /* Compare this CompatSet to another.
+ * CAREFULLY NOTE: This operation is NOT commutative.
+ * a > b DOES NOT imply that b < a.
+ * If returns:
+ * 0: The CompatSets have the same feature set.
+ * 1: This CompatSet's features are a strict superset of the other's.
+ * -1: This CompatSet is missing at least one feature
+ * described in the other. It may still have more features, though.
+ */
+ int compare(const CompatSet& other) const {
+ if ((other.compat.mask == compat.mask) &&
+ (other.ro_compat.mask == ro_compat.mask) &&
+ (other.incompat.mask == incompat.mask)) return 0;
+ //okay, they're not the same
+
+ //if we're writeable we have a superset of theirs on incompat and ro_compat
+ if (writeable(other) && !((other.compat.mask ^ compat.mask)
+ & other.compat.mask)) return 1;
+ //if we make it here, we weren't writeable or had a difference compat set
+ return -1;
+ }
+
+ /* Get the features supported by other CompatSet but not this one,
+ * as a CompatSet.
+ */
+ CompatSet unsupported(const CompatSet& other) const {
+ CompatSet diff;
+ uint64_t other_compat =
+ ((other.compat.mask ^ compat.mask) & other.compat.mask);
+ uint64_t other_ro_compat =
+ ((other.ro_compat.mask ^ ro_compat.mask) & other.ro_compat.mask);
+ uint64_t other_incompat =
+ ((other.incompat.mask ^ incompat.mask) & other.incompat.mask);
+ for (int id = 1; id < 64; ++id) {
+ uint64_t mask = (uint64_t)1 << id;
+ if (mask & other_compat) {
+ diff.compat.insert( Feature(id, other.compat.names.at(id)));
+ }
+ if (mask & other_ro_compat) {
+ diff.ro_compat.insert(Feature(id, other.ro_compat.names.at(id)));
+ }
+ if (mask & other_incompat) {
+ diff.incompat.insert( Feature(id, other.incompat.names.at(id)));
+ }
+ }
+ return diff;
+ }
+
+ /* Merge features supported by other CompatSet into this one.
+ * Return: true if some features were merged
+ */
+ bool merge(CompatSet const & other) {
+ uint64_t other_compat =
+ ((other.compat.mask ^ compat.mask) & other.compat.mask);
+ uint64_t other_ro_compat =
+ ((other.ro_compat.mask ^ ro_compat.mask) & other.ro_compat.mask);
+ uint64_t other_incompat =
+ ((other.incompat.mask ^ incompat.mask) & other.incompat.mask);
+ if (!other_compat && !other_ro_compat && !other_incompat)
+ return false;
+ for (int id = 1; id < 64; ++id) {
+ uint64_t mask = (uint64_t)1 << id;
+ if (mask & other_compat) {
+ compat.insert( Feature(id, other.compat.get_name(id)));
+ }
+ if (mask & other_ro_compat) {
+ ro_compat.insert(Feature(id, other.ro_compat.get_name(id)));
+ }
+ if (mask & other_incompat) {
+ incompat.insert( Feature(id, other.incompat.get_name(id)));
+ }
+ }
+ return true;
+ }
+
+ std::ostream& printlite(std::ostream& o) const {
+ o << "{c=[" << std::hex << compat.mask << "]";
+ o << ",r=[" << std::hex << ro_compat.mask << "]";
+ o << ",i=[" << std::hex << incompat.mask << "]}";
+ o << std::dec;
+ return o;
+ }
+
+ void encode(ceph::buffer::list& bl) const {
+ compat.encode(bl);
+ ro_compat.encode(bl);
+ incompat.encode(bl);
+ }
+
+ void decode(ceph::buffer::list::const_iterator& bl) {
+ compat.decode(bl);
+ ro_compat.decode(bl);
+ incompat.decode(bl);
+ }
+
+ void dump(ceph::Formatter *f) const {
+ f->open_object_section("compat");
+ compat.dump(f);
+ f->close_section();
+ f->open_object_section("ro_compat");
+ ro_compat.dump(f);
+ f->close_section();
+ f->open_object_section("incompat");
+ incompat.dump(f);
+ f->close_section();
+ }
+
+ static void generate_test_instances(std::list<CompatSet*>& o) {
+ o.push_back(new CompatSet);
+ o.push_back(new CompatSet);
+ o.back()->compat.insert(Feature(1, "one"));
+ o.back()->compat.insert(Feature(2, "two"));
+ o.back()->ro_compat.insert(Feature(4, "four"));
+ o.back()->incompat.insert(Feature(3, "three"));
+ }
+};
+WRITE_CLASS_ENCODER(CompatSet)
+
+inline std::ostream& operator<<(std::ostream& out, const CompatSet::Feature& f)
+{
+ return out << "F(" << f.id << ", \"" << f.name << "\")";
+}
+
+inline std::ostream& operator<<(std::ostream& out, const CompatSet::FeatureSet& fs)
+{
+ return out << fs.names;
+}
+
+inline std::ostream& operator<<(std::ostream& out, const CompatSet& compat)
+{
+ return out << "compat=" << compat.compat
+ << ",rocompat=" << compat.ro_compat
+ << ",incompat=" << compat.incompat;
+}
+
+#endif
diff --git a/src/include/Context.h b/src/include/Context.h
new file mode 100644
index 000000000..bef85ca5b
--- /dev/null
+++ b/src/include/Context.h
@@ -0,0 +1,535 @@
+// -*- 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-2006 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_CONTEXT_H
+#define CEPH_CONTEXT_H
+
+#include "common/dout.h"
+
+#include <functional>
+#include <list>
+#include <memory>
+#include <set>
+
+#include <boost/function.hpp>
+#include <boost/system/error_code.hpp>
+
+#include "common/error_code.h"
+
+#include "include/ceph_assert.h"
+#include "common/ceph_mutex.h"
+
+#define mydout(cct, v) lgeneric_subdout(cct, context, v)
+
+/*
+ * GenContext - abstract callback class
+ */
+template <typename T>
+class GenContext {
+ GenContext(const GenContext& other);
+ const GenContext& operator=(const GenContext& other);
+
+ protected:
+ virtual void finish(T t) = 0;
+
+ public:
+ GenContext() {}
+ virtual ~GenContext() {} // we want a virtual destructor!!!
+
+ template <typename C>
+ void complete(C &&t) {
+ finish(std::forward<C>(t));
+ delete this;
+ }
+
+ template <typename C>
+ void operator()(C &&t) noexcept {
+ complete(std::forward<C>(t));
+ }
+
+ template<typename U = T>
+ auto operator()() noexcept
+ -> typename std::enable_if<std::is_default_constructible<U>::value,
+ void>::type {
+ complete(T{});
+ }
+
+
+ std::reference_wrapper<GenContext> func() {
+ return std::ref(*this);
+ }
+};
+
+template <typename T>
+using GenContextURef = std::unique_ptr<GenContext<T> >;
+
+/*
+ * Context - abstract callback class
+ */
+class Finisher;
+class Context {
+ Context(const Context& other);
+ const Context& operator=(const Context& other);
+
+ protected:
+ virtual void finish(int r) = 0;
+
+ // variant of finish that is safe to call "synchronously." override should
+ // return true.
+ virtual bool sync_finish(int r) {
+ return false;
+ }
+
+ public:
+ Context() {}
+ virtual ~Context() {} // we want a virtual destructor!!!
+ virtual void complete(int r) {
+ finish(r);
+ delete this;
+ }
+ virtual bool sync_complete(int r) {
+ if (sync_finish(r)) {
+ delete this;
+ return true;
+ }
+ return false;
+ }
+ void complete(boost::system::error_code ec) {
+ complete(ceph::from_error_code(ec));
+ }
+ void operator()(boost::system::error_code ec) noexcept {
+ complete(ec);
+ }
+
+ void operator()() noexcept {
+ complete({});
+ }
+
+ std::reference_wrapper<Context> func() {
+ return std::ref(*this);
+ }
+};
+
+/**
+ * Simple context holding a single object
+ */
+template<class T>
+class ContainerContext : public Context {
+ T obj;
+public:
+ ContainerContext(T &obj) : obj(obj) {}
+ void finish(int r) override {}
+};
+template <typename T>
+ContainerContext<T> *make_container_context(T &&t) {
+ return new ContainerContext<T>(std::forward<T>(t));
+}
+
+template <class T>
+struct Wrapper : public Context {
+ Context *to_run;
+ T val;
+ Wrapper(Context *to_run, T val) : to_run(to_run), val(val) {}
+ void finish(int r) override {
+ if (to_run)
+ to_run->complete(r);
+ }
+};
+struct RunOnDelete {
+ Context *to_run;
+ RunOnDelete(Context *to_run) : to_run(to_run) {}
+ ~RunOnDelete() {
+ if (to_run)
+ to_run->complete(0);
+ }
+};
+typedef std::shared_ptr<RunOnDelete> RunOnDeleteRef;
+
+template <typename T>
+class LambdaContext : public Context {
+public:
+ LambdaContext(T &&t) : t(std::forward<T>(t)) {}
+ void finish(int r) override {
+ if constexpr (std::is_invocable_v<T, int>)
+ t(r);
+ else
+ t();
+ }
+private:
+ T t;
+};
+
+template <typename T>
+LambdaContext<T> *make_lambda_context(T &&t) {
+ return new LambdaContext<T>(std::move(t));
+}
+
+template <typename F, typename T>
+struct LambdaGenContext : GenContext<T> {
+ F f;
+ LambdaGenContext(F &&f) : f(std::forward<F>(f)) {}
+ void finish(T t) override {
+ f(std::forward<T>(t));
+ }
+};
+template <typename T, typename F>
+GenContextURef<T> make_gen_lambda_context(F &&f) {
+ return GenContextURef<T>(new LambdaGenContext<F, T>(std::move(f)));
+}
+
+/*
+ * finish and destroy a list of Contexts
+ */
+template<class C>
+inline void finish_contexts(CephContext *cct, C& finished, int result = 0)
+{
+ if (finished.empty())
+ return;
+
+ C ls;
+ ls.swap(finished); // swap out of place to avoid weird loops
+
+ if (cct)
+ mydout(cct,10) << ls.size() << " contexts to finish with " << result << dendl;
+ for (Context* c : ls) {
+ if (cct)
+ mydout(cct,10) << "---- " << c << dendl;
+ c->complete(result);
+ }
+}
+
+class C_NoopContext : public Context {
+public:
+ void finish(int r) override { }
+};
+
+
+struct C_Lock : public Context {
+ ceph::mutex *lock;
+ Context *fin;
+ C_Lock(ceph::mutex *l, Context *c) : lock(l), fin(c) {}
+ ~C_Lock() override {
+ delete fin;
+ }
+ void finish(int r) override {
+ if (fin) {
+ std::lock_guard l{*lock};
+ fin->complete(r);
+ fin = NULL;
+ }
+ }
+};
+
+/*
+ * C_Contexts - set of Contexts
+ *
+ * ContextType must be an ancestor class of ContextInstanceType, or the same class.
+ * ContextInstanceType must be default-constructable.
+ */
+template <class ContextType, class ContextInstanceType, class Container = std::list<ContextType *>>
+class C_ContextsBase : public ContextInstanceType {
+public:
+ CephContext *cct;
+ Container contexts;
+
+ C_ContextsBase(CephContext *cct_)
+ : cct(cct_)
+ {
+ }
+ ~C_ContextsBase() override {
+ for (auto c : contexts) {
+ delete c;
+ }
+ }
+ void add(ContextType* c) {
+ contexts.push_back(c);
+ }
+ void take(Container& ls) {
+ Container c;
+ c.swap(ls);
+ if constexpr (std::is_same_v<Container, std::list<ContextType *>>) {
+ contexts.splice(contexts.end(), c);
+ } else {
+ contexts.insert(contexts.end(), c.begin(), c.end());
+ }
+ }
+ void complete(int r) override {
+ // Neuter any ContextInstanceType custom complete(), because although
+ // I want to look like it, I don't actually want to run its code.
+ Context::complete(r);
+ }
+ void finish(int r) override {
+ finish_contexts(cct, contexts, r);
+ }
+ bool empty() { return contexts.empty(); }
+
+ template<class C>
+ static ContextType *list_to_context(C& cs) {
+ if (cs.size() == 0) {
+ return 0;
+ } else if (cs.size() == 1) {
+ ContextType *c = cs.front();
+ cs.clear();
+ return c;
+ } else {
+ C_ContextsBase<ContextType, ContextInstanceType> *c(new C_ContextsBase<ContextType, ContextInstanceType>(0));
+ c->take(cs);
+ return c;
+ }
+ }
+};
+
+typedef C_ContextsBase<Context, Context> C_Contexts;
+
+/*
+ * C_Gather
+ *
+ * ContextType must be an ancestor class of ContextInstanceType, or the same class.
+ * ContextInstanceType must be default-constructable.
+ *
+ * BUG:? only reports error from last sub to have an error return
+ */
+template <class ContextType, class ContextInstanceType>
+class C_GatherBase {
+private:
+ CephContext *cct;
+ int result = 0;
+ ContextType *onfinish;
+#ifdef DEBUG_GATHER
+ std::set<ContextType*> waitfor;
+#endif
+ int sub_created_count = 0;
+ int sub_existing_count = 0;
+ mutable ceph::recursive_mutex lock =
+ ceph::make_recursive_mutex("C_GatherBase::lock"); // disable lockdep
+ bool activated = false;
+
+ void sub_finish(ContextType* sub, int r) {
+ lock.lock();
+#ifdef DEBUG_GATHER
+ ceph_assert(waitfor.count(sub));
+ waitfor.erase(sub);
+#endif
+ --sub_existing_count;
+ mydout(cct,10) << "C_GatherBase " << this << ".sub_finish(r=" << r << ") " << sub
+#ifdef DEBUG_GATHER
+ << " (remaining " << waitfor << ")"
+#endif
+ << dendl;
+ if (r < 0 && result == 0)
+ result = r;
+ if ((activated == false) || (sub_existing_count != 0)) {
+ lock.unlock();
+ return;
+ }
+ lock.unlock();
+ delete_me();
+ }
+
+ void delete_me() {
+ if (onfinish) {
+ onfinish->complete(result);
+ onfinish = 0;
+ }
+ delete this;
+ }
+
+ class C_GatherSub : public ContextInstanceType {
+ C_GatherBase *gather;
+ public:
+ C_GatherSub(C_GatherBase *g) : gather(g) {}
+ void complete(int r) override {
+ // Cancel any customized complete() functionality
+ // from the Context subclass we're templated for,
+ // we only want to hit that in onfinish, not at each
+ // sub finish. e.g. MDSInternalContext.
+ Context::complete(r);
+ }
+ void finish(int r) override {
+ gather->sub_finish(this, r);
+ gather = 0;
+ }
+ ~C_GatherSub() override {
+ if (gather)
+ gather->sub_finish(this, 0);
+ }
+ };
+
+public:
+ C_GatherBase(CephContext *cct_, ContextType *onfinish_)
+ : cct(cct_), onfinish(onfinish_)
+ {
+ mydout(cct,10) << "C_GatherBase " << this << ".new" << dendl;
+ }
+ ~C_GatherBase() {
+ mydout(cct,10) << "C_GatherBase " << this << ".delete" << dendl;
+ }
+ void set_finisher(ContextType *onfinish_) {
+ std::lock_guard l{lock};
+ ceph_assert(!onfinish);
+ onfinish = onfinish_;
+ }
+ void activate() {
+ lock.lock();
+ ceph_assert(activated == false);
+ activated = true;
+ if (sub_existing_count != 0) {
+ lock.unlock();
+ return;
+ }
+ lock.unlock();
+ delete_me();
+ }
+ ContextType *new_sub() {
+ std::lock_guard l{lock};
+ ceph_assert(activated == false);
+ sub_created_count++;
+ sub_existing_count++;
+ ContextType *s = new C_GatherSub(this);
+#ifdef DEBUG_GATHER
+ waitfor.insert(s);
+#endif
+ mydout(cct,10) << "C_GatherBase " << this << ".new_sub is " << sub_created_count << " " << s << dendl;
+ return s;
+ }
+
+ inline int get_sub_existing_count() const {
+ std::lock_guard l{lock};
+ return sub_existing_count;
+ }
+
+ inline int get_sub_created_count() const {
+ std::lock_guard l{lock};
+ return sub_created_count;
+ }
+};
+
+/*
+ * The C_GatherBuilder remembers each C_Context created by
+ * C_GatherBuilder.new_sub() in a C_Gather. When a C_Context created
+ * by new_sub() is complete(), C_Gather forgets about it. When
+ * C_GatherBuilder notices that there are no C_Context left in
+ * C_Gather, it calls complete() on the C_Context provided as the
+ * second argument of the constructor (finisher).
+ *
+ * How to use C_GatherBuilder:
+ *
+ * 1. Create a C_GatherBuilder on the stack
+ * 2. Call gather_bld.new_sub() as many times as you want to create new subs
+ * It is safe to call this 0 times, or 100, or anything in between.
+ * 3. If you didn't supply a finisher in the C_GatherBuilder constructor,
+ * set one with gather_bld.set_finisher(my_finisher)
+ * 4. Call gather_bld.activate()
+ *
+ * Example:
+ *
+ * C_SaferCond all_done;
+ * C_GatherBuilder gb(g_ceph_context, all_done);
+ * j.submit_entry(1, first, 0, gb.new_sub()); // add a C_Context to C_Gather
+ * j.submit_entry(2, first, 0, gb.new_sub()); // add a C_Context to C_Gather
+ * gb.activate(); // consume C_Context as soon as they complete()
+ * all_done.wait(); // all_done is complete() after all new_sub() are complete()
+ *
+ * The finisher may be called at any point after step 4, including immediately
+ * from the activate() function.
+ * The finisher will never be called before activate().
+ *
+ * Note: Currently, subs must be manually freed by the caller (for some reason.)
+ */
+template <class ContextType, class GatherType>
+class C_GatherBuilderBase
+{
+public:
+ C_GatherBuilderBase(CephContext *cct_)
+ : cct(cct_), c_gather(NULL), finisher(NULL), activated(false)
+ {
+ }
+ C_GatherBuilderBase(CephContext *cct_, ContextType *finisher_)
+ : cct(cct_), c_gather(NULL), finisher(finisher_), activated(false)
+ {
+ }
+ ~C_GatherBuilderBase() {
+ if (c_gather) {
+ ceph_assert(activated); // Don't forget to activate your C_Gather!
+ }
+ else {
+ delete finisher;
+ }
+ }
+ ContextType *new_sub() {
+ if (!c_gather) {
+ c_gather = new GatherType(cct, finisher);
+ }
+ return c_gather->new_sub();
+ }
+ void activate() {
+ if (!c_gather)
+ return;
+ ceph_assert(finisher != NULL);
+ activated = true;
+ c_gather->activate();
+ }
+ void set_finisher(ContextType *finisher_) {
+ finisher = finisher_;
+ if (c_gather)
+ c_gather->set_finisher(finisher);
+ }
+ GatherType *get() const {
+ return c_gather;
+ }
+ bool has_subs() const {
+ return (c_gather != NULL);
+ }
+ int num_subs_created() {
+ ceph_assert(!activated);
+ if (c_gather == NULL)
+ return 0;
+ return c_gather->get_sub_created_count();
+ }
+ int num_subs_remaining() {
+ ceph_assert(!activated);
+ if (c_gather == NULL)
+ return 0;
+ return c_gather->get_sub_existing_count();
+ }
+
+private:
+ CephContext *cct;
+ GatherType *c_gather;
+ ContextType *finisher;
+ bool activated;
+};
+
+typedef C_GatherBase<Context, Context> C_Gather;
+typedef C_GatherBuilderBase<Context, C_Gather > C_GatherBuilder;
+
+template <class ContextType>
+class ContextFactory {
+public:
+ virtual ~ContextFactory() {}
+ virtual ContextType *build() = 0;
+};
+
+inline auto lambdafy(Context *c) {
+ return [fin = std::unique_ptr<Context>(c)]
+ (boost::system::error_code ec) mutable {
+ fin.release()->complete(ceph::from_error_code(ec));
+ };
+}
+
+
+#undef mydout
+
+#endif
diff --git a/src/include/Distribution.h b/src/include/Distribution.h
new file mode 100644
index 000000000..56e998757
--- /dev/null
+++ b/src/include/Distribution.h
@@ -0,0 +1,73 @@
+// -*- 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-2006 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_DISTRIBUTION_H
+#define CEPH_DISTRIBUTION_H
+
+#include <vector>
+
+class Distribution {
+ std::vector<float> p;
+ std::vector<int> v;
+
+ public:
+ //Distribution() {
+ //}
+
+ unsigned get_width() {
+ return p.size();
+ }
+
+ void clear() {
+ p.clear();
+ v.clear();
+ }
+ void add(int val, float pr) {
+ p.push_back(pr);
+ v.push_back(val);
+ }
+
+ void random() {
+ float sum = 0.0;
+ for (unsigned i=0; i<p.size(); i++) {
+ p[i] = (float)(rand() % 10000);
+ sum += p[i];
+ }
+ for (unsigned i=0; i<p.size(); i++)
+ p[i] /= sum;
+ }
+
+ int sample() {
+ float s = (float)(rand() % 10000) / 10000.0;
+ for (unsigned i=0; i<p.size(); i++) {
+ if (s < p[i]) return v[i];
+ s -= p[i];
+ }
+ ceph_abort();
+ return v[p.size() - 1]; // hmm. :/
+ }
+
+ float normalize() {
+ float s = 0.0;
+ for (unsigned i=0; i<p.size(); i++)
+ s += p[i];
+ for (unsigned i=0; i<p.size(); i++)
+ p[i] /= s;
+ return s;
+ }
+
+};
+
+#endif
diff --git a/src/include/addr_parsing.h b/src/include/addr_parsing.h
new file mode 100644
index 000000000..c205ac75f
--- /dev/null
+++ b/src/include/addr_parsing.h
@@ -0,0 +1,28 @@
+/*
+ * addr_parsing.h
+ *
+ * Created on: Sep 14, 2010
+ * Author: gregf
+ * contains functions used by Ceph to convert named addresses
+ * (eg ceph.com) into IP addresses (ie 127.0.0.1).
+ */
+
+#ifndef ADDR_PARSING_H_
+#define ADDR_PARSING_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int safe_cat(char **pstr, int *plen, int pos, const char *str2);
+
+/*
+ * returns a string allocated by malloc; caller must free
+ */
+char *resolve_addrs(const char *orig_str);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ADDR_PARSING_H_ */
diff --git a/src/include/alloc_ptr.h b/src/include/alloc_ptr.h
new file mode 100644
index 000000000..258c58338
--- /dev/null
+++ b/src/include/alloc_ptr.h
@@ -0,0 +1,91 @@
+// -*- 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) 2017 Red Hat, Inc.
+ *
+ * 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_ALLOC_PTR_H
+#define CEPH_ALLOC_PTR_H
+
+#include <memory>
+
+template <class T>
+class alloc_ptr
+{
+public:
+ typedef typename std::pointer_traits< std::unique_ptr<T> >::pointer pointer;
+ typedef typename std::pointer_traits< std::unique_ptr<T> >::element_type element_type;
+
+ alloc_ptr() : ptr() {}
+
+ template<class U>
+ alloc_ptr(U&& u) : ptr(std::forward<U>(u)) {}
+
+ alloc_ptr(alloc_ptr<pointer>&& rhs) : ptr(std::move(rhs.ptr)) {}
+ alloc_ptr(const alloc_ptr<pointer>& rhs) = delete;
+ alloc_ptr& operator=(const alloc_ptr<pointer>&& rhs) {
+ ptr = rhs.ptr;
+ }
+ alloc_ptr& operator=(const alloc_ptr<pointer>& rhs) {
+ ptr = rhs.ptr;
+ }
+
+ void swap (alloc_ptr<pointer>& rhs) {
+ ptr.swap(rhs.ptr);
+ }
+ element_type* release() {
+ return ptr.release();
+ }
+ void reset(element_type *p = nullptr) {
+ ptr.reset(p);
+ }
+ element_type* get() const {
+ if (!ptr)
+ ptr.reset(new element_type);
+ return ptr.get();
+ }
+ element_type& operator*() const {
+ if (!ptr)
+ ptr.reset(new element_type);
+ return *ptr;
+ }
+ element_type* operator->() const {
+ if (!ptr)
+ ptr.reset(new element_type);
+ return ptr.get();
+ }
+ operator bool() const {
+ return !!ptr;
+ }
+
+ friend bool operator< (const alloc_ptr& lhs, const alloc_ptr& rhs) {
+ return std::less<element_type>(*lhs, *rhs);
+ }
+ friend bool operator<=(const alloc_ptr& lhs, const alloc_ptr& rhs) {
+ return std::less_equal<element_type>(*lhs, *rhs);
+ }
+ friend bool operator> (const alloc_ptr& lhs, const alloc_ptr& rhs) {
+ return std::greater<element_type>(*lhs, *rhs);
+ }
+ friend bool operator>=(const alloc_ptr& lhs, const alloc_ptr& rhs) {
+ return std::greater_equal<element_type>(*lhs, *rhs);
+ }
+ friend bool operator==(const alloc_ptr& lhs, const alloc_ptr& rhs) {
+ return *lhs == *rhs;
+ }
+ friend bool operator!=(const alloc_ptr& lhs, const alloc_ptr& rhs) {
+ return *lhs != *rhs;
+ }
+private:
+ mutable std::unique_ptr<element_type> ptr;
+};
+
+#endif
diff --git a/src/include/any.h b/src/include/any.h
new file mode 100644
index 000000000..da59c88f4
--- /dev/null
+++ b/src/include/any.h
@@ -0,0 +1,704 @@
+// -*- 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) 2018 Adam C. Emerson <aemerson@redhat.com>
+ *
+ * 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 INCLUDE_STATIC_ANY
+#define INCLUDE_STATIC_ANY
+
+#include <any>
+#include <cstddef>
+#include <initializer_list>
+#include <memory>
+#include <typeinfo>
+#include <type_traits>
+
+#include <boost/smart_ptr/shared_ptr.hpp>
+#include <boost/smart_ptr/make_shared.hpp>
+
+namespace ceph {
+
+namespace _any {
+
+// Shared Functionality
+// --------------------
+//
+// Common implementation details. Most functionality is here. We
+// assume that destructors do not throw. Some of them might and
+// they'll invoke terminate and that's fine.
+//
+// We are using the Curiously Recurring Template Pattern! We require
+// that all classes inheriting from us provide:
+//
+// - `static constexpr size_t capacity`: Maximum capacity. No object
+// larger than this may be
+// stored. `dynamic` for dynamic.
+// - `void* ptr() const noexcept`: returns a pointer to storage.
+// (`alloc_storage` must have been called.
+// `free_storage` must not have been called
+// since.)
+// - `void* alloc_storage(const std::size_t)`: allocate storage
+// - `void free_storage() noexcept`: free storage. Must be idempotent.
+//
+// We provide most of the public interface, as well as the operator function,
+// cast_helper, and the type() call.
+
+// Set `capacity` to this value to indicate that there is no fixed
+// capacity.
+//
+inline constexpr std::size_t dynamic = ~0;
+
+// Driver Function
+// ---------------
+//
+// The usual type-erasure control function trick. This one is simpler
+// than usual since we punt on moving and copying. We could dispense
+// with this and just store a deleter and a pointer to a typeinfo, but
+// that would be twice the space.
+//
+// Moved out here so the type of `func_t` isn't dependent on the
+// enclosing class.
+//
+enum class op { type, destroy };
+template<typename T>
+inline void op_func(const op o, void* p) noexcept {
+ static const std::type_info& type = typeid(T);
+ switch (o) {
+ case op::type:
+ *(reinterpret_cast<const std::type_info**>(p)) = &type;
+ break;
+ case op::destroy:
+ reinterpret_cast<T*>(p)->~T();
+ break;
+ }
+}
+using func_t = void (*)(const op, void* p) noexcept;
+
+// The base class
+// --------------
+//
+// The `storage_t` parameter gives the type of the value that manages
+// storage and allocation. We use it to create a protected data member
+// (named `storage`). This allows us to sidestep the problem in
+// initialization order where, where exposed constructors were using
+// trying to allocate or free storage *before* the data members of the
+// derived class were initialized.
+//
+// Making storage_t a member type of the derived class won't work, due
+// to C++'s rules for nested types being *horrible*. Just downright
+// *horrible*.
+//
+template<typename D, typename storage_t>
+class base {
+ // Make definitions from our superclass visible
+ // --------------------------------------------
+ //
+ // And check that they fit the requirements. At least those that are
+ // statically checkable.
+ //
+ static constexpr std::size_t capacity = D::capacity;
+
+ void* ptr() const noexcept {
+ static_assert(
+ noexcept(static_cast<const D*>(this)->ptr()) &&
+ std::is_same_v<decltype(static_cast<const D*>(this)->ptr()), void*>,
+ "‘void* ptr() const noexcept’ missing from superclass");
+ return static_cast<const D*>(this)->ptr();
+ }
+
+ void* alloc_storage(const std::size_t z) {
+ static_assert(
+ std::is_same_v<decltype(static_cast<D*>(this)->alloc_storage(z)), void*>,
+ "‘void* alloc_storage(const size_t)’ missing from superclass.");
+ return static_cast<D*>(this)->alloc_storage(z);
+ }
+
+ void free_storage() noexcept {
+ static_assert(
+ noexcept(static_cast<D*>(this)->free_storage()) &&
+ std::is_void_v<decltype(static_cast<D*>(this)->free_storage())>,
+ "‘void free_storage() noexcept’ missing from superclass.");
+ static_cast<D*>(this)->free_storage();
+ }
+
+
+ // Pile O' Templates
+ // -----------------
+ //
+ // These are just verbose and better typed once than twice. They're
+ // used for SFINAE and declaring noexcept.
+ //
+ template<class T>
+ struct is_in_place_type_helper : std::false_type {};
+ template<class T>
+ struct is_in_place_type_helper<std::in_place_type_t<T>> : std::true_type {};
+
+ template<class T>
+ static constexpr bool is_in_place_type_v =
+ is_in_place_type_helper<std::decay_t<T>>::value;
+
+ // SFINAE condition for value initialized
+ // constructors/assigners. This is analogous to the standard's
+ // requirement that this overload only participate in overload
+ // resolution if std::decay_t<T> is not the same type as the
+ // any-type, nor a specialization of std::in_place_type_t
+ //
+ template<typename T>
+ using value_condition_t = std::enable_if_t<
+ !std::is_same_v<std::decay_t<T>, D> &&
+ !is_in_place_type_v<std::decay_t<T>>>;
+
+ // This `noexcept` condition for value construction lets
+ // `immobile_any`'s value constructor/assigner be noexcept, so long
+ // as the type's copy or move constructor cooperates.
+ //
+ template<typename T>
+ static constexpr bool value_noexcept_v =
+ std::is_nothrow_constructible_v<std::decay_t<T>, T> && capacity != dynamic;
+
+ // SFINAE condition for in-place constructors/assigners
+ //
+ template<typename T, typename... Args>
+ using in_place_condition_t = std::enable_if_t<std::is_constructible_v<
+ std::decay_t<T>, Args...>>;
+
+ // Analogous to the above. Give noexcept to immobile_any::emplace
+ // when possible.
+ //
+ template<typename T, typename... Args>
+ static constexpr bool in_place_noexcept_v =
+ std::is_nothrow_constructible_v<std::decay_t<T>, Args...> &&
+ capacity != dynamic;
+
+private:
+
+ // Functionality!
+ // --------------
+
+ // The driver function for the currently stored object. Whether this
+ // is null is the canonical way to know whether an instance has a
+ // value.
+ //
+ func_t func = nullptr;
+
+ // Construct an object within ourselves. As you can see we give the
+ // weak exception safety guarantee.
+ //
+ template<typename T, typename ...Args>
+ std::decay_t<T>& construct(Args&& ...args) {
+ using Td = std::decay_t<T>;
+ static_assert(capacity == dynamic || sizeof(Td) <= capacity,
+ "Supplied type is too large for this specialization.");
+ try {
+ func = &op_func<Td>;
+ return *new (reinterpret_cast<Td*>(alloc_storage(sizeof(Td))))
+ Td(std::forward<Args>(args)...);
+ } catch (...) {
+ reset();
+ throw;
+ }
+ }
+
+protected:
+
+ // We hold the storage, even if the superclass class manipulates it,
+ // so that its default initialization comes soon enough for us to
+ // use it in our constructors.
+ //
+ storage_t storage;
+
+public:
+
+ base() noexcept = default;
+ ~base() noexcept {
+ reset();
+ }
+
+protected:
+ // Since some of our derived classes /can/ be copied or moved.
+ //
+ base(const base& rhs) noexcept : func(rhs.func) {
+ if constexpr (std::is_copy_assignable_v<storage_t>) {
+ storage = rhs.storage;
+ }
+ }
+ base& operator =(const base& rhs) noexcept {
+ reset();
+ func = rhs.func;
+ if constexpr (std::is_copy_assignable_v<storage_t>) {
+ storage = rhs.storage;
+ }
+ return *this;
+ }
+
+ base(base&& rhs) noexcept : func(std::move(rhs.func)) {
+ if constexpr (std::is_move_assignable_v<storage_t>) {
+ storage = std::move(rhs.storage);
+ }
+ rhs.func = nullptr;
+ }
+ base& operator =(base&& rhs) noexcept {
+ reset();
+ func = rhs.func;
+ if constexpr (std::is_move_assignable_v<storage_t>) {
+ storage = std::move(rhs.storage);
+ }
+ rhs.func = nullptr;
+ return *this;
+ }
+
+public:
+
+ // Value construct/assign
+ // ----------------------
+ //
+ template<typename T,
+ typename = value_condition_t<T>>
+ base(T&& t) noexcept(value_noexcept_v<T>) {
+ construct<T>(std::forward<T>(t));
+ }
+
+ // On exception, *this is set to empty.
+ //
+ template<typename T,
+ typename = value_condition_t<T>>
+ base& operator =(T&& t) noexcept(value_noexcept_v<T>) {
+ reset();
+ construct<T>(std::forward<T>(t));
+ return *this;
+ }
+
+ // In-place construct/assign
+ // -------------------------
+ //
+ // I really hate the way the C++ standard library treats references
+ // as if they were stepchildren in a Charles Dickens novel. I am
+ // quite upset that std::optional lacks a specialization for
+ // references. There's no legitimate reason for it. The whole
+ // 're-seat or refuse' debate is simply a canard. The optional is
+ // effectively a container, so of course it can be emptied or
+ // reassigned. No, pointers are not an acceptable substitute. A
+ // pointer gives an address in memory which may be null and which
+ // may represent an object or may a location in which an object is
+ // to be created. An optional reference, on the other hand, is a
+ // reference to an initialized, live object or /empty/. This is an
+ // obvious difference that should be communicable to any programmer
+ // reading the code through the type system.
+ //
+ // `std::any`, even in the case of in-place construction,
+ // only stores the decayed type. I suspect this was to get around
+ // the question of whether, for a std::any holding a T&,
+ // std::any_cast<T> should return a copy or throw
+ // std::bad_any_cast.
+ //
+ // I think the appropriate response in that case would be to make a
+ // copy if the type supports it and fail otherwise. Once a concrete
+ // type is known the problem solves itself.
+ //
+ // If one were inclined, one could easily load the driver function
+ // with a heavy subset of the type traits (those that depend only on
+ // the type in question) and simply /ask/ whether it's a reference.
+ //
+ // At the moment, I'm maintaining compatibility with the standard
+ // library except for copy/move semantics.
+ //
+ template<typename T,
+ typename... Args,
+ typename = in_place_condition_t<T, Args...>>
+ base(std::in_place_type_t<T>,
+ Args&& ...args) noexcept(in_place_noexcept_v<T, Args...>) {
+ construct<T>(std::forward<Args>(args)...);
+ }
+
+ // On exception, *this is set to empty.
+ //
+ template<typename T,
+ typename... Args,
+ typename = in_place_condition_t<T>>
+ std::decay_t<T>& emplace(Args&& ...args) noexcept(in_place_noexcept_v<
+ T, Args...>) {
+ reset();
+ return construct<T>(std::forward<Args>(args)...);
+ }
+
+ template<typename T,
+ typename U,
+ typename... Args,
+ typename = in_place_condition_t<T, std::initializer_list<U>,
+ Args...>>
+ base(std::in_place_type_t<T>,
+ std::initializer_list<U> i,
+ Args&& ...args) noexcept(in_place_noexcept_v<T, std::initializer_list<U>,
+ Args...>) {
+ construct<T>(i, std::forward<Args>(args)...);
+ }
+
+ // On exception, *this is set to empty.
+ //
+ template<typename T,
+ typename U,
+ typename... Args,
+ typename = in_place_condition_t<T, std::initializer_list<U>,
+ Args...>>
+ std::decay_t<T>& emplace(std::initializer_list<U> i,
+ Args&& ...args) noexcept(in_place_noexcept_v<T,
+ std::initializer_list<U>,
+ Args...>) {
+ reset();
+ return construct<T>(i,std::forward<Args>(args)...);
+ }
+
+ // Empty ourselves, using the subclass to free any storage.
+ //
+ void reset() noexcept {
+ if (has_value()) {
+ func(op::destroy, ptr());
+ func = nullptr;
+ }
+ free_storage();
+ }
+
+ template<typename U = storage_t,
+ typename = std::enable_if<std::is_swappable_v<storage_t>>>
+ void swap(base& rhs) {
+ using std::swap;
+ swap(func, rhs.func);
+ swap(storage, rhs.storage);
+ }
+
+ // All other functions should use this function to test emptiness
+ // rather than examining `func` directly.
+ //
+ bool has_value() const noexcept {
+ return !!func;
+ }
+
+ // Returns the type of the value stored, if any.
+ //
+ const std::type_info& type() const noexcept {
+ if (has_value()) {
+ const std::type_info* t;
+ func(op::type, reinterpret_cast<void*>(&t));
+ return *t;
+ } else {
+ return typeid(void);
+ }
+ }
+
+ template<typename T, typename U, typename V>
+ friend inline void* cast_helper(const base<U, V>& b) noexcept;
+};
+
+// Function used by all `any_cast` functions
+//
+// Returns a void* to the contents if they exist and match the
+// requested type, otherwise `nullptr`.
+//
+template<typename T, typename U, typename V>
+inline void* cast_helper(const base<U, V>& b) noexcept {
+ if (b.func && ((&op_func<T> == b.func) ||
+ (b.type() == typeid(T)))) {
+ return b.ptr();
+ } else {
+ return nullptr;
+ }
+}
+}
+
+// `any_cast`
+// ==========
+//
+// Just the usual gamut of `any_cast` overloads. These get a bit
+// repetitive and it would be nice to think of a way to collapse them
+// down a bit.
+//
+
+// The pointer pair!
+//
+template<typename T, typename U, typename V>
+inline T* any_cast(_any::base<U, V>* a) noexcept {
+ if (a) {
+ return static_cast<T*>(_any::cast_helper<std::decay_t<T>>(*a));
+ }
+ return nullptr;
+}
+
+template<typename T, typename U, typename V>
+inline const T* any_cast(const _any::base<U, V>* a) noexcept {
+ if (a) {
+ return static_cast<T*>(_any::cast_helper<std::decay_t<T>>(*a));
+ }
+ return nullptr;
+}
+
+// While we disallow copying the immobile any itself, we can allow
+// anything with an extracted value that the type supports.
+//
+template<typename T, typename U, typename V>
+inline T any_cast(_any::base<U, V>& a) {
+ static_assert(std::is_reference_v<T> ||
+ std::is_copy_constructible_v<T>,
+ "The supplied type must be either a reference or "
+ "copy constructible.");
+ auto p = any_cast<std::decay_t<T>>(&a);
+ if (p) {
+ return static_cast<T>(*p);
+ }
+ throw std::bad_any_cast();
+}
+
+template<typename T, typename U, typename V>
+inline T any_cast(const _any::base<U, V>& a) {
+ static_assert(std::is_reference_v<T> ||
+ std::is_copy_constructible_v<T>,
+ "The supplied type must be either a reference or "
+ "copy constructible.");
+ auto p = any_cast<std::decay_t<T>>(&a);
+ if (p) {
+ return static_cast<T>(*p);
+ }
+ throw std::bad_any_cast();
+}
+
+template<typename T, typename U, typename V>
+inline std::enable_if_t<(std::is_move_constructible_v<T> ||
+ std::is_copy_constructible_v<T>) &&
+ !std::is_rvalue_reference_v<T>, T>
+any_cast(_any::base<U, V>&& a) {
+ auto p = any_cast<std::decay_t<T>>(&a);
+ if (p) {
+ return std::move((*p));
+ }
+ throw std::bad_any_cast();
+}
+
+template<typename T, typename U, typename V>
+inline std::enable_if_t<std::is_rvalue_reference_v<T>, T>
+any_cast(_any::base<U, V>&& a) {
+ auto p = any_cast<std::decay_t<T>>(&a);
+ if (p) {
+ return static_cast<T>(*p);
+ }
+ throw std::bad_any_cast();
+}
+
+// `immobile_any`
+// ==============
+//
+// Sometimes, uncopyable objects exist and I want to do things with
+// them. The C++ standard library is really quite keen on insisting
+// things be copyable before it deigns to work. I find this annoying.
+//
+// Also, the allocator, while useful, is really not considerate of
+// other people's time. Every time we go to visit it, it takes us
+// quite an awfully long time to get away again. As such, I've been
+// trying to avoid its company whenever it is convenient and seemly.
+//
+// We accept any type that will fit in the declared capacity. You may
+// store types with throwing destructors, but terminate will be
+// invoked when they throw.
+//
+template<std::size_t S>
+class immobile_any : public _any::base<immobile_any<S>,
+ std::aligned_storage_t<S>> {
+ using base = _any::base<immobile_any<S>, std::aligned_storage_t<S>>;
+ friend base;
+
+ using _any::base<immobile_any<S>, std::aligned_storage_t<S>>::storage;
+
+ // Superclass requirements!
+ // ------------------------
+ //
+ // Simple as anything. We have a buffer of fixed size and return the
+ // pointer to it when asked.
+ //
+ static constexpr std::size_t capacity = S;
+ void* ptr() const noexcept {
+ return const_cast<void*>(static_cast<const void*>(&storage));
+ }
+ void* alloc_storage(std::size_t) noexcept {
+ return ptr();
+ }
+ void free_storage() noexcept {}
+
+ static_assert(capacity != _any::dynamic,
+ "That is not a valid size for an immobile_any.");
+
+public:
+
+ immobile_any() noexcept = default;
+
+ immobile_any(const immobile_any&) = delete;
+ immobile_any& operator =(const immobile_any&) = delete;
+ immobile_any(immobile_any&&) = delete;
+ immobile_any& operator =(immobile_any&&) = delete;
+
+ using base::base;
+ using base::operator =;
+
+ void swap(immobile_any&) = delete;
+};
+
+template<typename T, std::size_t S, typename... Args>
+inline immobile_any<S> make_immobile_any(Args&& ...args) {
+ return immobile_any<S>(std::in_place_type<T>, std::forward<Args>(args)...);
+}
+
+template<typename T, std::size_t S, typename U, typename... Args>
+inline immobile_any<S> make_immobile_any(std::initializer_list<U> i, Args&& ...args) {
+ return immobile_any<S>(std::in_place_type<T>, i, std::forward<Args>(args)...);
+}
+
+// `unique_any`
+// ============
+//
+// Oh dear. Now we're getting back into allocation. You don't think
+// the allocator noticed all those mean things we said about it, do
+// you?
+//
+// Well. Okay, allocator. Sometimes when it's the middle of the night
+// and you're writing template code you say things you don't exactly
+// mean. If it weren't for you, we wouldn't have any memory to run all
+// our programs in at all. Really, I'm just being considerate of
+// *your* needs, trying to avoid having to run to you every time we
+// instantiate a type, making a few that can be self-sufficient…uh…
+//
+// **Anyway**, this is movable but not copyable, as you should expect
+// from anything with ‘unique’ in the name.
+//
+class unique_any : public _any::base<unique_any, std::unique_ptr<std::byte[]>> {
+ using base = _any::base<unique_any, std::unique_ptr<std::byte[]>>;
+ friend base;
+
+ using base::storage;
+
+ // Superclass requirements
+ // -----------------------
+ //
+ // Our storage is a single chunk of RAM owned by a
+ // `std::unique_ptr`.
+ //
+ static constexpr std::size_t capacity = _any::dynamic;
+ void* ptr() const noexcept {
+ return static_cast<void*>(storage.get());
+ return nullptr;
+ }
+
+ void* alloc_storage(const std::size_t z) {
+ storage.reset(new std::byte[z]);
+ return ptr();
+ }
+
+ void free_storage() noexcept {
+ storage.reset();
+ }
+
+public:
+
+ unique_any() noexcept = default;
+ ~unique_any() noexcept = default;
+
+ unique_any(const unique_any&) = delete;
+ unique_any& operator =(const unique_any&) = delete;
+
+ // We can rely on the behavior of `unique_ptr` and the base class to
+ // give us a default move constructor that does the right thing.
+ //
+ unique_any(unique_any&& rhs) noexcept = default;
+ unique_any& operator =(unique_any&& rhs) = default;
+
+ using base::base;
+ using base::operator =;
+};
+
+inline void swap(unique_any& lhs, unique_any& rhs) noexcept {
+ lhs.swap(rhs);
+}
+
+template<typename T, typename... Args>
+inline unique_any make_unique_any(Args&& ...args) {
+ return unique_any(std::in_place_type<T>, std::forward<Args>(args)...);
+}
+
+template<typename T, typename U, typename... Args>
+inline unique_any make_unique_any(std::initializer_list<U> i, Args&& ...args) {
+ return unique_any(std::in_place_type<T>, i, std::forward<Args>(args)...);
+}
+
+// `shared_any`
+// ============
+//
+// Once more with feeling!
+//
+// This is both copyable *and* movable. In case you need that sort of
+// thing. It seemed a reasonable completion.
+//
+class shared_any : public _any::base<shared_any, boost::shared_ptr<std::byte[]>> {
+ using base = _any::base<shared_any, boost::shared_ptr<std::byte[]>>;
+ friend base;
+
+ using base::storage;
+
+ // Superclass requirements
+ // -----------------------
+ //
+ // Our storage is a single chunk of RAM allocated from the
+ // heap. This time it's owned by a `boost::shared_ptr` so we can use
+ // `boost::make_shared_noinit`. (This lets us get the optimization
+ // that allocates array and control block in one without wasting
+ // time on `memset`.)
+ //
+ static constexpr std::size_t capacity = _any::dynamic;
+ void* ptr() const noexcept {
+ return static_cast<void*>(storage.get());
+ }
+
+ void* alloc_storage(std::size_t n) {
+ storage = boost::make_shared_noinit<std::byte[]>(n);
+ return ptr();
+ }
+
+ void free_storage() noexcept {
+ storage.reset();
+ }
+
+public:
+
+ shared_any() noexcept = default;
+ ~shared_any() noexcept = default;
+
+ shared_any(const shared_any& rhs) noexcept = default;
+ shared_any& operator =(const shared_any&) noexcept = default;
+
+ shared_any(shared_any&& rhs) noexcept = default;
+ shared_any& operator =(shared_any&& rhs) noexcept = default;
+
+ using base::base;
+ using base::operator =;
+};
+
+inline void swap(shared_any& lhs, shared_any& rhs) noexcept {
+ lhs.swap(rhs);
+}
+
+template<typename T, typename... Args>
+inline shared_any make_shared_any(Args&& ...args) {
+ return shared_any(std::in_place_type<T>, std::forward<Args>(args)...);
+}
+
+template<typename T, typename U, typename... Args>
+inline shared_any make_shared_any(std::initializer_list<U> i, Args&& ...args) {
+ return shared_any(std::in_place_type<T>, i, std::forward<Args>(args)...);
+}
+}
+
+#endif // INCLUDE_STATIC_ANY
diff --git a/src/include/bitmapper.h b/src/include/bitmapper.h
new file mode 100644
index 000000000..5a65cc20f
--- /dev/null
+++ b/src/include/bitmapper.h
@@ -0,0 +1,48 @@
+// -*- 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-2006 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_BITMAPPER_H
+#define CEPH_BITMAPPER_H
+
+class bitmapper {
+ char *_data;
+ int _len;
+
+ public:
+ bitmapper() : _data(0), _len(0) { }
+ bitmapper(char *data, int len) : _data(data), _len(len) { }
+
+ void set_data(char *data, int len) { _data = data; _len = len; }
+
+ int bytes() const { return _len; }
+ int bits() const { return _len * 8; }
+
+ bool operator[](int b) const {
+ return get(b);
+ }
+ bool get(int b) const {
+ return _data[b >> 3] & (1 << (b&7));
+ }
+ void set(int b) {
+ _data[b >> 3] |= 1 << (b&7);
+ }
+ void clear(int b) {
+ _data[b >> 3] &= ~(1 << (b&7));
+ }
+ void toggle(int b) {
+ _data[b >> 3] ^= 1 << (b&7);
+ }
+};
+
+#endif
diff --git a/src/include/blobhash.h b/src/include/blobhash.h
new file mode 100644
index 000000000..303892b13
--- /dev/null
+++ b/src/include/blobhash.h
@@ -0,0 +1,53 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2004-2006 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_BLOBHASH_H
+#define CEPH_BLOBHASH_H
+
+#include <cstdint>
+#include "hash.h"
+
+class blobhash {
+public:
+ uint32_t operator()(const void* p, size_t len) {
+ static rjhash<std::uint32_t> H;
+ std::uint32_t acc = 0;
+ auto buf = static_cast<const unsigned char*>(p);
+ while (len >= sizeof(acc)) {
+ acc ^= unaligned_load(buf);
+ buf += sizeof(std::uint32_t);
+ len -= sizeof(std::uint32_t);
+ }
+ // handle the last few bytes of p[-(len % 4):]
+ switch (len) {
+ case 3:
+ acc ^= buf[2] << 16;
+ [[fallthrough]];
+ case 2:
+ acc ^= buf[1] << 8;
+ [[fallthrough]];
+ case 1:
+ acc ^= buf[0];
+ }
+ return H(acc);
+ }
+private:
+ static inline std::uint32_t unaligned_load(const unsigned char* p) {
+ std::uint32_t result;
+ __builtin_memcpy(&result, p, sizeof(result));
+ return result;
+ }
+};
+
+
+#endif
diff --git a/src/include/btree_map.h b/src/include/btree_map.h
new file mode 100644
index 000000000..218835a0f
--- /dev/null
+++ b/src/include/btree_map.h
@@ -0,0 +1,68 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_INCLUDE_BTREE_MAP_H
+#define CEPH_INCLUDE_BTREE_MAP_H
+
+#include "include/cpp-btree/btree.h"
+#include "include/cpp-btree/btree_map.h"
+#include "include/ceph_assert.h" // cpp-btree uses system assert, blech
+#include "include/encoding.h"
+
+template<class T, class U>
+inline void encode(const btree::btree_map<T,U>& m, ceph::buffer::list& bl)
+{
+ using ceph::encode;
+ __u32 n = (__u32)(m.size());
+ encode(n, bl);
+ for (typename btree::btree_map<T,U>::const_iterator p = m.begin(); p != m.end(); ++p) {
+ encode(p->first, bl);
+ encode(p->second, bl);
+ }
+}
+template<class T, class U>
+inline void encode(const btree::btree_map<T,U>& m, ceph::buffer::list& bl, uint64_t features)
+{
+ using ceph::encode;
+ __u32 n = (__u32)(m.size());
+ encode(n, bl);
+ for (typename btree::btree_map<T,U>::const_iterator p = m.begin(); p != m.end(); ++p) {
+ encode(p->first, bl, features);
+ encode(p->second, bl, features);
+ }
+}
+template<class T, class U>
+inline void decode(btree::btree_map<T,U>& m, ceph::buffer::list::const_iterator& p)
+{
+ using ceph::decode;
+ __u32 n;
+ decode(n, p);
+ m.clear();
+ while (n--) {
+ T k;
+ decode(k, p);
+ decode(m[k], p);
+ }
+}
+template<class T, class U>
+inline void encode_nohead(const btree::btree_map<T,U>& m, ceph::buffer::list& bl)
+{
+ using ceph::encode;
+ for (typename btree::btree_map<T,U>::const_iterator p = m.begin(); p != m.end(); ++p) {
+ encode(p->first, bl);
+ encode(p->second, bl);
+ }
+}
+template<class T, class U>
+inline void decode_nohead(int n, btree::btree_map<T,U>& m, ceph::buffer::list::const_iterator& p)
+{
+ using ceph::decode;
+ m.clear();
+ while (n--) {
+ T k;
+ decode(k, p);
+ decode(m[k], p);
+ }
+}
+
+#endif
diff --git a/src/include/buffer.h b/src/include/buffer.h
new file mode 100644
index 000000000..10dceaec2
--- /dev/null
+++ b/src/include/buffer.h
@@ -0,0 +1,1294 @@
+// -*- 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-2006 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_BUFFER_H
+#define CEPH_BUFFER_H
+
+#if defined(__linux__) || defined(__FreeBSD__)
+#include <stdlib.h>
+#endif
+#include <limits.h>
+
+#ifndef _XOPEN_SOURCE
+# define _XOPEN_SOURCE 600
+#endif
+
+#include <stdio.h>
+#include <sys/uio.h>
+
+#if defined(__linux__) // For malloc(2).
+#include <malloc.h>
+#endif
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <string.h>
+
+#if !defined(__CYGWIN__) && !defined(_WIN32)
+# include <sys/mman.h>
+#endif
+
+#include <iosfwd>
+#include <iomanip>
+#include <list>
+#include <memory>
+#include <vector>
+#include <string>
+#if __cplusplus >= 201703L
+#include <string_view>
+#endif // __cplusplus >= 201703L
+
+#include <exception>
+#include <type_traits>
+
+#include "page.h"
+#include "crc32c.h"
+#include "buffer_fwd.h"
+
+
+#ifdef __CEPH__
+# include "include/ceph_assert.h"
+#else
+# include <assert.h>
+#endif
+
+#include "inline_memory.h"
+
+#define CEPH_BUFFER_API
+
+#ifdef HAVE_SEASTAR
+namespace seastar {
+template <typename T> class temporary_buffer;
+namespace net {
+class packet;
+}
+}
+#endif // HAVE_SEASTAR
+class deleter;
+
+template<typename T> class DencDumper;
+
+namespace ceph {
+
+template <class T>
+struct nop_delete {
+ void operator()(T*) {}
+};
+
+// This is not unique_ptr-like smart pointer! It just signalizes ownership
+// but DOES NOT manage the resource. It WILL LEAK if not manually deleted.
+// It's rather a replacement for raw pointer than any other smart one.
+//
+// Considered options:
+// * unique_ptr with custom deleter implemented in .cc (would provide
+// the non-zero-cost resource management),
+// * GSL's owner<T*> (pretty neat but would impose an extra depedency),
+// * unique_ptr with nop deleter,
+// * raw pointer (doesn't embed ownership enforcement - std::move).
+template <class T>
+struct unique_leakable_ptr : public std::unique_ptr<T, ceph::nop_delete<T>> {
+ using std::unique_ptr<T, ceph::nop_delete<T>>::unique_ptr;
+};
+
+namespace buffer CEPH_BUFFER_API {
+inline namespace v15_2_0 {
+
+/// Actual definitions in common/error_code.h
+struct error;
+struct bad_alloc;
+struct end_of_buffer;
+struct malformed_input;
+struct error_code;
+
+ /// count of cached crc hits (matching input)
+ int get_cached_crc();
+ /// count of cached crc hits (mismatching input, required adjustment)
+ int get_cached_crc_adjusted();
+ /// count of crc cache misses
+ int get_missed_crc();
+ /// enable/disable tracking of cached crcs
+ void track_cached_crc(bool b);
+
+ /*
+ * an abstract raw buffer. with a reference count.
+ */
+ class raw;
+ class raw_malloc;
+ class raw_static;
+ class raw_posix_aligned;
+ class raw_hack_aligned;
+ class raw_claimed_char;
+ class raw_unshareable; // diagnostic, unshareable char buffer
+ class raw_combined;
+ class raw_claim_buffer;
+
+
+ /*
+ * named constructors
+ */
+ ceph::unique_leakable_ptr<raw> copy(const char *c, unsigned len);
+ ceph::unique_leakable_ptr<raw> create(unsigned len);
+ ceph::unique_leakable_ptr<raw> create(unsigned len, char c);
+ ceph::unique_leakable_ptr<raw> create_in_mempool(unsigned len, int mempool);
+ ceph::unique_leakable_ptr<raw> claim_char(unsigned len, char *buf);
+ ceph::unique_leakable_ptr<raw> create_malloc(unsigned len);
+ ceph::unique_leakable_ptr<raw> claim_malloc(unsigned len, char *buf);
+ ceph::unique_leakable_ptr<raw> create_static(unsigned len, char *buf);
+ ceph::unique_leakable_ptr<raw> create_aligned(unsigned len, unsigned align);
+ ceph::unique_leakable_ptr<raw> create_aligned_in_mempool(unsigned len, unsigned align, int mempool);
+ ceph::unique_leakable_ptr<raw> create_page_aligned(unsigned len);
+ ceph::unique_leakable_ptr<raw> create_small_page_aligned(unsigned len);
+ ceph::unique_leakable_ptr<raw> claim_buffer(unsigned len, char *buf, deleter del);
+
+#ifdef HAVE_SEASTAR
+ /// create a raw buffer to wrap seastar cpu-local memory, using foreign_ptr to
+ /// make it safe to share between cpus
+ ceph::unique_leakable_ptr<buffer::raw> create(seastar::temporary_buffer<char>&& buf);
+ /// create a raw buffer to wrap seastar cpu-local memory, without the safety
+ /// of foreign_ptr. the caller must otherwise guarantee that the buffer ptr is
+ /// destructed on this cpu
+ ceph::unique_leakable_ptr<buffer::raw> create_local(seastar::temporary_buffer<char>&& buf);
+#endif
+
+ /*
+ * a buffer pointer. references (a subsequence of) a raw buffer.
+ */
+ class CEPH_BUFFER_API ptr {
+ friend class list;
+ protected:
+ raw *_raw;
+ unsigned _off, _len;
+ private:
+
+ void release();
+
+ template<bool is_const>
+ class iterator_impl {
+ const ptr *bp; ///< parent ptr
+ const char *start; ///< starting pointer into bp->c_str()
+ const char *pos; ///< pointer into bp->c_str()
+ const char *end_ptr; ///< pointer to bp->end_c_str()
+ const bool deep; ///< if true, do not allow shallow ptr copies
+
+ iterator_impl(typename std::conditional<is_const, const ptr*, ptr*>::type p,
+ size_t offset, bool d)
+ : bp(p),
+ start(p->c_str() + offset),
+ pos(start),
+ end_ptr(p->end_c_str()),
+ deep(d)
+ {}
+
+ friend class ptr;
+
+ public:
+ using pointer = typename std::conditional<is_const, const char*, char *>::type;
+ pointer get_pos_add(size_t n) {
+ auto r = pos;
+ *this += n;
+ return r;
+ }
+ ptr get_ptr(size_t len) {
+ if (deep) {
+ return buffer::copy(get_pos_add(len), len);
+ } else {
+ size_t off = pos - bp->c_str();
+ *this += len;
+ return ptr(*bp, off, len);
+ }
+ }
+
+ iterator_impl& operator+=(size_t len);
+
+ const char *get_pos() {
+ return pos;
+ }
+ const char *get_end() {
+ return end_ptr;
+ }
+
+ size_t get_offset() {
+ return pos - start;
+ }
+
+ bool end() const {
+ return pos == end_ptr;
+ }
+ };
+
+ public:
+ using const_iterator = iterator_impl<true>;
+ using iterator = iterator_impl<false>;
+
+ ptr() : _raw(nullptr), _off(0), _len(0) {}
+ ptr(ceph::unique_leakable_ptr<raw> r);
+ // cppcheck-suppress noExplicitConstructor
+ ptr(unsigned l);
+ ptr(const char *d, unsigned l);
+ ptr(const ptr& p);
+ ptr(ptr&& p) noexcept;
+ ptr(const ptr& p, unsigned o, unsigned l);
+ ptr(const ptr& p, ceph::unique_leakable_ptr<raw> r);
+ ptr& operator= (const ptr& p);
+ ptr& operator= (ptr&& p) noexcept;
+ ~ptr() {
+ // BE CAREFUL: this destructor is called also for hypercombined ptr_node.
+ // After freeing underlying raw, `*this` can become inaccessible as well!
+ release();
+ }
+
+ bool have_raw() const { return _raw ? true:false; }
+
+ void swap(ptr& other) noexcept;
+
+ iterator begin(size_t offset=0) {
+ return iterator(this, offset, false);
+ }
+ const_iterator begin(size_t offset=0) const {
+ return const_iterator(this, offset, false);
+ }
+ const_iterator cbegin() const {
+ return begin();
+ }
+ const_iterator begin_deep(size_t offset=0) const {
+ return const_iterator(this, offset, true);
+ }
+
+ // misc
+ bool is_aligned(unsigned align) const {
+ return ((uintptr_t)c_str() & (align-1)) == 0;
+ }
+ bool is_page_aligned() const { return is_aligned(CEPH_PAGE_SIZE); }
+ bool is_n_align_sized(unsigned align) const
+ {
+ return (length() % align) == 0;
+ }
+ bool is_n_page_sized() const { return is_n_align_sized(CEPH_PAGE_SIZE); }
+ bool is_partial() const {
+ return have_raw() && (start() > 0 || end() < raw_length());
+ }
+
+ int get_mempool() const;
+ void reassign_to_mempool(int pool);
+ void try_assign_to_mempool(int pool);
+
+ // accessors
+ const char *c_str() const;
+ char *c_str();
+ const char *end_c_str() const;
+ char *end_c_str();
+ unsigned length() const { return _len; }
+ unsigned offset() const { return _off; }
+ unsigned start() const { return _off; }
+ unsigned end() const { return _off + _len; }
+ unsigned unused_tail_length() const;
+ const char& operator[](unsigned n) const;
+ char& operator[](unsigned n);
+
+ const char *raw_c_str() const;
+ unsigned raw_length() const;
+ int raw_nref() const;
+
+ void copy_out(unsigned o, unsigned l, char *dest) const;
+
+ unsigned wasted() const;
+
+ int cmp(const ptr& o) const;
+ bool is_zero() const;
+
+ // modifiers
+ void set_offset(unsigned o) {
+#ifdef __CEPH__
+ ceph_assert(raw_length() >= o);
+#else
+ assert(raw_length() >= o);
+#endif
+ _off = o;
+ }
+ void set_length(unsigned l) {
+#ifdef __CEPH__
+ ceph_assert(raw_length() >= l);
+#else
+ assert(raw_length() >= l);
+#endif
+ _len = l;
+ }
+
+ unsigned append(char c);
+ unsigned append(const char *p, unsigned l);
+#if __cplusplus >= 201703L
+ inline unsigned append(std::string_view s) {
+ return append(s.data(), s.length());
+ }
+#endif // __cplusplus >= 201703L
+ void copy_in(unsigned o, unsigned l, const char *src, bool crc_reset = true);
+ void zero(bool crc_reset = true);
+ void zero(unsigned o, unsigned l, bool crc_reset = true);
+ unsigned append_zeros(unsigned l);
+
+#ifdef HAVE_SEASTAR
+ /// create a temporary_buffer, copying the ptr as its deleter
+ operator seastar::temporary_buffer<char>() &;
+ /// convert to temporary_buffer, stealing the ptr as its deleter
+ operator seastar::temporary_buffer<char>() &&;
+#endif // HAVE_SEASTAR
+
+ };
+
+
+ struct ptr_hook {
+ mutable ptr_hook* next;
+
+ ptr_hook() = default;
+ ptr_hook(ptr_hook* const next)
+ : next(next) {
+ }
+ };
+
+ class ptr_node : public ptr_hook, public ptr {
+ public:
+ struct cloner {
+ ptr_node* operator()(const ptr_node& clone_this);
+ };
+ struct disposer {
+ void operator()(ptr_node* const delete_this) {
+ if (!__builtin_expect(dispose_if_hypercombined(delete_this), 0)) {
+ delete delete_this;
+ }
+ }
+ };
+
+ ~ptr_node() = default;
+
+ static std::unique_ptr<ptr_node, disposer>
+ create(ceph::unique_leakable_ptr<raw> r) {
+ return create_hypercombined(std::move(r));
+ }
+ static std::unique_ptr<ptr_node, disposer>
+ create(const unsigned l) {
+ return create_hypercombined(buffer::create(l));
+ }
+ template <class... Args>
+ static std::unique_ptr<ptr_node, disposer>
+ create(Args&&... args) {
+ return std::unique_ptr<ptr_node, disposer>(
+ new ptr_node(std::forward<Args>(args)...));
+ }
+
+ static ptr_node* copy_hypercombined(const ptr_node& copy_this);
+
+ private:
+ friend list;
+
+ template <class... Args>
+ ptr_node(Args&&... args) : ptr(std::forward<Args>(args)...) {
+ }
+ ptr_node(const ptr_node&) = default;
+
+ ptr& operator= (const ptr& p) = delete;
+ ptr& operator= (ptr&& p) noexcept = delete;
+ ptr_node& operator= (const ptr_node& p) = delete;
+ ptr_node& operator= (ptr_node&& p) noexcept = delete;
+ void swap(ptr& other) noexcept = delete;
+ void swap(ptr_node& other) noexcept = delete;
+
+ static bool dispose_if_hypercombined(ptr_node* delete_this);
+ static std::unique_ptr<ptr_node, disposer> create_hypercombined(
+ ceph::unique_leakable_ptr<raw> r);
+ };
+ /*
+ * list - the useful bit!
+ */
+
+ class CEPH_BUFFER_API list {
+ public:
+ // this the very low-level implementation of singly linked list
+ // ceph::buffer::list is built on. We don't use intrusive slist
+ // of Boost (or any other 3rd party) to save extra dependencies
+ // in our public headers.
+ class buffers_t {
+ // _root.next can be thought as _head
+ ptr_hook _root;
+ ptr_hook* _tail;
+
+ public:
+ template <class T>
+ class buffers_iterator {
+ typename std::conditional<
+ std::is_const<T>::value, const ptr_hook*, ptr_hook*>::type cur;
+ template <class U> friend class buffers_iterator;
+ public:
+ using value_type = T;
+ using reference = typename std::add_lvalue_reference<T>::type;
+ using pointer = typename std::add_pointer<T>::type;
+ using difference_type = std::ptrdiff_t;
+ using iterator_category = std::forward_iterator_tag;
+
+ template <class U>
+ buffers_iterator(U* const p)
+ : cur(p) {
+ }
+ // copy constructor
+ buffers_iterator(const buffers_iterator<T>& other)
+ : cur(other.cur) {
+ }
+ // converting constructor, from iterator -> const_iterator only
+ template <class U, typename std::enable_if<
+ std::is_const<T>::value && !std::is_const<U>::value, int>::type = 0>
+ buffers_iterator(const buffers_iterator<U>& other)
+ : cur(other.cur) {
+ }
+ buffers_iterator() = default;
+
+ T& operator*() const {
+ return *reinterpret_cast<T*>(cur);
+ }
+ T* operator->() const {
+ return reinterpret_cast<T*>(cur);
+ }
+
+ buffers_iterator& operator++() {
+ cur = cur->next;
+ return *this;
+ }
+ buffers_iterator operator++(int) {
+ const auto temp(*this);
+ ++*this;
+ return temp;
+ }
+
+ template <class U>
+ buffers_iterator& operator=(buffers_iterator<U>& other) {
+ cur = other.cur;
+ return *this;
+ }
+
+ bool operator==(const buffers_iterator& rhs) const {
+ return cur == rhs.cur;
+ }
+ bool operator!=(const buffers_iterator& rhs) const {
+ return !(*this==rhs);
+ }
+ };
+
+ typedef buffers_iterator<const ptr_node> const_iterator;
+ typedef buffers_iterator<ptr_node> iterator;
+
+ typedef const ptr_node& const_reference;
+ typedef ptr_node& reference;
+
+ buffers_t()
+ : _root(&_root),
+ _tail(&_root) {
+ }
+ buffers_t(const buffers_t&) = delete;
+ buffers_t(buffers_t&& other)
+ : _root(other._root.next == &other._root ? &_root : other._root.next),
+ _tail(other._tail == &other._root ? &_root : other._tail) {
+ other._root.next = &other._root;
+ other._tail = &other._root;
+
+ _tail->next = &_root;
+ }
+ buffers_t& operator=(buffers_t&& other) {
+ if (&other != this) {
+ clear_and_dispose();
+ swap(other);
+ }
+ return *this;
+ }
+
+ void push_back(reference item) {
+ item.next = &_root;
+ // this updates _root.next when called on empty
+ _tail->next = &item;
+ _tail = &item;
+ }
+
+ void push_front(reference item) {
+ item.next = _root.next;
+ _root.next = &item;
+ _tail = _tail == &_root ? &item : _tail;
+ }
+
+ // *_after
+ iterator erase_after(const_iterator it) {
+ const auto* to_erase = it->next;
+
+ it->next = to_erase->next;
+ _root.next = _root.next == to_erase ? to_erase->next : _root.next;
+ _tail = _tail == to_erase ? (ptr_hook*)&*it : _tail;
+ return it->next;
+ }
+
+ void insert_after(const_iterator it, reference item) {
+ item.next = it->next;
+ it->next = &item;
+ _root.next = it == end() ? &item : _root.next;
+ _tail = const_iterator(_tail) == it ? &item : _tail;
+ }
+
+ void splice_back(buffers_t& other) {
+ if (other.empty()) {
+ return;
+ }
+
+ other._tail->next = &_root;
+ // will update root.next if empty() == true
+ _tail->next = other._root.next;
+ _tail = other._tail;
+
+ other._root.next = &other._root;
+ other._tail = &other._root;
+ }
+
+ bool empty() const { return _tail == &_root; }
+
+ const_iterator begin() const {
+ return _root.next;
+ }
+ const_iterator before_begin() const {
+ return &_root;
+ }
+ const_iterator end() const {
+ return &_root;
+ }
+ iterator begin() {
+ return _root.next;
+ }
+ iterator before_begin() {
+ return &_root;
+ }
+ iterator end() {
+ return &_root;
+ }
+
+ reference front() {
+ return reinterpret_cast<reference>(*_root.next);
+ }
+ reference back() {
+ return reinterpret_cast<reference>(*_tail);
+ }
+ const_reference front() const {
+ return reinterpret_cast<const_reference>(*_root.next);
+ }
+ const_reference back() const {
+ return reinterpret_cast<const_reference>(*_tail);
+ }
+
+ void clone_from(const buffers_t& other) {
+ clear_and_dispose();
+ for (auto& node : other) {
+ ptr_node* clone = ptr_node::cloner()(node);
+ push_back(*clone);
+ }
+ }
+ void clear_and_dispose() {
+ ptr_node::disposer dispose;
+ for (auto it = begin(), e = end(); it != e; /* nop */) {
+ auto& node = *it++;
+ dispose(&node);
+ }
+ _tail = &_root;
+ _root.next = _tail;
+ }
+ iterator erase_after_and_dispose(iterator it) {
+ auto* to_dispose = &*std::next(it);
+ auto ret = erase_after(it);
+ ptr_node::disposer()(to_dispose);
+ return ret;
+ }
+
+ void swap(buffers_t& other) {
+ const auto copy_root = _root;
+ _root.next = \
+ other._root.next == &other._root ? &this->_root : other._root.next;
+ other._root.next = \
+ copy_root.next == &_root ? &other._root : copy_root.next;
+
+ const auto copy_tail = _tail;
+ _tail = other._tail == &other._root ? &this->_root : other._tail;
+ other._tail = copy_tail == &_root ? &other._root : copy_tail;
+
+ _tail->next = &_root;
+ other._tail->next = &other._root;
+ }
+ };
+
+ class iterator;
+
+ private:
+ // my private bits
+ buffers_t _buffers;
+
+ // track bufferptr we can modify (especially ::append() to). Not all bptrs
+ // bufferlist holds have this trait -- if somebody ::push_back(const ptr&),
+ // he expects it won't change.
+ ptr_node* _carriage;
+ unsigned _len, _num;
+
+ template <bool is_const>
+ class CEPH_BUFFER_API iterator_impl {
+ protected:
+ typedef typename std::conditional<is_const,
+ const list,
+ list>::type bl_t;
+ typedef typename std::conditional<is_const,
+ const buffers_t,
+ buffers_t >::type list_t;
+ typedef typename std::conditional<is_const,
+ typename buffers_t::const_iterator,
+ typename buffers_t::iterator>::type list_iter_t;
+ bl_t* bl;
+ list_t* ls; // meh.. just here to avoid an extra pointer dereference..
+ list_iter_t p;
+ unsigned off; // in bl
+ unsigned p_off; // in *p
+ friend class iterator_impl<true>;
+
+ public:
+ using iterator_category = std::forward_iterator_tag;
+ using value_type = typename std::conditional<is_const, const char, char>::type;
+ using difference_type = std::ptrdiff_t;
+ using pointer = typename std::add_pointer<value_type>::type;
+ using reference = typename std::add_lvalue_reference<value_type>::type;
+
+ // constructor. position.
+ iterator_impl()
+ : bl(0), ls(0), off(0), p_off(0) {}
+ iterator_impl(bl_t *l, unsigned o=0);
+ iterator_impl(bl_t *l, unsigned o, list_iter_t ip, unsigned po)
+ : bl(l), ls(&bl->_buffers), p(ip), off(o), p_off(po) {}
+ iterator_impl(const list::iterator& i);
+
+ /// get current iterator offset in buffer::list
+ unsigned get_off() const { return off; }
+
+ /// get number of bytes remaining from iterator position to the end of the buffer::list
+ unsigned get_remaining() const { return bl->length() - off; }
+
+ /// true if iterator is at the end of the buffer::list
+ bool end() const {
+ return p == ls->end();
+ //return off == bl->length();
+ }
+ void seek(unsigned o);
+ char operator*() const;
+ iterator_impl& operator+=(unsigned o);
+ iterator_impl& operator++();
+ ptr get_current_ptr() const;
+ bool is_pointing_same_raw(const ptr& other) const;
+
+ bl_t& get_bl() const { return *bl; }
+
+ // copy data out.
+ // note that these all _append_ to dest!
+ void copy(unsigned len, char *dest);
+ // deprecated, use copy_deep()
+ void copy(unsigned len, ptr &dest) __attribute__((deprecated));
+ void copy_deep(unsigned len, ptr &dest);
+ void copy_shallow(unsigned len, ptr &dest);
+ void copy(unsigned len, list &dest);
+ void copy(unsigned len, std::string &dest);
+ void copy_all(list &dest);
+
+ // get a pointer to the currenet iterator position, return the
+ // number of bytes we can read from that position (up to want),
+ // and advance the iterator by that amount.
+ size_t get_ptr_and_advance(size_t want, const char **p);
+
+ /// calculate crc from iterator position
+ uint32_t crc32c(size_t length, uint32_t crc);
+
+ friend bool operator==(const iterator_impl& lhs,
+ const iterator_impl& rhs) {
+ return &lhs.get_bl() == &rhs.get_bl() && lhs.get_off() == rhs.get_off();
+ }
+ friend bool operator!=(const iterator_impl& lhs,
+ const iterator_impl& rhs) {
+ return &lhs.get_bl() != &rhs.get_bl() || lhs.get_off() != rhs.get_off();
+ }
+ };
+
+ public:
+ typedef iterator_impl<true> const_iterator;
+
+ class CEPH_BUFFER_API iterator : public iterator_impl<false> {
+ public:
+ iterator() = default;
+ iterator(bl_t *l, unsigned o=0);
+ iterator(bl_t *l, unsigned o, list_iter_t ip, unsigned po);
+ // copy data in
+ void copy_in(unsigned len, const char *src, bool crc_reset = true);
+ void copy_in(unsigned len, const list& otherl);
+ };
+
+ struct reserve_t {
+ char* bp_data;
+ unsigned* bp_len;
+ unsigned* bl_len;
+ };
+
+ class contiguous_appender {
+ ceph::bufferlist& bl;
+ ceph::bufferlist::reserve_t space;
+ char* pos;
+ bool deep;
+
+ /// running count of bytes appended that are not reflected by @pos
+ size_t out_of_band_offset = 0;
+
+ contiguous_appender(bufferlist& bl, size_t len, bool d)
+ : bl(bl),
+ space(bl.obtain_contiguous_space(len)),
+ pos(space.bp_data),
+ deep(d) {
+ }
+
+ void flush_and_continue() {
+ const size_t l = pos - space.bp_data;
+ *space.bp_len += l;
+ *space.bl_len += l;
+ space.bp_data = pos;
+ }
+
+ friend class list;
+ template<typename Type> friend class ::DencDumper;
+
+ public:
+ ~contiguous_appender() {
+ flush_and_continue();
+ }
+
+ size_t get_out_of_band_offset() const {
+ return out_of_band_offset;
+ }
+ void append(const char* __restrict__ p, size_t l) {
+ maybe_inline_memcpy(pos, p, l, 16);
+ pos += l;
+ }
+ char *get_pos_add(size_t len) {
+ char *r = pos;
+ pos += len;
+ return r;
+ }
+ char *get_pos() const {
+ return pos;
+ }
+
+ void append(const bufferptr& p) {
+ const auto plen = p.length();
+ if (!plen) {
+ return;
+ }
+ if (deep) {
+ append(p.c_str(), plen);
+ } else {
+ flush_and_continue();
+ bl.append(p);
+ space = bl.obtain_contiguous_space(0);
+ out_of_band_offset += plen;
+ }
+ }
+ void append(const bufferlist& l) {
+ if (deep) {
+ for (const auto &p : l._buffers) {
+ append(p.c_str(), p.length());
+ }
+ } else {
+ flush_and_continue();
+ bl.append(l);
+ space = bl.obtain_contiguous_space(0);
+ out_of_band_offset += l.length();
+ }
+ }
+
+ size_t get_logical_offset() const {
+ return out_of_band_offset + (pos - space.bp_data);
+ }
+ };
+
+ contiguous_appender get_contiguous_appender(size_t len, bool deep=false) {
+ return contiguous_appender(*this, len, deep);
+ }
+
+ class contiguous_filler {
+ friend buffer::list;
+ char* pos;
+
+ contiguous_filler(char* const pos) : pos(pos) {}
+
+ public:
+ void advance(const unsigned len) {
+ pos += len;
+ }
+ void copy_in(const unsigned len, const char* const src) {
+ memcpy(pos, src, len);
+ advance(len);
+ }
+ char* c_str() {
+ return pos;
+ }
+ };
+ // The contiguous_filler is supposed to be not costlier than a single
+ // pointer. Keep it dumb, please.
+ static_assert(sizeof(contiguous_filler) == sizeof(char*),
+ "contiguous_filler should be no costlier than pointer");
+
+ class page_aligned_appender {
+ bufferlist& bl;
+ unsigned min_alloc;
+
+ page_aligned_appender(list *l, unsigned min_pages)
+ : bl(*l),
+ min_alloc(min_pages * CEPH_PAGE_SIZE) {
+ }
+
+ void _refill(size_t len);
+
+ template <class Func>
+ void _append_common(size_t len, Func&& impl_f) {
+ const auto free_in_last = bl.get_append_buffer_unused_tail_length();
+ const auto first_round = std::min(len, free_in_last);
+ if (first_round) {
+ impl_f(first_round);
+ }
+ // no C++17 for the sake of the C++11 guarantees of librados, sorry.
+ const auto second_round = len - first_round;
+ if (second_round) {
+ _refill(second_round);
+ impl_f(second_round);
+ }
+ }
+
+ friend class list;
+
+ public:
+ void append(const bufferlist& l) {
+ bl.append(l);
+ bl.obtain_contiguous_space(0);
+ }
+
+ void append(const char* buf, size_t entire_len) {
+ _append_common(entire_len,
+ [buf, this] (const size_t chunk_len) mutable {
+ bl.append(buf, chunk_len);
+ buf += chunk_len;
+ });
+ }
+
+ void append_zero(size_t entire_len) {
+ _append_common(entire_len, [this] (const size_t chunk_len) {
+ bl.append_zero(chunk_len);
+ });
+ }
+
+ void substr_of(const list& bl, unsigned off, unsigned len) {
+ for (const auto& bptr : bl.buffers()) {
+ if (off >= bptr.length()) {
+ off -= bptr.length();
+ continue;
+ }
+ const auto round_size = std::min(bptr.length() - off, len);
+ append(bptr.c_str() + off, round_size);
+ len -= round_size;
+ off = 0;
+ }
+ }
+ };
+
+ page_aligned_appender get_page_aligned_appender(unsigned min_pages=1) {
+ return page_aligned_appender(this, min_pages);
+ }
+
+ private:
+ // always_empty_bptr has no underlying raw but its _len is always 0.
+ // This is useful for e.g. get_append_buffer_unused_tail_length() as
+ // it allows to avoid conditionals on hot paths.
+ static ptr_node always_empty_bptr;
+ ptr_node& refill_append_space(const unsigned len);
+
+ // for page_aligned_appender; never ever expose this publicly!
+ // carriage / append_buffer is just an implementation's detail.
+ ptr& get_append_buffer() {
+ return *_carriage;
+ }
+
+ public:
+ // cons/des
+ list()
+ : _carriage(&always_empty_bptr),
+ _len(0),
+ _num(0) {
+ }
+ // cppcheck-suppress noExplicitConstructor
+ // cppcheck-suppress noExplicitConstructor
+ list(unsigned prealloc)
+ : _carriage(&always_empty_bptr),
+ _len(0),
+ _num(0) {
+ reserve(prealloc);
+ }
+
+ list(const list& other)
+ : _carriage(&always_empty_bptr),
+ _len(other._len),
+ _num(other._num) {
+ _buffers.clone_from(other._buffers);
+ }
+
+ list(list&& other) noexcept
+ : _buffers(std::move(other._buffers)),
+ _carriage(other._carriage),
+ _len(other._len),
+ _num(other._num) {
+ other.clear();
+ }
+
+ ~list() {
+ _buffers.clear_and_dispose();
+ }
+
+ list& operator= (const list& other) {
+ if (this != &other) {
+ _carriage = &always_empty_bptr;
+ _buffers.clone_from(other._buffers);
+ _len = other._len;
+ _num = other._num;
+ }
+ return *this;
+ }
+ list& operator= (list&& other) noexcept {
+ _buffers = std::move(other._buffers);
+ _carriage = other._carriage;
+ _len = other._len;
+ _num = other._num;
+ other.clear();
+ return *this;
+ }
+
+ uint64_t get_wasted_space() const;
+ unsigned get_num_buffers() const { return _num; }
+ const ptr_node& front() const { return _buffers.front(); }
+ const ptr_node& back() const { return _buffers.back(); }
+
+ int get_mempool() const;
+ void reassign_to_mempool(int pool);
+ void try_assign_to_mempool(int pool);
+
+ size_t get_append_buffer_unused_tail_length() const {
+ return _carriage->unused_tail_length();
+ }
+
+ const buffers_t& buffers() const { return _buffers; }
+ buffers_t& mut_buffers() { return _buffers; }
+ void swap(list& other) noexcept;
+ unsigned length() const {
+#if 0
+ // DEBUG: verify _len
+ unsigned len = 0;
+ for (std::list<ptr>::const_iterator it = _buffers.begin();
+ it != _buffers.end();
+ it++) {
+ len += (*it).length();
+ }
+#ifdef __CEPH__
+ ceph_assert(len == _len);
+#else
+ assert(len == _len);
+#endif // __CEPH__
+#endif
+ return _len;
+ }
+
+ bool contents_equal(const buffer::list& other) const;
+ bool contents_equal(const void* other, size_t length) const;
+
+ bool is_provided_buffer(const char *dst) const;
+ bool is_aligned(unsigned align) const;
+ bool is_page_aligned() const;
+ bool is_n_align_sized(unsigned align) const;
+ bool is_n_page_sized() const;
+ bool is_aligned_size_and_memory(unsigned align_size,
+ unsigned align_memory) const;
+
+ bool is_zero() const;
+
+ // modifiers
+ void clear() noexcept {
+ _carriage = &always_empty_bptr;
+ _buffers.clear_and_dispose();
+ _len = 0;
+ _num = 0;
+ }
+ void push_back(const ptr& bp) {
+ if (bp.length() == 0)
+ return;
+ _buffers.push_back(*ptr_node::create(bp).release());
+ _len += bp.length();
+ _num += 1;
+ }
+ void push_back(ptr&& bp) {
+ if (bp.length() == 0)
+ return;
+ _len += bp.length();
+ _num += 1;
+ _buffers.push_back(*ptr_node::create(std::move(bp)).release());
+ _carriage = &always_empty_bptr;
+ }
+ void push_back(const ptr_node&) = delete;
+ void push_back(ptr_node&) = delete;
+ void push_back(ptr_node&&) = delete;
+ void push_back(std::unique_ptr<ptr_node, ptr_node::disposer> bp) {
+ _carriage = bp.get();
+ _len += bp->length();
+ _num += 1;
+ _buffers.push_back(*bp.release());
+ }
+ void push_back(raw* const r) = delete;
+ void push_back(ceph::unique_leakable_ptr<raw> r) {
+ _buffers.push_back(*ptr_node::create(std::move(r)).release());
+ _carriage = &_buffers.back();
+ _len += _buffers.back().length();
+ _num += 1;
+ }
+
+ void zero();
+ void zero(unsigned o, unsigned l);
+
+ bool is_contiguous() const;
+ void rebuild();
+ void rebuild(std::unique_ptr<ptr_node, ptr_node::disposer> nb);
+ bool rebuild_aligned(unsigned align);
+ // max_buffers = 0 mean don't care _buffers.size(), other
+ // must make _buffers.size() <= max_buffers after rebuilding.
+ bool rebuild_aligned_size_and_memory(unsigned align_size,
+ unsigned align_memory,
+ unsigned max_buffers = 0);
+ bool rebuild_page_aligned();
+
+ void reserve(size_t prealloc);
+
+ [[deprecated("in favor of operator=(list&&)")]] void claim(list& bl) {
+ *this = std::move(bl);
+ }
+ void claim_append(list& bl);
+ void claim_append(list&& bl) {
+ claim_append(bl);
+ }
+
+ // copy with explicit volatile-sharing semantics
+ void share(const list& bl)
+ {
+ if (this != &bl) {
+ clear();
+ for (const auto& bp : bl._buffers) {
+ _buffers.push_back(*ptr_node::create(bp).release());
+ }
+ _len = bl._len;
+ _num = bl._num;
+ }
+ }
+
+#ifdef HAVE_SEASTAR
+ /// convert the bufferlist into a network packet
+ operator seastar::net::packet() &&;
+#endif
+
+ iterator begin(size_t offset=0) {
+ return iterator(this, offset);
+ }
+ iterator end() {
+ return iterator(this, _len, _buffers.end(), 0);
+ }
+
+ const_iterator begin(size_t offset=0) const {
+ return const_iterator(this, offset);
+ }
+ const_iterator cbegin(size_t offset=0) const {
+ return begin(offset);
+ }
+ const_iterator end() const {
+ return const_iterator(this, _len, _buffers.end(), 0);
+ }
+
+ void append(char c);
+ void append(const char *data, unsigned len);
+ void append(std::string s) {
+ append(s.data(), s.length());
+ }
+#if __cplusplus >= 201703L
+ // To forcibly disambiguate between string and string_view in the
+ // case of arrays
+ template<std::size_t N>
+ void append(const char (&s)[N]) {
+ append(s, N);
+ }
+ void append(const char* s) {
+ append(s, strlen(s));
+ }
+ void append(std::string_view s) {
+ append(s.data(), s.length());
+ }
+#endif // __cplusplus >= 201703L
+ void append(const ptr& bp);
+ void append(ptr&& bp);
+ void append(const ptr& bp, unsigned off, unsigned len);
+ void append(const list& bl);
+ /// append each non-empty line from the stream and add '\n',
+ /// so a '\n' will be added even the stream does not end with EOL.
+ ///
+ /// For example, if the stream contains "ABC\n\nDEF", "ABC\nDEF\n" is
+ /// actually appended.
+ void append(std::istream& in);
+ contiguous_filler append_hole(unsigned len);
+ void append_zero(unsigned len);
+ void prepend_zero(unsigned len);
+
+ reserve_t obtain_contiguous_space(const unsigned len);
+
+ /*
+ * get a char
+ */
+ const char& operator[](unsigned n) const;
+ char *c_str();
+ std::string to_str() const;
+
+ void substr_of(const list& other, unsigned off, unsigned len);
+
+ // funky modifer
+ void splice(unsigned off, unsigned len, list *claim_by=0 /*, bufferlist& replace_with */);
+ void write(int off, int len, std::ostream& out) const;
+
+ void encode_base64(list& o);
+ void decode_base64(list& o);
+
+ void write_stream(std::ostream &out) const;
+ void hexdump(std::ostream &out, bool trailing_newline = true) const;
+ ssize_t pread_file(const char *fn, uint64_t off, uint64_t len, std::string *error);
+ int read_file(const char *fn, std::string *error);
+ ssize_t read_fd(int fd, size_t len);
+ ssize_t recv_fd(int fd, size_t len);
+ int write_file(const char *fn, int mode=0644);
+ int write_fd(int fd) const;
+ int write_fd(int fd, uint64_t offset) const;
+ int send_fd(int fd) const;
+ template<typename VectorT>
+ void prepare_iov(VectorT *piov) const {
+#ifdef __CEPH__
+ ceph_assert(_num <= IOV_MAX);
+#else
+ assert(_num <= IOV_MAX);
+#endif
+ piov->resize(_num);
+ unsigned n = 0;
+ for (auto& p : _buffers) {
+ (*piov)[n].iov_base = (void *)p.c_str();
+ (*piov)[n].iov_len = p.length();
+ ++n;
+ }
+ }
+
+ struct iovec_t {
+ uint64_t offset;
+ uint64_t length;
+ std::vector<iovec> iov;
+ };
+ using iov_vec_t = std::vector<iovec_t>;
+ iov_vec_t prepare_iovs() const;
+
+ uint32_t crc32c(uint32_t crc) const;
+ void invalidate_crc();
+
+ // These functions return a bufferlist with a pointer to a single
+ // static buffer. They /must/ not outlive the memory they
+ // reference.
+ static list static_from_mem(char* c, size_t l);
+ static list static_from_cstring(char* c);
+ static list static_from_string(std::string& s);
+ };
+
+} // inline namespace v15_2_0
+
+ /*
+ * efficient hash of one or more bufferlists
+ */
+
+ class hash {
+ uint32_t crc;
+
+ public:
+ hash() : crc(0) { }
+ // cppcheck-suppress noExplicitConstructor
+ hash(uint32_t init) : crc(init) { }
+
+ void update(const buffer::list& bl) {
+ crc = bl.crc32c(crc);
+ }
+
+ uint32_t digest() {
+ return crc;
+ }
+ };
+
+inline bool operator==(const bufferlist &lhs, const bufferlist &rhs) {
+ if (lhs.length() != rhs.length())
+ return false;
+ return std::equal(lhs.begin(), lhs.end(), rhs.begin());
+}
+
+inline bool operator<(const bufferlist& lhs, const bufferlist& rhs) {
+ auto l = lhs.begin(), r = rhs.begin();
+ for (; l != lhs.end() && r != rhs.end(); ++l, ++r) {
+ if (*l < *r) return true;
+ if (*l > *r) return false;
+ }
+ return (l == lhs.end()) && (r != rhs.end()); // lhs.length() < rhs.length()
+}
+
+inline bool operator<=(const bufferlist& lhs, const bufferlist& rhs) {
+ auto l = lhs.begin(), r = rhs.begin();
+ for (; l != lhs.end() && r != rhs.end(); ++l, ++r) {
+ if (*l < *r) return true;
+ if (*l > *r) return false;
+ }
+ return l == lhs.end(); // lhs.length() <= rhs.length()
+}
+
+inline bool operator!=(const bufferlist &l, const bufferlist &r) {
+ return !(l == r);
+}
+inline bool operator>(const bufferlist& lhs, const bufferlist& rhs) {
+ return rhs < lhs;
+}
+inline bool operator>=(const bufferlist& lhs, const bufferlist& rhs) {
+ return rhs <= lhs;
+}
+
+std::ostream& operator<<(std::ostream& out, const buffer::ptr& bp);
+
+std::ostream& operator<<(std::ostream& out, const buffer::raw &r);
+
+std::ostream& operator<<(std::ostream& out, const buffer::list& bl);
+
+inline bufferhash& operator<<(bufferhash& l, const bufferlist &r) {
+ l.update(r);
+ return l;
+}
+
+} // namespace buffer
+
+} // namespace ceph
+
+
+#endif
diff --git a/src/include/buffer_fwd.h b/src/include/buffer_fwd.h
new file mode 100644
index 000000000..6de7b1a1f
--- /dev/null
+++ b/src/include/buffer_fwd.h
@@ -0,0 +1,19 @@
+#ifndef BUFFER_FWD_H
+#define BUFFER_FWD_H
+
+namespace ceph {
+ namespace buffer {
+ inline namespace v15_2_0 {
+ class ptr;
+ class list;
+ }
+ class hash;
+ }
+
+ using bufferptr = buffer::ptr;
+ using bufferlist = buffer::list;
+ using bufferhash = buffer::hash;
+}
+
+#endif
+
diff --git a/src/include/buffer_raw.h b/src/include/buffer_raw.h
new file mode 100644
index 000000000..2298525c9
--- /dev/null
+++ b/src/include/buffer_raw.h
@@ -0,0 +1,120 @@
+// -*- 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) 2017 Red Hat, Inc.
+ *
+ * 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_BUFFER_RAW_H
+#define CEPH_BUFFER_RAW_H
+
+#include <map>
+#include <utility>
+#include <type_traits>
+#include "common/ceph_atomic.h"
+#include "include/buffer.h"
+#include "include/mempool.h"
+#include "include/spinlock.h"
+
+namespace ceph::buffer {
+inline namespace v15_2_0 {
+
+ class raw {
+ public:
+ // In the future we might want to have a slab allocator here with few
+ // embedded slots. This would allow to avoid the "if" in dtor of ptr_node.
+ std::aligned_storage<sizeof(ptr_node),
+ alignof(ptr_node)>::type bptr_storage;
+ protected:
+ char *data;
+ unsigned len;
+ public:
+ ceph::atomic<unsigned> nref { 0 };
+ int mempool;
+
+ std::pair<size_t, size_t> last_crc_offset {std::numeric_limits<size_t>::max(), std::numeric_limits<size_t>::max()};
+ std::pair<uint32_t, uint32_t> last_crc_val;
+
+ mutable ceph::spinlock crc_spinlock;
+
+ explicit raw(unsigned l, int mempool=mempool::mempool_buffer_anon)
+ : data(nullptr), len(l), nref(0), mempool(mempool) {
+ mempool::get_pool(mempool::pool_index_t(mempool)).adjust_count(1, len);
+ }
+ raw(char *c, unsigned l, int mempool=mempool::mempool_buffer_anon)
+ : data(c), len(l), nref(0), mempool(mempool) {
+ mempool::get_pool(mempool::pool_index_t(mempool)).adjust_count(1, len);
+ }
+ virtual ~raw() {
+ mempool::get_pool(mempool::pool_index_t(mempool)).adjust_count(
+ -1, -(int)len);
+ }
+
+ void _set_len(unsigned l) {
+ mempool::get_pool(mempool::pool_index_t(mempool)).adjust_count(
+ -1, -(int)len);
+ len = l;
+ mempool::get_pool(mempool::pool_index_t(mempool)).adjust_count(1, len);
+ }
+
+ void reassign_to_mempool(int pool) {
+ if (pool == mempool) {
+ return;
+ }
+ mempool::get_pool(mempool::pool_index_t(mempool)).adjust_count(
+ -1, -(int)len);
+ mempool = pool;
+ mempool::get_pool(mempool::pool_index_t(pool)).adjust_count(1, len);
+ }
+
+ void try_assign_to_mempool(int pool) {
+ if (mempool == mempool::mempool_buffer_anon) {
+ reassign_to_mempool(pool);
+ }
+ }
+
+private:
+ // no copying.
+ // cppcheck-suppress noExplicitConstructor
+ raw(const raw &other) = delete;
+ const raw& operator=(const raw &other) = delete;
+public:
+ char *get_data() const {
+ return data;
+ }
+ unsigned get_len() const {
+ return len;
+ }
+ bool get_crc(const std::pair<size_t, size_t> &fromto,
+ std::pair<uint32_t, uint32_t> *crc) const {
+ std::lock_guard lg(crc_spinlock);
+ if (last_crc_offset == fromto) {
+ *crc = last_crc_val;
+ return true;
+ }
+ return false;
+ }
+ void set_crc(const std::pair<size_t, size_t> &fromto,
+ const std::pair<uint32_t, uint32_t> &crc) {
+ std::lock_guard lg(crc_spinlock);
+ last_crc_offset = fromto;
+ last_crc_val = crc;
+ }
+ void invalidate_crc() {
+ std::lock_guard lg(crc_spinlock);
+ last_crc_offset.first = std::numeric_limits<size_t>::max();
+ last_crc_offset.second = std::numeric_limits<size_t>::max();
+ }
+ };
+
+} // inline namespace v15_2_0
+} // namespace ceph::buffer
+
+#endif // CEPH_BUFFER_RAW_H
diff --git a/src/include/byteorder.h b/src/include/byteorder.h
new file mode 100644
index 000000000..eb6d5e102
--- /dev/null
+++ b/src/include/byteorder.h
@@ -0,0 +1,55 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+
+#pragma once
+
+#include <boost/endian/conversion.hpp>
+
+#include "int_types.h"
+
+template<typename T>
+inline T swab(T val) {
+ return boost::endian::endian_reverse(val);
+}
+
+template<typename T>
+struct ceph_le {
+private:
+ T v;
+public:
+ ceph_le() = default;
+ explicit ceph_le(T nv)
+ : v{boost::endian::native_to_little(nv)}
+ {}
+ ceph_le<T>& operator=(T nv) {
+ v = boost::endian::native_to_little(nv);
+ return *this;
+ }
+ operator T() const { return boost::endian::little_to_native(v); }
+ friend inline bool operator==(ceph_le a, ceph_le b) {
+ return a.v == b.v;
+ }
+} __attribute__ ((packed));
+
+using ceph_le64 = ceph_le<__u64>;
+using ceph_le32 = ceph_le<__u32>;
+using ceph_le16 = ceph_le<__u16>;
+
+using ceph_les64 = ceph_le<__s64>;
+using ceph_les32 = ceph_le<__s32>;
+using ceph_les16 = ceph_le<__s16>;
+
+inline ceph_les64 init_les64(__s64 x) {
+ ceph_les64 v;
+ v = x;
+ return v;
+}
+inline ceph_les32 init_les32(__s32 x) {
+ ceph_les32 v;
+ v = x;
+ return v;
+}
+inline ceph_les16 init_les16(__s16 x) {
+ ceph_les16 v;
+ v = x;
+ return v;
+}
diff --git a/src/include/ceph_assert.h b/src/include/ceph_assert.h
new file mode 100644
index 000000000..0627894ea
--- /dev/null
+++ b/src/include/ceph_assert.h
@@ -0,0 +1,147 @@
+#ifndef CEPH_ASSERT_H
+#define CEPH_ASSERT_H
+
+#include <cstdlib>
+#include <string>
+
+#ifndef __STRING
+# define __STRING(x) #x
+#endif
+
+#if defined(__linux__)
+#include <features.h>
+
+#elif defined(__FreeBSD__)
+#include <sys/cdefs.h>
+#define __GNUC_PREREQ(minor, major) __GNUC_PREREQ__(minor, major)
+#elif defined(__sun) || defined(_AIX)
+#include "include/compat.h"
+#include <assert.h>
+#endif
+
+#ifdef __CEPH__
+# include "acconfig.h"
+#endif
+
+#include "include/common_fwd.h"
+
+namespace ceph {
+
+struct BackTrace;
+
+/*
+ * Select a function-name variable based on compiler tests, and any compiler
+ * specific overrides.
+ */
+#if defined(HAVE_PRETTY_FUNC)
+# define __CEPH_ASSERT_FUNCTION __PRETTY_FUNCTION__
+#elif defined(HAVE_FUNC)
+# define __CEPH_ASSERT_FUNCTION __func__
+#else
+# define __CEPH_ASSERT_FUNCTION ((__const char *) 0)
+#endif
+
+extern void register_assert_context(CephContext *cct);
+
+struct assert_data {
+ const char *assertion;
+ const char *file;
+ const int line;
+ const char *function;
+};
+
+extern void __ceph_assert_fail(const char *assertion, const char *file, int line, const char *function)
+ __attribute__ ((__noreturn__));
+extern void __ceph_assert_fail(const assert_data &ctx)
+ __attribute__ ((__noreturn__));
+
+extern void __ceph_assertf_fail(const char *assertion, const char *file, int line, const char *function, const char* msg, ...)
+ __attribute__ ((__noreturn__));
+extern void __ceph_assert_warn(const char *assertion, const char *file, int line, const char *function);
+
+[[noreturn]] void __ceph_abort(const char *file, int line, const char *func,
+ const std::string& msg);
+
+[[noreturn]] void __ceph_abortf(const char *file, int line, const char *func,
+ const char* msg, ...);
+
+#define _CEPH_ASSERT_VOID_CAST static_cast<void>
+
+#define assert_warn(expr) \
+ ((expr) \
+ ? _CEPH_ASSERT_VOID_CAST (0) \
+ : ::ceph::__ceph_assert_warn (__STRING(expr), __FILE__, __LINE__, __CEPH_ASSERT_FUNCTION))
+
+}
+
+using namespace ceph;
+
+
+/*
+ * ceph_abort aborts the program with a nice backtrace.
+ *
+ * Currently, it's the same as assert(0), but we may one day make assert a
+ * debug-only thing, like it is in many projects.
+ */
+#define ceph_abort(msg, ...) \
+ ::ceph::__ceph_abort( __FILE__, __LINE__, __CEPH_ASSERT_FUNCTION, "abort() called")
+
+#define ceph_abort_msg(msg) \
+ ::ceph::__ceph_abort( __FILE__, __LINE__, __CEPH_ASSERT_FUNCTION, msg)
+
+#define ceph_abort_msgf(...) \
+ ::ceph::__ceph_abortf( __FILE__, __LINE__, __CEPH_ASSERT_FUNCTION, __VA_ARGS__)
+
+#ifdef __SANITIZE_ADDRESS__
+#define ceph_assert(expr) \
+ do { \
+ ((expr)) \
+ ? _CEPH_ASSERT_VOID_CAST (0) \
+ : ::ceph::__ceph_assert_fail(__STRING(expr), __FILE__, __LINE__, __CEPH_ASSERT_FUNCTION); \
+ } while (false)
+#else
+#define ceph_assert(expr) \
+ do { static const ceph::assert_data assert_data_ctx = \
+ {__STRING(expr), __FILE__, __LINE__, __CEPH_ASSERT_FUNCTION}; \
+ ((expr) \
+ ? _CEPH_ASSERT_VOID_CAST (0) \
+ : ::ceph::__ceph_assert_fail(assert_data_ctx)); } while(false)
+#endif
+
+// this variant will *never* get compiled out to NDEBUG in the future.
+// (ceph_assert currently doesn't either, but in the future it might.)
+#ifdef __SANITIZE_ADDRESS__
+#define ceph_assert_always(expr) \
+ do { \
+ ((expr)) \
+ ? _CEPH_ASSERT_VOID_CAST (0) \
+ : ::ceph::__ceph_assert_fail(__STRING(expr), __FILE__, __LINE__, __CEPH_ASSERT_FUNCTION); \
+ } while(false)
+#else
+#define ceph_assert_always(expr) \
+ do { static const ceph::assert_data assert_data_ctx = \
+ {__STRING(expr), __FILE__, __LINE__, __CEPH_ASSERT_FUNCTION}; \
+ ((expr) \
+ ? _CEPH_ASSERT_VOID_CAST (0) \
+ : ::ceph::__ceph_assert_fail(assert_data_ctx)); } while(false)
+#endif
+
+// Named by analogy with printf. Along with an expression, takes a format
+// string and parameters which are printed if the assertion fails.
+#define assertf(expr, ...) \
+ ((expr) \
+ ? _CEPH_ASSERT_VOID_CAST (0) \
+ : ::ceph::__ceph_assertf_fail (__STRING(expr), __FILE__, __LINE__, __CEPH_ASSERT_FUNCTION, __VA_ARGS__))
+#define ceph_assertf(expr, ...) \
+ ((expr) \
+ ? _CEPH_ASSERT_VOID_CAST (0) \
+ : ::ceph::__ceph_assertf_fail (__STRING(expr), __FILE__, __LINE__, __CEPH_ASSERT_FUNCTION, __VA_ARGS__))
+
+// this variant will *never* get compiled out to NDEBUG in the future.
+// (ceph_assertf currently doesn't either, but in the future it might.)
+#define ceph_assertf_always(expr, ...) \
+ ((expr) \
+ ? _CEPH_ASSERT_VOID_CAST (0) \
+ : ::ceph::__ceph_assertf_fail (__STRING(expr), __FILE__, __LINE__, __CEPH_ASSERT_FUNCTION, __VA_ARGS__))
+
+#endif
diff --git a/src/include/ceph_features.h b/src/include/ceph_features.h
new file mode 100644
index 000000000..794e10efd
--- /dev/null
+++ b/src/include/ceph_features.h
@@ -0,0 +1,280 @@
+#ifndef __CEPH_FEATURES
+#define __CEPH_FEATURES
+
+#include "sys/types.h"
+
+/*
+ * Each time we reclaim bits for reuse we need to specify another
+ * bitmask that, if all bits are set, indicates we have the new
+ * incarnation of that feature. Base case is 1 (first use)
+ */
+#define CEPH_FEATURE_INCARNATION_1 (0ull)
+#define CEPH_FEATURE_INCARNATION_2 (1ull<<57) // SERVER_JEWEL
+#define CEPH_FEATURE_INCARNATION_3 ((1ull<<57)|(1ull<<28)) // SERVER_MIMIC
+
+#define DEFINE_CEPH_FEATURE(bit, incarnation, name) \
+ const static uint64_t CEPH_FEATURE_##name = (1ULL<<bit); \
+ const static uint64_t CEPH_FEATUREMASK_##name = \
+ (1ULL<<bit | CEPH_FEATURE_INCARNATION_##incarnation);
+
+// this bit is ignored but still advertised by release *when*
+#define DEFINE_CEPH_FEATURE_DEPRECATED(bit, incarnation, name, when) \
+ const static uint64_t DEPRECATED_CEPH_FEATURE_##name = (1ULL<<bit); \
+ const static uint64_t DEPRECATED_CEPH_FEATUREMASK_##name = \
+ (1ULL<<bit | CEPH_FEATURE_INCARNATION_##incarnation);
+
+// this bit is ignored by release *unused* and not advertised by
+// release *unadvertised*
+#define DEFINE_CEPH_FEATURE_RETIRED(bit, inc, name, unused, unadvertised)
+
+
+// test for a feature. this test is safer than a typical mask against
+// the bit because it ensures that we have the bit AND the marker for the
+// bit's incarnation. this must be used in any case where the features
+// bits may include an old meaning of the bit.
+#define HAVE_FEATURE(x, name) \
+ (((x) & (CEPH_FEATUREMASK_##name)) == (CEPH_FEATUREMASK_##name))
+
+
+/*
+ * Notes on deprecation:
+ *
+ * For feature bits used *only* on the server-side:
+ *
+ * - In the first phase we indicate that a feature is DEPRECATED as of
+ * a particular release. This is the first major release X (say,
+ * mimic) that does not depend on its peers advertising the feature.
+ * That is, it safely assumes its peers all have the feature. We
+ * indicate this with the DEPRECATED macro. For example,
+ *
+ * DEFINE_CEPH_FEATURE_DEPRECATED( 2, 1, MON_METADATA, MIMIC)
+ *
+ * because 13.2.z (mimic) did not care if its peers advertised this
+ * feature bit.
+ *
+ * - In the second phase we stop advertising the the bit and call it
+ * RETIRED. This can normally be done 2 major releases
+ * following the one in which we marked the feature DEPRECATED. In
+ * the above example, for 15.0.z (octopus) we can say:
+ *
+ * DEFINE_CEPH_FEATURE_RETIRED( 2, 1, MON_METADATA, MIMIC, OCTOPUS)
+ *
+ * - The bit can be reused in the next release that will never talk to
+ * a pre-octopus daemon (13 mimic or 14 nautlius) that advertises the
+ * bit: in this case, the 16.y.z (P-release).
+ *
+ * This ensures that no two versions who have different meanings for
+ * the bit ever speak to each other.
+ */
+
+/*
+ * Notes on the kernel client:
+ *
+ * - "X" means that the feature bit has been advertised and supported
+ * since kernel X
+ *
+ * - "X req" means that the feature bit has been advertised and required
+ * since kernel X
+ *
+ * The remaining feature bits are not and have never been used by the
+ * kernel client.
+ */
+
+DEFINE_CEPH_FEATURE( 0, 1, UID)
+DEFINE_CEPH_FEATURE( 1, 1, NOSRCADDR) // 2.6.35 req
+DEFINE_CEPH_FEATURE_RETIRED( 2, 1, MONCLOCKCHECK, JEWEL, LUMINOUS)
+DEFINE_CEPH_FEATURE( 2, 3, SERVER_NAUTILUS)
+DEFINE_CEPH_FEATURE( 3, 1, FLOCK) // 2.6.36
+DEFINE_CEPH_FEATURE( 4, 1, SUBSCRIBE2) // 4.6 req
+DEFINE_CEPH_FEATURE( 5, 1, MONNAMES)
+DEFINE_CEPH_FEATURE( 6, 1, RECONNECT_SEQ) // 3.10 req
+DEFINE_CEPH_FEATURE( 7, 1, DIRLAYOUTHASH) // 2.6.38
+DEFINE_CEPH_FEATURE( 8, 1, OBJECTLOCATOR)
+DEFINE_CEPH_FEATURE( 9, 1, PGID64) // 3.9 req
+DEFINE_CEPH_FEATURE(10, 1, INCSUBOSDMAP)
+DEFINE_CEPH_FEATURE(11, 1, PGPOOL3) // 3.9 req
+DEFINE_CEPH_FEATURE(12, 1, OSDREPLYMUX)
+DEFINE_CEPH_FEATURE(13, 1, OSDENC) // 3.9 req
+DEFINE_CEPH_FEATURE_RETIRED(14, 1, OMAP, HAMMER, JEWEL)
+DEFINE_CEPH_FEATURE(14, 2, SERVER_KRAKEN)
+DEFINE_CEPH_FEATURE(15, 1, MONENC)
+DEFINE_CEPH_FEATURE_RETIRED(16, 1, QUERY_T, JEWEL, LUMINOUS)
+DEFINE_CEPH_FEATURE(16, 3, SERVER_OCTOPUS)
+DEFINE_CEPH_FEATURE(16, 3, OSD_REPOP_MLCOD)
+DEFINE_CEPH_FEATURE_RETIRED(17, 1, INDEP_PG_MAP, JEWEL, LUMINOUS)
+DEFINE_CEPH_FEATURE(17, 3, OS_PERF_STAT_NS)
+DEFINE_CEPH_FEATURE(18, 1, CRUSH_TUNABLES) // 3.6
+DEFINE_CEPH_FEATURE_RETIRED(19, 1, CHUNKY_SCRUB, JEWEL, LUMINOUS)
+DEFINE_CEPH_FEATURE(19, 2, OSD_PGLOG_HARDLIMIT)
+DEFINE_CEPH_FEATURE_RETIRED(20, 1, MON_NULLROUTE, JEWEL, LUMINOUS)
+DEFINE_CEPH_FEATURE(20, 3, SERVER_PACIFIC)
+DEFINE_CEPH_FEATURE_RETIRED(21, 1, MON_GV, HAMMER, JEWEL)
+DEFINE_CEPH_FEATURE(21, 2, SERVER_LUMINOUS) // 4.13
+DEFINE_CEPH_FEATURE(21, 2, RESEND_ON_SPLIT) // overlap
+DEFINE_CEPH_FEATURE(21, 2, RADOS_BACKOFF) // overlap
+DEFINE_CEPH_FEATURE(21, 2, OSDMAP_PG_UPMAP) // overlap
+DEFINE_CEPH_FEATURE(21, 2, CRUSH_CHOOSE_ARGS) // overlap
+DEFINE_CEPH_FEATURE_RETIRED(22, 1, BACKFILL_RESERVATION, JEWEL, LUMINOUS)
+DEFINE_CEPH_FEATURE(22, 2, OSD_FIXED_COLLECTION_LIST)
+DEFINE_CEPH_FEATURE(23, 1, MSG_AUTH) // 3.19 req (unless nocephx_require_signatures)
+DEFINE_CEPH_FEATURE_RETIRED(24, 1, RECOVERY_RESERVATION, JEWEL, LUMINOUS)
+DEFINE_CEPH_FEATURE(24, 2, RECOVERY_RESERVATION_2)
+DEFINE_CEPH_FEATURE(25, 1, CRUSH_TUNABLES2) // 3.9
+DEFINE_CEPH_FEATURE(26, 1, CREATEPOOLID)
+DEFINE_CEPH_FEATURE(27, 1, REPLY_CREATE_INODE) // 3.9
+DEFINE_CEPH_FEATURE_RETIRED(28, 1, OSD_HBMSGS, HAMMER, JEWEL)
+DEFINE_CEPH_FEATURE(28, 2, SERVER_MIMIC)
+DEFINE_CEPH_FEATURE(29, 1, MDSENC) // 4.7
+DEFINE_CEPH_FEATURE(30, 1, OSDHASHPSPOOL) // 3.9
+DEFINE_CEPH_FEATURE_RETIRED(31, 1, MON_SINGLE_PAXOS, NAUTILUS, PACIFIC)
+DEFINE_CEPH_FEATURE(31, 3, SERVER_REEF)
+DEFINE_CEPH_FEATURE_RETIRED(32, 1, OSD_SNAPMAPPER, JEWEL, LUMINOUS)
+DEFINE_CEPH_FEATURE(32, 3, STRETCH_MODE)
+DEFINE_CEPH_FEATURE_RETIRED(33, 1, MON_SCRUB, JEWEL, LUMINOUS)
+DEFINE_CEPH_FEATURE(33, 3, SERVER_QUINCY)
+DEFINE_CEPH_FEATURE_RETIRED(34, 1, OSD_PACKED_RECOVERY, JEWEL, LUMINOUS)
+DEFINE_CEPH_FEATURE(34, 3, RANGE_BLOCKLIST)
+DEFINE_CEPH_FEATURE(35, 1, OSD_CACHEPOOL) // 3.14
+DEFINE_CEPH_FEATURE(36, 1, CRUSH_V2) // 3.14
+DEFINE_CEPH_FEATURE(37, 1, EXPORT_PEER) // 3.14
+DEFINE_CEPH_FEATURE_RETIRED(38, 1, OSD_ERASURE_CODES, MIMIC, OCTOPUS)
+// available
+DEFINE_CEPH_FEATURE(39, 1, OSDMAP_ENC) // 3.15
+DEFINE_CEPH_FEATURE(40, 1, MDS_INLINE_DATA) // 3.19
+DEFINE_CEPH_FEATURE(41, 1, CRUSH_TUNABLES3) // 3.15
+DEFINE_CEPH_FEATURE(41, 1, OSD_PRIMARY_AFFINITY) // overlap
+DEFINE_CEPH_FEATURE(42, 1, MSGR_KEEPALIVE2) // 4.3 (for consistency)
+DEFINE_CEPH_FEATURE(43, 1, OSD_POOLRESEND) // 4.13
+DEFINE_CEPH_FEATURE_RETIRED(44, 1, ERASURE_CODE_PLUGINS_V2, MIMIC, OCTOPUS)
+// available
+DEFINE_CEPH_FEATURE_RETIRED(45, 1, OSD_SET_ALLOC_HINT, JEWEL, LUMINOUS)
+// available
+DEFINE_CEPH_FEATURE(46, 1, OSD_FADVISE_FLAGS)
+DEFINE_CEPH_FEATURE_RETIRED(46, 1, OSD_REPOP, JEWEL, LUMINOUS) // overlap
+DEFINE_CEPH_FEATURE_RETIRED(46, 1, OSD_OBJECT_DIGEST, JEWEL, LUMINOUS) // overlap
+DEFINE_CEPH_FEATURE_RETIRED(46, 1, OSD_TRANSACTION_MAY_LAYOUT, JEWEL, LUMINOUS) // overlap
+DEFINE_CEPH_FEATURE(47, 1, MDS_QUOTA) // 4.17
+DEFINE_CEPH_FEATURE(48, 1, CRUSH_V4) // 4.1
+DEFINE_CEPH_FEATURE_RETIRED(49, 1, OSD_MIN_SIZE_RECOVERY, JEWEL, LUMINOUS)
+DEFINE_CEPH_FEATURE_RETIRED(49, 1, OSD_PROXY_FEATURES, JEWEL, LUMINOUS) // overlap
+// available
+DEFINE_CEPH_FEATURE_RETIRED(50, 1, MON_METADATA, MIMIC, OCTOPUS)
+// available
+DEFINE_CEPH_FEATURE_RETIRED(51, 1, OSD_BITWISE_HOBJ_SORT, MIMIC, OCTOPUS)
+// available
+DEFINE_CEPH_FEATURE_RETIRED(52, 1, OSD_PROXY_WRITE_FEATURES, MIMIC, OCTOPUS)
+// available
+DEFINE_CEPH_FEATURE_RETIRED(53, 1, ERASURE_CODE_PLUGINS_V3, MIMIC, OCTOPUS)
+// available
+DEFINE_CEPH_FEATURE_RETIRED(54, 1, OSD_HITSET_GMT, MIMIC, OCTOPUS)
+// available
+DEFINE_CEPH_FEATURE_RETIRED(55, 1, HAMMER_0_94_4, MIMIC, OCTOPUS)
+// available
+DEFINE_CEPH_FEATURE(56, 1, NEW_OSDOP_ENCODING) // 4.13 (for pg_pool_t >= v25)
+DEFINE_CEPH_FEATURE(57, 1, MON_STATEFUL_SUB) // 4.13
+DEFINE_CEPH_FEATURE_RETIRED(57, 1, MON_ROUTE_OSDMAP, MIMIC, OCTOPUS) // overlap
+DEFINE_CEPH_FEATURE(57, 1, SERVER_JEWEL) // overlap
+DEFINE_CEPH_FEATURE(58, 1, CRUSH_TUNABLES5) // 4.5
+DEFINE_CEPH_FEATURE(58, 1, NEW_OSDOPREPLY_ENCODING) // overlap
+DEFINE_CEPH_FEATURE(58, 1, FS_FILE_LAYOUT_V2) // overlap
+DEFINE_CEPH_FEATURE(59, 1, FS_BTIME)
+DEFINE_CEPH_FEATURE(59, 1, FS_CHANGE_ATTR) // overlap
+DEFINE_CEPH_FEATURE(59, 1, MSG_ADDR2) // overlap
+DEFINE_CEPH_FEATURE(60, 1, OSD_RECOVERY_DELETES) // *do not share this bit*
+DEFINE_CEPH_FEATURE(61, 1, CEPHX_V2) // 4.19, *do not share this bit*
+
+DEFINE_CEPH_FEATURE(62, 1, RESERVED) // do not use; used as a sentinel
+DEFINE_CEPH_FEATURE_RETIRED(63, 1, RESERVED_BROKEN, LUMINOUS, QUINCY) // client-facing
+// available
+
+
+/*
+ * Features supported. Should be everything above.
+ */
+#define CEPH_FEATURES_ALL \
+ (CEPH_FEATURE_UID | \
+ CEPH_FEATURE_NOSRCADDR | \
+ CEPH_FEATURE_FLOCK | \
+ CEPH_FEATURE_SUBSCRIBE2 | \
+ CEPH_FEATURE_MONNAMES | \
+ CEPH_FEATURE_RECONNECT_SEQ | \
+ CEPH_FEATURE_DIRLAYOUTHASH | \
+ CEPH_FEATURE_OBJECTLOCATOR | \
+ CEPH_FEATURE_PGID64 | \
+ CEPH_FEATURE_INCSUBOSDMAP | \
+ CEPH_FEATURE_PGPOOL3 | \
+ CEPH_FEATURE_OSDREPLYMUX | \
+ CEPH_FEATURE_OSDENC | \
+ CEPH_FEATURE_MONENC | \
+ CEPH_FEATURE_CRUSH_TUNABLES | \
+ CEPH_FEATURE_MSG_AUTH | \
+ CEPH_FEATURE_CRUSH_TUNABLES2 | \
+ CEPH_FEATURE_CREATEPOOLID | \
+ CEPH_FEATURE_REPLY_CREATE_INODE | \
+ CEPH_FEATURE_MDSENC | \
+ CEPH_FEATURE_OSDHASHPSPOOL | \
+ CEPH_FEATURE_NEW_OSDOP_ENCODING | \
+ CEPH_FEATURE_NEW_OSDOPREPLY_ENCODING | \
+ CEPH_FEATURE_OSD_CACHEPOOL | \
+ CEPH_FEATURE_CRUSH_V2 | \
+ CEPH_FEATURE_EXPORT_PEER | \
+ CEPH_FEATURE_OSDMAP_ENC | \
+ CEPH_FEATURE_MDS_INLINE_DATA | \
+ CEPH_FEATURE_CRUSH_TUNABLES3 | \
+ CEPH_FEATURE_OSD_PRIMARY_AFFINITY | \
+ CEPH_FEATURE_MSGR_KEEPALIVE2 | \
+ CEPH_FEATURE_OSD_POOLRESEND | \
+ CEPH_FEATURE_OSD_FADVISE_FLAGS | \
+ CEPH_FEATURE_MDS_QUOTA | \
+ CEPH_FEATURE_CRUSH_V4 | \
+ CEPH_FEATURE_MON_STATEFUL_SUB | \
+ CEPH_FEATURE_CRUSH_TUNABLES5 | \
+ CEPH_FEATURE_SERVER_JEWEL | \
+ CEPH_FEATURE_FS_FILE_LAYOUT_V2 | \
+ CEPH_FEATURE_SERVER_KRAKEN | \
+ CEPH_FEATURE_FS_BTIME | \
+ CEPH_FEATURE_FS_CHANGE_ATTR | \
+ CEPH_FEATURE_MSG_ADDR2 | \
+ CEPH_FEATURE_SERVER_LUMINOUS | \
+ CEPH_FEATURE_RESEND_ON_SPLIT | \
+ CEPH_FEATURE_RADOS_BACKOFF | \
+ CEPH_FEATURE_OSD_RECOVERY_DELETES | \
+ CEPH_FEATURE_SERVER_MIMIC | \
+ CEPH_FEATURE_RECOVERY_RESERVATION_2 | \
+ CEPH_FEATURE_SERVER_NAUTILUS | \
+ CEPH_FEATURE_CEPHX_V2 | \
+ CEPH_FEATURE_OSD_PGLOG_HARDLIMIT | \
+ CEPH_FEATUREMASK_SERVER_OCTOPUS | \
+ CEPH_FEATUREMASK_STRETCH_MODE | \
+ CEPH_FEATUREMASK_OSD_REPOP_MLCOD | \
+ CEPH_FEATUREMASK_SERVER_PACIFIC | \
+ CEPH_FEATURE_OSD_FIXED_COLLECTION_LIST | \
+ CEPH_FEATUREMASK_SERVER_QUINCY | \
+ CEPH_FEATURE_RANGE_BLOCKLIST | \
+ CEPH_FEATUREMASK_SERVER_REEF | \
+ 0ULL)
+
+#define CEPH_FEATURES_SUPPORTED_DEFAULT CEPH_FEATURES_ALL
+
+/*
+ * crush related features
+ */
+#define CEPH_FEATURES_CRUSH \
+ (CEPH_FEATURE_CRUSH_TUNABLES | \
+ CEPH_FEATURE_CRUSH_TUNABLES2 | \
+ CEPH_FEATURE_CRUSH_TUNABLES3 | \
+ CEPH_FEATURE_CRUSH_TUNABLES5 | \
+ CEPH_FEATURE_CRUSH_V2 | \
+ CEPH_FEATURE_CRUSH_V4 | \
+ CEPH_FEATUREMASK_CRUSH_CHOOSE_ARGS)
+
+/*
+ * make sure we don't try to use the reserved features
+ */
+#define CEPH_STATIC_ASSERT(x) (void)(sizeof(int[((x)==0) ? -1 : 0]))
+
+static inline void ____build_time_check_for_reserved_bits(void) {
+ CEPH_STATIC_ASSERT((CEPH_FEATURES_ALL & CEPH_FEATURE_RESERVED) == 0);
+}
+
+#endif
diff --git a/src/include/ceph_frag.h b/src/include/ceph_frag.h
new file mode 100644
index 000000000..5babb8e95
--- /dev/null
+++ b/src/include/ceph_frag.h
@@ -0,0 +1,109 @@
+#ifndef FS_CEPH_FRAG_H
+#define FS_CEPH_FRAG_H
+
+/*
+ * "Frags" are a way to describe a subset of a 32-bit number space,
+ * using a mask and a value to match against that mask. Any given frag
+ * (subset of the number space) can be partitioned into 2^n sub-frags.
+ *
+ * Frags are encoded into a 32-bit word:
+ * 8 upper bits = "bits"
+ * 24 lower bits = "value"
+ * (We could go to 5+27 bits, but who cares.)
+ *
+ * We use the _most_ significant bits of the 24 bit value. This makes
+ * values logically sort.
+ *
+ * Unfortunately, because the "bits" field is still in the high bits, we
+ * can't sort encoded frags numerically. However, it does allow you
+ * to feed encoded frags as values into frag_contains_value.
+ */
+static inline __u32 ceph_frag_make(__u32 b, __u32 v)
+{
+ return (b << 24) |
+ (v & (0xffffffu << (24-b)) & 0xffffffu);
+}
+static inline __u32 ceph_frag_bits(__u32 f)
+{
+ return f >> 24;
+}
+static inline __u32 ceph_frag_value(__u32 f)
+{
+ return f & 0xffffffu;
+}
+static inline __u32 ceph_frag_mask(__u32 f)
+{
+ return (0xffffffu << (24-ceph_frag_bits(f))) & 0xffffffu;
+}
+static inline __u32 ceph_frag_mask_shift(__u32 f)
+{
+ return 24 - ceph_frag_bits(f);
+}
+
+static inline int ceph_frag_contains_value(__u32 f, __u32 v)
+{
+ return (v & ceph_frag_mask(f)) == ceph_frag_value(f);
+}
+static inline int ceph_frag_contains_frag(__u32 f, __u32 sub)
+{
+ /* is sub as specific as us, and contained by us? */
+ return ceph_frag_bits(sub) >= ceph_frag_bits(f) &&
+ (ceph_frag_value(sub) & ceph_frag_mask(f)) == ceph_frag_value(f);
+}
+
+static inline __u32 ceph_frag_parent(__u32 f)
+{
+ return ceph_frag_make(ceph_frag_bits(f) - 1,
+ ceph_frag_value(f) & (ceph_frag_mask(f) << 1));
+}
+static inline int ceph_frag_is_left_child(__u32 f)
+{
+ return ceph_frag_bits(f) > 0 &&
+ (ceph_frag_value(f) & (0x1000000 >> ceph_frag_bits(f))) == 0;
+}
+static inline int ceph_frag_is_right_child(__u32 f)
+{
+ return ceph_frag_bits(f) > 0 &&
+ (ceph_frag_value(f) & (0x1000000 >> ceph_frag_bits(f))) == 1;
+}
+static inline __u32 ceph_frag_sibling(__u32 f)
+{
+ return ceph_frag_make(ceph_frag_bits(f),
+ ceph_frag_value(f) ^ (0x1000000 >> ceph_frag_bits(f)));
+}
+static inline __u32 ceph_frag_left_child(__u32 f)
+{
+ return ceph_frag_make(ceph_frag_bits(f)+1, ceph_frag_value(f));
+}
+static inline __u32 ceph_frag_right_child(__u32 f)
+{
+ return ceph_frag_make(ceph_frag_bits(f)+1,
+ ceph_frag_value(f) | (0x1000000 >> (1+ceph_frag_bits(f))));
+}
+static inline __u32 ceph_frag_make_child(__u32 f, int by, int i)
+{
+ int newbits = ceph_frag_bits(f) + by;
+ return ceph_frag_make(newbits,
+ ceph_frag_value(f) | (i << (24 - newbits)));
+}
+static inline int ceph_frag_is_leftmost(__u32 f)
+{
+ return ceph_frag_value(f) == 0;
+}
+static inline int ceph_frag_is_rightmost(__u32 f)
+{
+ return ceph_frag_value(f) == ceph_frag_mask(f);
+}
+static inline __u32 ceph_frag_next(__u32 f)
+{
+ return ceph_frag_make(ceph_frag_bits(f),
+ ceph_frag_value(f) + (0x1000000 >> ceph_frag_bits(f)));
+}
+
+/*
+ * comparator to sort frags logically, as when traversing the
+ * number space in ascending order...
+ */
+int ceph_frag_compare(__u32 a, __u32 b);
+
+#endif
diff --git a/src/include/ceph_fs.h b/src/include/ceph_fs.h
new file mode 100644
index 000000000..28440c820
--- /dev/null
+++ b/src/include/ceph_fs.h
@@ -0,0 +1,1137 @@
+/*
+ * ceph_fs.h - Ceph constants and data types to share between kernel and
+ * user space.
+ *
+ * Most types in this file are defined as little-endian, and are
+ * primarily intended to describe data structures that pass over the
+ * wire or that are stored on disk.
+ *
+ * LGPL-2.1 or LGPL-3.0
+ */
+
+#ifndef CEPH_FS_H
+#define CEPH_FS_H
+
+#include "msgr.h"
+#include "rados.h"
+#include "include/encoding.h"
+#include "include/denc.h"
+
+/*
+ * The data structures defined here are shared between Linux kernel and
+ * user space. Also, those data structures are maintained always in
+ * little-endian byte order, even on big-endian systems. This is handled
+ * differently in kernel vs. user space. For use as kernel headers, the
+ * little-endian fields need to use the __le16/__le32/__le64 types. These
+ * are markers that indicate endian conversion routines must be used
+ * whenever such fields are accessed, which can be verified by checker
+ * tools like "sparse". For use as user-space headers, the little-endian
+ * fields instead use types ceph_le16/ceph_le32/ceph_le64, which are C++
+ * classes that implement automatic endian conversion on every access.
+ * To still allow for header sharing, this file uses the __le types, but
+ * redefines those to the ceph_ types when compiled in user space.
+ */
+#ifndef __KERNEL__
+#include "byteorder.h"
+#define __le16 ceph_le16
+#define __le32 ceph_le32
+#define __le64 ceph_le64
+#endif
+
+/*
+ * subprotocol versions. when specific messages types or high-level
+ * protocols change, bump the affected components. we keep rev
+ * internal cluster protocols separately from the public,
+ * client-facing protocol.
+ */
+#define CEPH_OSDC_PROTOCOL 24 /* server/client */
+#define CEPH_MDSC_PROTOCOL 32 /* server/client */
+#define CEPH_MONC_PROTOCOL 15 /* server/client */
+
+
+#define CEPH_INO_ROOT 1
+/*
+ * hidden .ceph dir, which is no longer created but
+ * recognised in existing filesystems so that we
+ * don't try to fragment it.
+ */
+#define CEPH_INO_CEPH 2
+#define CEPH_INO_GLOBAL_SNAPREALM 3
+#define CEPH_INO_LOST_AND_FOUND 4 /* reserved ino for use in recovery */
+
+/* arbitrary limit on max # of monitors (cluster of 3 is typical) */
+#define CEPH_MAX_MON 31
+
+/*
+ * ceph_file_layout - describe data layout for a file/inode
+ */
+struct ceph_file_layout {
+ /* file -> object mapping */
+ __le32 fl_stripe_unit; /* stripe unit, in bytes. must be multiple
+ of page size. */
+ __le32 fl_stripe_count; /* over this many objects */
+ __le32 fl_object_size; /* until objects are this big, then move to
+ new objects */
+ __le32 fl_cas_hash; /* UNUSED. 0 = none; 1 = sha256 */
+
+ /* pg -> disk layout */
+ __le32 fl_object_stripe_unit; /* UNUSED. for per-object parity, if any */
+
+ /* object -> pg layout */
+ __le32 fl_unused; /* unused; used to be preferred primary for pg (-1 for none) */
+ __le32 fl_pg_pool; /* namespace, crush rule, rep level */
+} __attribute__ ((packed));
+
+#define CEPH_MIN_STRIPE_UNIT 65536
+
+struct ceph_dir_layout {
+ __u8 dl_dir_hash; /* see ceph_hash.h for ids */
+ __u8 dl_unused1;
+ __u16 dl_unused2;
+ __u32 dl_unused3;
+} __attribute__ ((packed));
+
+/* crypto algorithms */
+#define CEPH_CRYPTO_NONE 0x0
+#define CEPH_CRYPTO_AES 0x1
+
+#define CEPH_AES_IV "cephsageyudagreg"
+
+/* security/authentication protocols */
+#define CEPH_AUTH_UNKNOWN 0x0
+#define CEPH_AUTH_NONE 0x1
+#define CEPH_AUTH_CEPHX 0x2
+
+/* msgr2 protocol modes */
+#define CEPH_CON_MODE_UNKNOWN 0x0
+#define CEPH_CON_MODE_CRC 0x1
+#define CEPH_CON_MODE_SECURE 0x2
+
+extern const char *ceph_con_mode_name(int con_mode);
+
+/* For options with "_", like: GSS_GSS
+ which means: Mode/Protocol to validate "authentication_authorization",
+ where:
+ - Authentication: Verifying the identity of an entity.
+ - Authorization: Verifying that an authenticated entity has
+ the right to access a particular resource.
+*/
+#define CEPH_AUTH_GSS 0x4
+#define CEPH_AUTH_GSS_GSS CEPH_AUTH_GSS
+
+#define CEPH_AUTH_UID_DEFAULT ((__u64) -1)
+
+
+/*********************************************
+ * message layer
+ */
+
+/*
+ * message types
+ */
+
+/* misc */
+#define CEPH_MSG_SHUTDOWN 1
+#define CEPH_MSG_PING 2
+
+/* client <-> monitor */
+#define CEPH_MSG_MON_MAP 4
+#define CEPH_MSG_MON_GET_MAP 5
+#define CEPH_MSG_MON_GET_OSDMAP 6
+#define CEPH_MSG_MON_METADATA 7
+#define CEPH_MSG_STATFS 13
+#define CEPH_MSG_STATFS_REPLY 14
+#define CEPH_MSG_MON_SUBSCRIBE 15
+#define CEPH_MSG_MON_SUBSCRIBE_ACK 16
+#define CEPH_MSG_AUTH 17
+#define CEPH_MSG_AUTH_REPLY 18
+#define CEPH_MSG_MON_GET_VERSION 19
+#define CEPH_MSG_MON_GET_VERSION_REPLY 20
+
+/* client <-> mds */
+#define CEPH_MSG_MDS_MAP 21
+
+#define CEPH_MSG_CLIENT_SESSION 22
+#define CEPH_MSG_CLIENT_RECONNECT 23
+
+#define CEPH_MSG_CLIENT_REQUEST 24
+#define CEPH_MSG_CLIENT_REQUEST_FORWARD 25
+#define CEPH_MSG_CLIENT_REPLY 26
+#define CEPH_MSG_CLIENT_RECLAIM 27
+#define CEPH_MSG_CLIENT_RECLAIM_REPLY 28
+#define CEPH_MSG_CLIENT_METRICS 29
+#define CEPH_MSG_CLIENT_CAPS 0x310
+#define CEPH_MSG_CLIENT_LEASE 0x311
+#define CEPH_MSG_CLIENT_SNAP 0x312
+#define CEPH_MSG_CLIENT_CAPRELEASE 0x313
+#define CEPH_MSG_CLIENT_QUOTA 0x314
+
+/* pool ops */
+#define CEPH_MSG_POOLOP_REPLY 48
+#define CEPH_MSG_POOLOP 49
+
+
+/* osd */
+#define CEPH_MSG_OSD_MAP 41
+#define CEPH_MSG_OSD_OP 42
+#define CEPH_MSG_OSD_OPREPLY 43
+#define CEPH_MSG_WATCH_NOTIFY 44
+#define CEPH_MSG_OSD_BACKOFF 61
+
+/* FSMap subscribers (see all MDS clusters at once) */
+#define CEPH_MSG_FS_MAP 45
+/* FSMapUser subscribers (get MDS clusters name->ID mapping) */
+#define CEPH_MSG_FS_MAP_USER 103
+
+/* watch-notify operations */
+enum {
+ CEPH_WATCH_EVENT_NOTIFY = 1, /* notifying watcher */
+ CEPH_WATCH_EVENT_NOTIFY_COMPLETE = 2, /* notifier notified when done */
+ CEPH_WATCH_EVENT_DISCONNECT = 3, /* we were disconnected */
+};
+
+const char *ceph_watch_event_name(int o);
+
+/* pool operations */
+enum {
+ POOL_OP_CREATE = 0x01,
+ POOL_OP_DELETE = 0x02,
+ POOL_OP_AUID_CHANGE = 0x03,
+ POOL_OP_CREATE_SNAP = 0x11,
+ POOL_OP_DELETE_SNAP = 0x12,
+ POOL_OP_CREATE_UNMANAGED_SNAP = 0x21,
+ POOL_OP_DELETE_UNMANAGED_SNAP = 0x22,
+};
+
+struct ceph_mon_request_header {
+ __le64 have_version;
+ __le16 session_mon;
+ __le64 session_mon_tid;
+} __attribute__ ((packed));
+
+struct ceph_mon_statfs {
+ struct ceph_mon_request_header monhdr;
+ struct ceph_fsid fsid;
+} __attribute__ ((packed));
+
+struct ceph_statfs {
+ __le64 kb, kb_used, kb_avail;
+ __le64 num_objects;
+} __attribute__ ((packed));
+
+struct ceph_mon_statfs_reply {
+ struct ceph_fsid fsid;
+ __le64 version;
+ struct ceph_statfs st;
+} __attribute__ ((packed));
+
+const char *ceph_pool_op_name(int op);
+
+struct ceph_mon_poolop {
+ struct ceph_mon_request_header monhdr;
+ struct ceph_fsid fsid;
+ __le32 pool;
+ __le32 op;
+ __le64 __old_auid; // obsolete
+ __le64 snapid;
+ __le32 name_len;
+} __attribute__ ((packed));
+
+struct ceph_mon_poolop_reply {
+ struct ceph_mon_request_header monhdr;
+ struct ceph_fsid fsid;
+ __le32 reply_code;
+ __le32 epoch;
+ char has_data;
+ char data[0];
+} __attribute__ ((packed));
+
+struct ceph_mon_unmanaged_snap {
+ __le64 snapid;
+} __attribute__ ((packed));
+
+struct ceph_osd_getmap {
+ struct ceph_mon_request_header monhdr;
+ struct ceph_fsid fsid;
+ __le32 start;
+} __attribute__ ((packed));
+
+struct ceph_mds_getmap {
+ struct ceph_mon_request_header monhdr;
+ struct ceph_fsid fsid;
+} __attribute__ ((packed));
+
+struct ceph_client_mount {
+ struct ceph_mon_request_header monhdr;
+} __attribute__ ((packed));
+
+#define CEPH_SUBSCRIBE_ONETIME 1 /* i want only 1 update after have */
+
+struct ceph_mon_subscribe_item {
+ __le64 start;
+ __u8 flags;
+} __attribute__ ((packed));
+
+struct ceph_mon_subscribe_ack {
+ __le32 duration; /* seconds */
+ struct ceph_fsid fsid;
+} __attribute__ ((packed));
+
+/*
+ * mdsmap flags
+ */
+#define CEPH_MDSMAP_NOT_JOINABLE (1<<0) /* standbys cannot join */
+#define CEPH_MDSMAP_DOWN (CEPH_MDSMAP_NOT_JOINABLE) /* backwards compat */
+#define CEPH_MDSMAP_ALLOW_SNAPS (1<<1) /* cluster allowed to create snapshots */
+/* deprecated #define CEPH_MDSMAP_ALLOW_MULTIMDS (1<<2) cluster allowed to have >1 active MDS */
+/* deprecated #define CEPH_MDSMAP_ALLOW_DIRFRAGS (1<<3) cluster allowed to fragment directories */
+#define CEPH_MDSMAP_ALLOW_MULTIMDS_SNAPS (1<<4) /* cluster alllowed to enable MULTIMDS
+ and SNAPS at the same time */
+#define CEPH_MDSMAP_ALLOW_STANDBY_REPLAY (1<<5) /* cluster alllowed to enable MULTIMDS */
+#define CEPH_MDSMAP_REFUSE_CLIENT_SESSION (1<<6) /* cluster allowed to refuse client session
+ request */
+#define CEPH_MDSMAP_DEFAULTS (CEPH_MDSMAP_ALLOW_SNAPS | \
+ CEPH_MDSMAP_ALLOW_MULTIMDS_SNAPS)
+
+/*
+ * mds states
+ * > 0 -> in
+ * <= 0 -> out
+ */
+#define CEPH_MDS_STATE_DNE 0 /* down, does not exist. */
+#define CEPH_MDS_STATE_STOPPED -1 /* down, once existed, but no subtrees.
+ empty log. */
+#define CEPH_MDS_STATE_BOOT -4 /* up, boot announcement. */
+#define CEPH_MDS_STATE_STANDBY -5 /* up, idle. waiting for assignment. */
+#define CEPH_MDS_STATE_CREATING -6 /* up, creating MDS instance. */
+#define CEPH_MDS_STATE_STARTING -7 /* up, starting previously stopped mds */
+#define CEPH_MDS_STATE_STANDBY_REPLAY -8 /* up, tailing active node's journal */
+#define CEPH_MDS_STATE_REPLAYONCE -9 /* Legacy, unused */
+#define CEPH_MDS_STATE_NULL -10
+
+#define CEPH_MDS_STATE_REPLAY 8 /* up, replaying journal. */
+#define CEPH_MDS_STATE_RESOLVE 9 /* up, disambiguating distributed
+ operations (import, rename, etc.) */
+#define CEPH_MDS_STATE_RECONNECT 10 /* up, reconnect to clients */
+#define CEPH_MDS_STATE_REJOIN 11 /* up, rejoining distributed cache */
+#define CEPH_MDS_STATE_CLIENTREPLAY 12 /* up, replaying client operations */
+#define CEPH_MDS_STATE_ACTIVE 13 /* up, active */
+#define CEPH_MDS_STATE_STOPPING 14 /* up, but exporting metadata */
+#define CEPH_MDS_STATE_DAMAGED 15 /* rank not replayable, need repair */
+
+extern const char *ceph_mds_state_name(int s);
+
+
+/*
+ * metadata lock types.
+ * - these are bitmasks.. we can compose them
+ * - they also define the lock ordering by the MDS
+ * - a few of these are internal to the mds
+ */
+#define CEPH_LOCK_DN (1 << 0)
+#define CEPH_LOCK_DVERSION (1 << 1)
+#define CEPH_LOCK_ISNAP (1 << 4) /* snapshot lock. MDS internal */
+#define CEPH_LOCK_IPOLICY (1 << 5) /* policy lock on dirs. MDS internal */
+#define CEPH_LOCK_IFILE (1 << 6)
+#define CEPH_LOCK_INEST (1 << 7) /* mds internal */
+#define CEPH_LOCK_IDFT (1 << 8) /* dir frag tree */
+#define CEPH_LOCK_IAUTH (1 << 9)
+#define CEPH_LOCK_ILINK (1 << 10)
+#define CEPH_LOCK_IXATTR (1 << 11)
+#define CEPH_LOCK_IFLOCK (1 << 12) /* advisory file locks */
+#define CEPH_LOCK_IVERSION (1 << 13) /* mds internal */
+
+#define CEPH_LOCK_IFIRST CEPH_LOCK_ISNAP
+
+
+/* client_session ops */
+enum {
+ CEPH_SESSION_REQUEST_OPEN,
+ CEPH_SESSION_OPEN,
+ CEPH_SESSION_REQUEST_CLOSE,
+ CEPH_SESSION_CLOSE,
+ CEPH_SESSION_REQUEST_RENEWCAPS,
+ CEPH_SESSION_RENEWCAPS,
+ CEPH_SESSION_STALE,
+ CEPH_SESSION_RECALL_STATE,
+ CEPH_SESSION_FLUSHMSG,
+ CEPH_SESSION_FLUSHMSG_ACK,
+ CEPH_SESSION_FORCE_RO,
+ // A response to REQUEST_OPEN indicating that the client should
+ // permanently desist from contacting the MDS
+ CEPH_SESSION_REJECT,
+ CEPH_SESSION_REQUEST_FLUSH_MDLOG
+};
+
+// flags for state reclaim
+#define CEPH_RECLAIM_RESET 1
+
+extern const char *ceph_session_op_name(int op);
+
+struct ceph_mds_session_head {
+ __le32 op;
+ __le64 seq;
+ struct ceph_timespec stamp;
+ __le32 max_caps, max_leases;
+} __attribute__ ((packed));
+
+/* client_request */
+/*
+ * metadata ops.
+ * & 0x001000 -> write op
+ * & 0x010000 -> follow symlink (e.g. stat(), not lstat()).
+ & & 0x100000 -> use weird ino/path trace
+ */
+#define CEPH_MDS_OP_WRITE 0x001000
+enum {
+ CEPH_MDS_OP_LOOKUP = 0x00100,
+ CEPH_MDS_OP_GETATTR = 0x00101,
+ CEPH_MDS_OP_LOOKUPHASH = 0x00102,
+ CEPH_MDS_OP_LOOKUPPARENT = 0x00103,
+ CEPH_MDS_OP_LOOKUPINO = 0x00104,
+ CEPH_MDS_OP_LOOKUPNAME = 0x00105,
+ CEPH_MDS_OP_GETVXATTR = 0x00106,
+ CEPH_MDS_OP_DUMMY = 0x00107,
+
+ CEPH_MDS_OP_SETXATTR = 0x01105,
+ CEPH_MDS_OP_RMXATTR = 0x01106,
+ CEPH_MDS_OP_SETLAYOUT = 0x01107,
+ CEPH_MDS_OP_SETATTR = 0x01108,
+ CEPH_MDS_OP_SETFILELOCK= 0x01109,
+ CEPH_MDS_OP_GETFILELOCK= 0x00110,
+ CEPH_MDS_OP_SETDIRLAYOUT=0x0110a,
+
+ CEPH_MDS_OP_MKNOD = 0x01201,
+ CEPH_MDS_OP_LINK = 0x01202,
+ CEPH_MDS_OP_UNLINK = 0x01203,
+ CEPH_MDS_OP_RENAME = 0x01204,
+ CEPH_MDS_OP_MKDIR = 0x01220,
+ CEPH_MDS_OP_RMDIR = 0x01221,
+ CEPH_MDS_OP_SYMLINK = 0x01222,
+
+ CEPH_MDS_OP_CREATE = 0x01301,
+ CEPH_MDS_OP_OPEN = 0x00302,
+ CEPH_MDS_OP_READDIR = 0x00305,
+
+ CEPH_MDS_OP_LOOKUPSNAP = 0x00400,
+ CEPH_MDS_OP_MKSNAP = 0x01400,
+ CEPH_MDS_OP_RMSNAP = 0x01401,
+ CEPH_MDS_OP_LSSNAP = 0x00402,
+ CEPH_MDS_OP_RENAMESNAP = 0x01403,
+ CEPH_MDS_OP_READDIR_SNAPDIFF = 0x01404,
+
+ // internal op
+ CEPH_MDS_OP_FRAGMENTDIR= 0x01500,
+ CEPH_MDS_OP_EXPORTDIR = 0x01501,
+ CEPH_MDS_OP_FLUSH = 0x01502,
+ CEPH_MDS_OP_ENQUEUE_SCRUB = 0x01503,
+ CEPH_MDS_OP_REPAIR_FRAGSTATS = 0x01504,
+ CEPH_MDS_OP_REPAIR_INODESTATS = 0x01505,
+ CEPH_MDS_OP_RDLOCK_FRAGSSTATS = 0x01507
+};
+
+#define IS_CEPH_MDS_OP_NEWINODE(op) (op == CEPH_MDS_OP_CREATE || \
+ op == CEPH_MDS_OP_MKNOD || \
+ op == CEPH_MDS_OP_MKDIR || \
+ op == CEPH_MDS_OP_SYMLINK)
+
+extern const char *ceph_mds_op_name(int op);
+
+// setattr mask is an int
+#ifndef CEPH_SETATTR_MODE
+#define CEPH_SETATTR_MODE (1 << 0)
+#define CEPH_SETATTR_UID (1 << 1)
+#define CEPH_SETATTR_GID (1 << 2)
+#define CEPH_SETATTR_MTIME (1 << 3)
+#define CEPH_SETATTR_ATIME (1 << 4)
+#define CEPH_SETATTR_SIZE (1 << 5)
+#define CEPH_SETATTR_CTIME (1 << 6)
+#define CEPH_SETATTR_MTIME_NOW (1 << 7)
+#define CEPH_SETATTR_ATIME_NOW (1 << 8)
+#define CEPH_SETATTR_BTIME (1 << 9)
+#define CEPH_SETATTR_KILL_SGUID (1 << 10)
+#define CEPH_SETATTR_FSCRYPT_AUTH (1 << 11)
+#define CEPH_SETATTR_FSCRYPT_FILE (1 << 12)
+#define CEPH_SETATTR_KILL_SUID (1 << 13)
+#define CEPH_SETATTR_KILL_SGID (1 << 14)
+#endif
+
+/*
+ * open request flags
+ */
+#define CEPH_O_RDONLY 00000000
+#define CEPH_O_WRONLY 00000001
+#define CEPH_O_RDWR 00000002
+#define CEPH_O_CREAT 00000100
+#define CEPH_O_EXCL 00000200
+#define CEPH_O_TRUNC 00001000
+#define CEPH_O_LAZY 00020000
+#define CEPH_O_DIRECTORY 00200000
+#define CEPH_O_NOFOLLOW 00400000
+
+int ceph_flags_sys2wire(int flags);
+
+/*
+ * Ceph setxattr request flags.
+ */
+#define CEPH_XATTR_CREATE (1 << 0)
+#define CEPH_XATTR_REPLACE (1 << 1)
+#define CEPH_XATTR_REMOVE (1 << 31)
+
+/*
+ * readdir/readdir_snapdiff request flags;
+ */
+#define CEPH_READDIR_REPLY_BITFLAGS (1<<0)
+
+/*
+ * readdir/readdir_snapdiff reply flags.
+ */
+#define CEPH_READDIR_FRAG_END (1<<0)
+#define CEPH_READDIR_FRAG_COMPLETE (1<<8)
+#define CEPH_READDIR_HASH_ORDER (1<<9)
+#define CEPH_READDIR_OFFSET_HASH (1<<10)
+
+/* Note that this is embedded wthin ceph_mds_request_head_legacy. */
+union ceph_mds_request_args_legacy {
+ struct {
+ __le32 mask; /* CEPH_CAP_* */
+ } __attribute__ ((packed)) getattr;
+ struct {
+ __le32 mode;
+ __le32 uid;
+ __le32 gid;
+ struct ceph_timespec mtime;
+ struct ceph_timespec atime;
+ __le64 size, old_size; /* old_size needed by truncate */
+ __le32 mask; /* CEPH_SETATTR_* */
+ } __attribute__ ((packed)) setattr;
+ struct {
+ __le32 frag; /* which dir fragment */
+ __le32 max_entries; /* how many dentries to grab */
+ __le32 max_bytes;
+ __le16 flags;
+ __le32 offset_hash;
+ } __attribute__ ((packed)) readdir;
+ struct {
+ __le32 mode;
+ __le32 rdev;
+ } __attribute__ ((packed)) mknod;
+ struct {
+ __le32 mode;
+ } __attribute__ ((packed)) mkdir;
+ struct {
+ __le32 flags;
+ __le32 mode;
+ __le32 stripe_unit; /* layout for newly created file */
+ __le32 stripe_count; /* ... */
+ __le32 object_size;
+ __le32 pool; /* if >= 0 and CREATEPOOLID feature */
+ __le32 mask; /* CEPH_CAP_* */
+ __le64 old_size; /* if O_TRUNC */
+ } __attribute__ ((packed)) open;
+ struct {
+ __le32 flags;
+ __le32 osdmap_epoch; /* use for set file/dir layout */
+ } __attribute__ ((packed)) setxattr;
+ struct {
+ struct ceph_file_layout layout;
+ } __attribute__ ((packed)) setlayout;
+ struct {
+ __u8 rule; /* currently fcntl or flock */
+ __u8 type; /* shared, exclusive, remove*/
+ __le64 owner; /* who requests/holds the lock */
+ __le64 pid; /* process id requesting the lock */
+ __le64 start; /* initial location to lock */
+ __le64 length; /* num bytes to lock from start */
+ __u8 wait; /* will caller wait for lock to become available? */
+ } __attribute__ ((packed)) filelock_change;
+} __attribute__ ((packed));
+
+#define CEPH_MDS_FLAG_REPLAY 1 /* this is a replayed op */
+#define CEPH_MDS_FLAG_WANT_DENTRY 2 /* want dentry in reply */
+#define CEPH_MDS_FLAG_ASYNC 4 /* request is async */
+
+struct ceph_mds_request_head_legacy {
+ __le64 oldest_client_tid;
+ __le32 mdsmap_epoch; /* on client */
+ __le32 flags; /* CEPH_MDS_FLAG_* */
+ __u8 num_retry, num_fwd; /* count retry, fwd attempts */
+ __le16 num_releases; /* # include cap/lease release records */
+ __le32 op; /* mds op code */
+ __le32 caller_uid, caller_gid;
+ __le64 ino; /* use this ino for openc, mkdir, mknod,
+ etc. (if replaying) */
+ union ceph_mds_request_args_legacy args;
+} __attribute__ ((packed));
+
+/*
+ * Note that this is embedded wthin ceph_mds_request_head. Also, compatibility
+ * with the ceph_mds_request_args_legacy must be maintained!
+ */
+union ceph_mds_request_args {
+ struct {
+ __le32 mask; /* CEPH_CAP_* */
+ } __attribute__ ((packed)) getattr;
+ struct {
+ __le32 mode;
+ __le32 uid;
+ __le32 gid;
+ struct ceph_timespec mtime;
+ struct ceph_timespec atime;
+ __le64 size, old_size; /* old_size needed by truncate */
+ __le32 mask; /* CEPH_SETATTR_* */
+ struct ceph_timespec btime;
+ } __attribute__ ((packed)) setattr;
+ struct {
+ __le32 frag; /* which dir fragment */
+ __le32 max_entries; /* how many dentries to grab */
+ __le32 max_bytes;
+ __le16 flags;
+ __le32 offset_hash;
+ } __attribute__ ((packed)) readdir;
+ struct {
+ __le32 mode;
+ __le32 rdev;
+ } __attribute__ ((packed)) mknod;
+ struct {
+ __le32 mode;
+ } __attribute__ ((packed)) mkdir;
+ struct {
+ __le32 flags;
+ __le32 mode;
+ __le32 stripe_unit; /* layout for newly created file */
+ __le32 stripe_count; /* ... */
+ __le32 object_size;
+ __le32 pool; /* if >= 0 and CREATEPOOLID feature */
+ __le32 mask; /* CEPH_CAP_* */
+ __le64 old_size; /* if O_TRUNC */
+ } __attribute__ ((packed)) open;
+ struct {
+ __le32 flags;
+ __le32 osdmap_epoch; /* use for set file/dir layout */
+ } __attribute__ ((packed)) setxattr;
+ struct {
+ struct ceph_file_layout layout;
+ } __attribute__ ((packed)) setlayout;
+ struct {
+ __u8 rule; /* currently fcntl or flock */
+ __u8 type; /* shared, exclusive, remove*/
+ __le64 owner; /* who requests/holds the lock */
+ __le64 pid; /* process id requesting the lock */
+ __le64 start; /* initial location to lock */
+ __le64 length; /* num bytes to lock from start */
+ __u8 wait; /* will caller wait for lock to become available? */
+ } __attribute__ ((packed)) filelock_change;
+ struct {
+ __le32 mask; /* CEPH_CAP_* */
+ __le64 snapid;
+ __le64 parent;
+ __le32 hash;
+ } __attribute__ ((packed)) lookupino;
+ struct {
+ __le32 frag; /* which dir fragment */
+ __le32 max_entries; /* how many dentries to grab */
+ __le32 max_bytes;
+ __le16 flags;
+ __le32 offset_hash;
+ __le64 snap_other;
+ } __attribute__ ((packed)) snapdiff;
+} __attribute__ ((packed));
+
+#define CEPH_MDS_REQUEST_HEAD_VERSION 3
+
+/*
+ * Note that any change to this structure must ensure that it is compatible
+ * with ceph_mds_request_head_legacy.
+ */
+struct ceph_mds_request_head {
+ __le16 version;
+ __le64 oldest_client_tid;
+ __le32 mdsmap_epoch; /* on client */
+ __le32 flags; /* CEPH_MDS_FLAG_* */
+ __u8 num_retry, num_fwd; /* legacy count retry and fwd attempts */
+ __le16 num_releases; /* # include cap/lease release records */
+ __le32 op; /* mds op code */
+ __le32 caller_uid, caller_gid;
+ __le64 ino; /* use this ino for openc, mkdir, mknod,
+ etc. (if replaying) */
+ union ceph_mds_request_args args;
+
+ __le32 ext_num_retry; /* new count retry attempts */
+ __le32 ext_num_fwd; /* new count fwd attempts */
+
+ __le32 struct_len; /* to store size of struct ceph_mds_request_head */
+ __le32 owner_uid, owner_gid; /* used for OPs which create inodes */
+} __attribute__ ((packed));
+
+void inline encode(const struct ceph_mds_request_head& h, ceph::buffer::list& bl) {
+ using ceph::encode;
+ encode(h.version, bl);
+ encode(h.oldest_client_tid, bl);
+ encode(h.mdsmap_epoch, bl);
+ encode(h.flags, bl);
+
+ // For old MDS daemons
+ __u8 num_retry = __u32(h.ext_num_retry);
+ __u8 num_fwd = __u32(h.ext_num_fwd);
+ encode(num_retry, bl);
+ encode(num_fwd, bl);
+
+ encode(h.num_releases, bl);
+ encode(h.op, bl);
+ encode(h.caller_uid, bl);
+ encode(h.caller_gid, bl);
+ encode(h.ino, bl);
+ bl.append((char*)&h.args, sizeof(h.args));
+
+ if (h.version >= 2) {
+ encode(h.ext_num_retry, bl);
+ encode(h.ext_num_fwd, bl);
+ }
+
+ if (h.version >= 3) {
+ __u32 struct_len = sizeof(struct ceph_mds_request_head);
+ encode(struct_len, bl);
+ encode(h.owner_uid, bl);
+ encode(h.owner_gid, bl);
+
+ /*
+ * Please, add new fields handling here.
+ * You don't need to check h.version as we do it
+ * in decode(), because decode can properly skip
+ * all unsupported fields if h.version >= 3.
+ */
+ }
+}
+
+void inline decode(struct ceph_mds_request_head& h, ceph::buffer::list::const_iterator& bl) {
+ using ceph::decode;
+ unsigned struct_end = bl.get_off();
+
+ decode(h.version, bl);
+ decode(h.oldest_client_tid, bl);
+ decode(h.mdsmap_epoch, bl);
+ decode(h.flags, bl);
+ decode(h.num_retry, bl);
+ decode(h.num_fwd, bl);
+ decode(h.num_releases, bl);
+ decode(h.op, bl);
+ decode(h.caller_uid, bl);
+ decode(h.caller_gid, bl);
+ decode(h.ino, bl);
+ bl.copy(sizeof(h.args), (char*)&(h.args));
+
+ if (h.version >= 2) {
+ decode(h.ext_num_retry, bl);
+ decode(h.ext_num_fwd, bl);
+ } else {
+ h.ext_num_retry = h.num_retry;
+ h.ext_num_fwd = h.num_fwd;
+ }
+
+ if (h.version >= 3) {
+ decode(h.struct_len, bl);
+ struct_end += h.struct_len;
+
+ decode(h.owner_uid, bl);
+ decode(h.owner_gid, bl);
+ } else {
+ /*
+ * client is old: let's take caller_{u,g}id as owner_{u,g}id
+ * this is how it worked before adding of owner_{u,g}id fields.
+ */
+ h.owner_uid = h.caller_uid;
+ h.owner_gid = h.caller_gid;
+ }
+
+ /* add new fields handling here */
+
+ /*
+ * From version 3 we have struct_len field.
+ * It allows us to properly handle a case
+ * when client send struct ceph_mds_request_head
+ * bigger in size than MDS supports. In this
+ * case we just want to skip all remaining bytes
+ * at the end.
+ *
+ * See also DECODE_FINISH macro. Unfortunately,
+ * we can't start using it right now as it will be
+ * an incompatible protocol change.
+ */
+ if (h.version >= 3) {
+ if (bl.get_off() > struct_end)
+ throw ::ceph::buffer::malformed_input(DECODE_ERR_PAST(__PRETTY_FUNCTION__));
+ if (bl.get_off() < struct_end)
+ bl += struct_end - bl.get_off();
+ }
+}
+
+/* cap/lease release record */
+struct ceph_mds_request_release {
+ __le64 ino, cap_id; /* ino and unique cap id */
+ __le32 caps, wanted; /* new issued, wanted */
+ __le32 seq, issue_seq, mseq;
+ __le32 dname_seq; /* if releasing a dentry lease, a */
+ __le32 dname_len; /* string follows. */
+} __attribute__ ((packed));
+
+static inline void
+copy_from_legacy_head(struct ceph_mds_request_head *head,
+ struct ceph_mds_request_head_legacy *legacy)
+{
+ struct ceph_mds_request_head_legacy *embedded_legacy =
+ (struct ceph_mds_request_head_legacy *)&head->oldest_client_tid;
+ *embedded_legacy = *legacy;
+}
+
+static inline void
+copy_to_legacy_head(struct ceph_mds_request_head_legacy *legacy,
+ struct ceph_mds_request_head *head)
+{
+ struct ceph_mds_request_head_legacy *embedded_legacy =
+ (struct ceph_mds_request_head_legacy *)&head->oldest_client_tid;
+ *legacy = *embedded_legacy;
+}
+
+/* client reply */
+struct ceph_mds_reply_head {
+ __le32 op;
+ __le32 result;
+ __le32 mdsmap_epoch;
+ __u8 safe; /* true if committed to disk */
+ __u8 is_dentry, is_target; /* true if dentry, target inode records
+ are included with reply */
+} __attribute__ ((packed));
+
+/* one for each node split */
+struct ceph_frag_tree_split {
+ __le32 frag; /* this frag splits... */
+ __le32 by; /* ...by this many bits */
+} __attribute__ ((packed));
+
+struct ceph_frag_tree_head {
+ __le32 nsplits; /* num ceph_frag_tree_split records */
+ struct ceph_frag_tree_split splits[];
+} __attribute__ ((packed));
+
+/* capability issue, for bundling with mds reply */
+struct ceph_mds_reply_cap {
+ __le32 caps, wanted; /* caps issued, wanted */
+ __le64 cap_id;
+ __le32 seq, mseq;
+ __le64 realm; /* snap realm */
+ __u8 flags; /* CEPH_CAP_FLAG_* */
+} __attribute__ ((packed));
+
+#define CEPH_CAP_FLAG_AUTH (1 << 0) /* cap is issued by auth mds */
+#define CEPH_CAP_FLAG_RELEASE (1 << 1) /* ask client to release the cap */
+
+/* reply_lease follows dname, and reply_inode */
+struct ceph_mds_reply_lease {
+ __le16 mask; /* lease type(s) */
+ __le32 duration_ms; /* lease duration */
+ __le32 seq;
+} __attribute__ ((packed));
+
+#define CEPH_LEASE_VALID (1 | 2) /* old and new bit values */
+#define CEPH_LEASE_PRIMARY_LINK 4 /* primary linkage */
+
+struct ceph_mds_reply_dirfrag {
+ __le32 frag; /* fragment */
+ __le32 auth; /* auth mds, if this is a delegation point */
+ __le32 ndist; /* number of mds' this is replicated on */
+ __le32 dist[];
+} __attribute__ ((packed));
+
+#define CEPH_LOCK_FCNTL 1
+#define CEPH_LOCK_FLOCK 2
+#define CEPH_LOCK_FCNTL_INTR 3
+#define CEPH_LOCK_FLOCK_INTR 4
+
+#define CEPH_LOCK_SHARED 1
+#define CEPH_LOCK_EXCL 2
+#define CEPH_LOCK_UNLOCK 4
+
+struct ceph_filelock {
+ __le64 start;/* file offset to start lock at */
+ __le64 length; /* num bytes to lock; 0 for all following start */
+ __le64 client; /* which client holds the lock */
+ __le64 owner; /* who requests/holds the lock */
+ __le64 pid; /* process id holding the lock on the client */
+ __u8 type; /* shared lock, exclusive lock, or unlock */
+} __attribute__ ((packed));
+
+
+/* file access modes */
+#define CEPH_FILE_MODE_PIN 0
+#define CEPH_FILE_MODE_RD 1
+#define CEPH_FILE_MODE_WR 2
+#define CEPH_FILE_MODE_RDWR 3 /* RD | WR */
+#define CEPH_FILE_MODE_LAZY 4 /* lazy io */
+#define CEPH_FILE_MODE_NUM 8 /* bc these are bit fields.. mostly */
+
+int ceph_flags_to_mode(int flags);
+
+/* inline data state */
+#define CEPH_INLINE_NONE ((__u64)-1)
+#define CEPH_INLINE_MAX_SIZE CEPH_MIN_STRIPE_UNIT
+
+/* capability bits */
+#define CEPH_CAP_PIN 1 /* no specific capabilities beyond the pin */
+
+/* generic cap bits */
+/* note: these definitions are duplicated in mds/locks.c */
+#define CEPH_CAP_GSHARED 1 /* client can reads */
+#define CEPH_CAP_GEXCL 2 /* client can read and update */
+#define CEPH_CAP_GCACHE 4 /* (file) client can cache reads */
+#define CEPH_CAP_GRD 8 /* (file) client can read */
+#define CEPH_CAP_GWR 16 /* (file) client can write */
+#define CEPH_CAP_GBUFFER 32 /* (file) client can buffer writes */
+#define CEPH_CAP_GWREXTEND 64 /* (file) client can extend EOF */
+#define CEPH_CAP_GLAZYIO 128 /* (file) client can perform lazy io */
+
+#define CEPH_CAP_SIMPLE_BITS 2
+#define CEPH_CAP_FILE_BITS 8
+
+/* per-lock shift */
+#define CEPH_CAP_SAUTH 2
+#define CEPH_CAP_SLINK 4
+#define CEPH_CAP_SXATTR 6
+#define CEPH_CAP_SFILE 8
+
+/* composed values */
+#define CEPH_CAP_AUTH_SHARED (CEPH_CAP_GSHARED << CEPH_CAP_SAUTH)
+#define CEPH_CAP_AUTH_EXCL (CEPH_CAP_GEXCL << CEPH_CAP_SAUTH)
+#define CEPH_CAP_LINK_SHARED (CEPH_CAP_GSHARED << CEPH_CAP_SLINK)
+#define CEPH_CAP_LINK_EXCL (CEPH_CAP_GEXCL << CEPH_CAP_SLINK)
+#define CEPH_CAP_XATTR_SHARED (CEPH_CAP_GSHARED << CEPH_CAP_SXATTR)
+#define CEPH_CAP_XATTR_EXCL (CEPH_CAP_GEXCL << CEPH_CAP_SXATTR)
+#define CEPH_CAP_FILE(x) ((x) << CEPH_CAP_SFILE)
+#define CEPH_CAP_FILE_SHARED (CEPH_CAP_GSHARED << CEPH_CAP_SFILE)
+#define CEPH_CAP_FILE_EXCL (CEPH_CAP_GEXCL << CEPH_CAP_SFILE)
+#define CEPH_CAP_FILE_CACHE (CEPH_CAP_GCACHE << CEPH_CAP_SFILE)
+#define CEPH_CAP_FILE_RD (CEPH_CAP_GRD << CEPH_CAP_SFILE)
+#define CEPH_CAP_FILE_WR (CEPH_CAP_GWR << CEPH_CAP_SFILE)
+#define CEPH_CAP_FILE_BUFFER (CEPH_CAP_GBUFFER << CEPH_CAP_SFILE)
+#define CEPH_CAP_FILE_WREXTEND (CEPH_CAP_GWREXTEND << CEPH_CAP_SFILE)
+#define CEPH_CAP_FILE_LAZYIO (CEPH_CAP_GLAZYIO << CEPH_CAP_SFILE)
+
+/* cap masks (for getattr) */
+#define CEPH_STAT_CAP_INODE CEPH_CAP_PIN
+#define CEPH_STAT_CAP_TYPE CEPH_CAP_PIN /* mode >> 12 */
+#define CEPH_STAT_CAP_SYMLINK CEPH_CAP_PIN
+#define CEPH_STAT_CAP_UID CEPH_CAP_AUTH_SHARED
+#define CEPH_STAT_CAP_GID CEPH_CAP_AUTH_SHARED
+#define CEPH_STAT_CAP_MODE CEPH_CAP_AUTH_SHARED
+#define CEPH_STAT_CAP_NLINK CEPH_CAP_LINK_SHARED
+#define CEPH_STAT_CAP_LAYOUT CEPH_CAP_FILE_SHARED
+#define CEPH_STAT_CAP_MTIME CEPH_CAP_FILE_SHARED
+#define CEPH_STAT_CAP_SIZE CEPH_CAP_FILE_SHARED
+#define CEPH_STAT_CAP_ATIME CEPH_CAP_FILE_SHARED /* fixme */
+#define CEPH_STAT_CAP_XATTR CEPH_CAP_XATTR_SHARED
+#define CEPH_STAT_CAP_INODE_ALL (CEPH_CAP_PIN | \
+ CEPH_CAP_AUTH_SHARED | \
+ CEPH_CAP_LINK_SHARED | \
+ CEPH_CAP_FILE_SHARED | \
+ CEPH_CAP_XATTR_SHARED)
+#define CEPH_STAT_CAP_INLINE_DATA (CEPH_CAP_FILE_SHARED | \
+ CEPH_CAP_FILE_RD)
+#define CEPH_STAT_RSTAT CEPH_CAP_FILE_WREXTEND
+
+#define CEPH_CAP_ANY_SHARED (CEPH_CAP_AUTH_SHARED | \
+ CEPH_CAP_LINK_SHARED | \
+ CEPH_CAP_XATTR_SHARED | \
+ CEPH_CAP_FILE_SHARED)
+#define CEPH_CAP_ANY_RD (CEPH_CAP_ANY_SHARED | CEPH_CAP_FILE_RD | \
+ CEPH_CAP_FILE_CACHE)
+
+#define CEPH_CAP_ANY_EXCL (CEPH_CAP_AUTH_EXCL | \
+ CEPH_CAP_LINK_EXCL | \
+ CEPH_CAP_XATTR_EXCL | \
+ CEPH_CAP_FILE_EXCL)
+#define CEPH_CAP_ANY_FILE_RD (CEPH_CAP_FILE_RD | CEPH_CAP_FILE_CACHE | \
+ CEPH_CAP_FILE_SHARED)
+#define CEPH_CAP_ANY_FILE_WR (CEPH_CAP_FILE_WR | CEPH_CAP_FILE_BUFFER | \
+ CEPH_CAP_FILE_EXCL)
+#define CEPH_CAP_ANY_WR (CEPH_CAP_ANY_EXCL | CEPH_CAP_ANY_FILE_WR)
+#define CEPH_CAP_ANY (CEPH_CAP_ANY_RD | CEPH_CAP_ANY_EXCL | \
+ CEPH_CAP_ANY_FILE_WR | CEPH_CAP_FILE_LAZYIO | \
+ CEPH_CAP_PIN)
+
+#define CEPH_CAP_LOCKS (CEPH_LOCK_IFILE | CEPH_LOCK_IAUTH | CEPH_LOCK_ILINK | \
+ CEPH_LOCK_IXATTR)
+
+/* cap masks async dir operations */
+#define CEPH_CAP_DIR_CREATE CEPH_CAP_FILE_CACHE
+#define CEPH_CAP_DIR_UNLINK CEPH_CAP_FILE_RD
+#define CEPH_CAP_ANY_DIR_OPS (CEPH_CAP_FILE_CACHE | CEPH_CAP_FILE_RD | \
+ CEPH_CAP_FILE_WREXTEND | CEPH_CAP_FILE_LAZYIO)
+
+
+int ceph_caps_for_mode(int mode);
+
+enum {
+ CEPH_CAP_OP_GRANT, /* mds->client grant */
+ CEPH_CAP_OP_REVOKE, /* mds->client revoke */
+ CEPH_CAP_OP_TRUNC, /* mds->client trunc notify */
+ CEPH_CAP_OP_EXPORT, /* mds has exported the cap */
+ CEPH_CAP_OP_IMPORT, /* mds has imported the cap */
+ CEPH_CAP_OP_UPDATE, /* client->mds update */
+ CEPH_CAP_OP_DROP, /* client->mds drop cap bits */
+ CEPH_CAP_OP_FLUSH, /* client->mds cap writeback */
+ CEPH_CAP_OP_FLUSH_ACK, /* mds->client flushed */
+ CEPH_CAP_OP_FLUSHSNAP, /* client->mds flush snapped metadata */
+ CEPH_CAP_OP_FLUSHSNAP_ACK, /* mds->client flushed snapped metadata */
+ CEPH_CAP_OP_RELEASE, /* client->mds release (clean) cap */
+ CEPH_CAP_OP_RENEW, /* client->mds renewal request */
+};
+
+extern const char *ceph_cap_op_name(int op);
+
+/* extra info for cap import/export */
+struct ceph_mds_cap_peer {
+ __le64 cap_id;
+ __le32 seq;
+ __le32 mseq;
+ __le32 mds;
+ __u8 flags;
+} __attribute__ ((packed));
+
+/*
+ * caps message, used for capability callbacks, acks, requests, etc.
+ */
+struct ceph_mds_caps_head {
+ __le32 op; /* CEPH_CAP_OP_* */
+ __le64 ino, realm;
+ __le64 cap_id;
+ __le32 seq, issue_seq;
+ __le32 caps, wanted, dirty; /* latest issued/wanted/dirty */
+ __le32 migrate_seq;
+ __le64 snap_follows;
+ __le32 snap_trace_len;
+
+ /* authlock */
+ __le32 uid, gid, mode;
+
+ /* linklock */
+ __le32 nlink;
+
+ /* xattrlock */
+ __le32 xattr_len;
+ __le64 xattr_version;
+} __attribute__ ((packed));
+
+struct ceph_mds_caps_non_export_body {
+ /* all except export */
+ /* filelock */
+ __le64 size, max_size, truncate_size;
+ __le32 truncate_seq;
+ struct ceph_timespec mtime, atime, ctime;
+ struct ceph_file_layout layout;
+ __le32 time_warp_seq;
+} __attribute__ ((packed));
+
+struct ceph_mds_caps_export_body {
+ /* export message */
+ struct ceph_mds_cap_peer peer;
+} __attribute__ ((packed));
+
+/* cap release msg head */
+struct ceph_mds_cap_release {
+ __le32 num; /* number of cap_items that follow */
+} __attribute__ ((packed));
+
+struct ceph_mds_cap_item {
+ __le64 ino;
+ __le64 cap_id;
+ __le32 migrate_seq, seq;
+} __attribute__ ((packed));
+
+#define CEPH_MDS_LEASE_REVOKE 1 /* mds -> client */
+#define CEPH_MDS_LEASE_RELEASE 2 /* client -> mds */
+#define CEPH_MDS_LEASE_RENEW 3 /* client <-> mds */
+#define CEPH_MDS_LEASE_REVOKE_ACK 4 /* client -> mds */
+
+extern const char *ceph_lease_op_name(int o);
+
+/* lease msg header */
+struct ceph_mds_lease {
+ __u8 action; /* CEPH_MDS_LEASE_* */
+ __le16 mask; /* which lease */
+ __le64 ino;
+ __le64 first, last; /* snap range */
+ __le32 seq;
+ __le32 duration_ms; /* duration of renewal */
+} __attribute__ ((packed));
+/* followed by a __le32+string for dname */
+
+/* client reconnect */
+struct ceph_mds_cap_reconnect {
+ __le64 cap_id;
+ __le32 wanted;
+ __le32 issued;
+ __le64 snaprealm;
+ __le64 pathbase; /* base ino for our path to this ino */
+ __le32 flock_len; /* size of flock state blob, if any */
+} __attribute__ ((packed));
+/* followed by flock blob */
+
+struct ceph_mds_cap_reconnect_v1 {
+ __le64 cap_id;
+ __le32 wanted;
+ __le32 issued;
+ __le64 size;
+ struct ceph_timespec mtime, atime;
+ __le64 snaprealm;
+ __le64 pathbase; /* base ino for our path to this ino */
+} __attribute__ ((packed));
+
+struct ceph_mds_snaprealm_reconnect {
+ __le64 ino; /* snap realm base */
+ __le64 seq; /* snap seq for this snap realm */
+ __le64 parent; /* parent realm */
+} __attribute__ ((packed));
+
+/*
+ * snaps
+ */
+enum {
+ CEPH_SNAP_OP_UPDATE, /* CREATE or DESTROY */
+ CEPH_SNAP_OP_CREATE,
+ CEPH_SNAP_OP_DESTROY,
+ CEPH_SNAP_OP_SPLIT,
+};
+
+extern const char *ceph_snap_op_name(int o);
+
+/* snap msg header */
+struct ceph_mds_snap_head {
+ __le32 op; /* CEPH_SNAP_OP_* */
+ __le64 split; /* ino to split off, if any */
+ __le32 num_split_inos; /* # inos belonging to new child realm */
+ __le32 num_split_realms; /* # child realms udner new child realm */
+ __le32 trace_len; /* size of snap trace blob */
+} __attribute__ ((packed));
+/* followed by split ino list, then split realms, then the trace blob */
+
+/*
+ * encode info about a snaprealm, as viewed by a client
+ */
+struct ceph_mds_snap_realm {
+ __le64 ino; /* ino */
+ __le64 created; /* snap: when created */
+ __le64 parent; /* ino: parent realm */
+ __le64 parent_since; /* snap: same parent since */
+ __le64 seq; /* snap: version */
+ __le32 num_snaps;
+ __le32 num_prior_parent_snaps;
+} __attribute__ ((packed));
+/* followed by my snap list, then prior parent snap list */
+
+#ifndef __KERNEL__
+#undef __le16
+#undef __le32
+#undef __le64
+#endif
+
+#endif
diff --git a/src/include/ceph_fuse.h b/src/include/ceph_fuse.h
new file mode 100644
index 000000000..cfa8097bb
--- /dev/null
+++ b/src/include/ceph_fuse.h
@@ -0,0 +1,51 @@
+// -*- 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) 2012 Inktank Storage, Inc.
+ * Copyright (C) 2014 Red Hat <contact@redhat.com>
+ *
+ * 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_FUSE_H
+#define CEPH_FUSE_H
+
+/*
+ * The API version that we want to use, regardless of what the
+ * library version is. Note that this must be defined before
+ * fuse.h is included.
+ */
+#ifndef FUSE_USE_VERSION
+#define FUSE_USE_VERSION 312
+#endif
+
+#include <fuse.h>
+#include "acconfig.h"
+
+/*
+ * Redefine the FUSE_VERSION macro defined in "fuse_common.h"
+ * header file, because the MINOR numner has been forgotten to
+ * update since libfuse 3.2 to 3.8. We need to fetch the MINOR
+ * number from pkgconfig file.
+ */
+#ifdef FUSE_VERSION
+#undef FUSE_VERSION
+#define FUSE_VERSION FUSE_MAKE_VERSION(CEPH_FUSE_MAJOR_VERSION, CEPH_FUSE_MINOR_VERSION)
+#endif
+
+static inline int filler_compat(fuse_fill_dir_t filler,
+ void *buf, const char *name,
+ const struct stat *stbuf,
+ off_t off)
+{
+ return filler(buf, name, stbuf, off
+#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
+ , static_cast<enum fuse_fill_dir_flags>(0)
+#endif
+ );
+}
+#endif /* CEPH_FUSE_H */
diff --git a/src/include/ceph_hash.h b/src/include/ceph_hash.h
new file mode 100644
index 000000000..f9d80ac36
--- /dev/null
+++ b/src/include/ceph_hash.h
@@ -0,0 +1,14 @@
+#ifndef FS_CEPH_HASH_H
+#define FS_CEPH_HASH_H
+
+#define CEPH_STR_HASH_LINUX 0x1 /* linux dcache hash */
+#define CEPH_STR_HASH_RJENKINS 0x2 /* robert jenkins' */
+
+extern unsigned ceph_str_hash_linux(const char *s, unsigned len);
+extern unsigned ceph_str_hash_rjenkins(const char *s, unsigned len);
+
+extern unsigned ceph_str_hash(int type, const char *s, unsigned len);
+extern const char *ceph_str_hash_name(int type);
+extern bool ceph_str_hash_valid(int type);
+
+#endif
diff --git a/src/include/cephfs/ceph_ll_client.h b/src/include/cephfs/ceph_ll_client.h
new file mode 100644
index 000000000..ac5b7c224
--- /dev/null
+++ b/src/include/cephfs/ceph_ll_client.h
@@ -0,0 +1,215 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * scalable distributed file system
+ *
+ * Copyright (C) Jeff Layton <jlayton@redhat.com>
+ *
+ * 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_CEPH_LL_CLIENT_H
+#define CEPH_CEPH_LL_CLIENT_H
+#include <stdint.h>
+
+#ifdef _WIN32
+#include "include/win32/fs_compat.h"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+
+class Fh;
+
+struct inodeno_t;
+struct vinodeno_t;
+typedef struct vinodeno_t vinodeno;
+
+#else /* __cplusplus */
+
+typedef struct Fh Fh;
+
+typedef struct inodeno_t {
+ uint64_t val;
+} inodeno_t;
+
+typedef struct _snapid_t {
+ uint64_t val;
+} snapid_t;
+
+typedef struct vinodeno_t {
+ inodeno_t ino;
+ snapid_t snapid;
+} vinodeno_t;
+
+#endif /* __cplusplus */
+
+/*
+ * Heavily borrowed from David Howells' draft statx patchset.
+ *
+ * Since the xstat patches are still a work in progress, we borrow its data
+ * structures and #defines to implement ceph_getattrx. Once the xstat stuff
+ * has been merged we should drop this and switch over to using that instead.
+ */
+struct ceph_statx {
+ uint32_t stx_mask;
+ uint32_t stx_blksize;
+ uint32_t stx_nlink;
+ uint32_t stx_uid;
+ uint32_t stx_gid;
+ uint16_t stx_mode;
+ uint64_t stx_ino;
+ uint64_t stx_size;
+ uint64_t stx_blocks;
+ dev_t stx_dev;
+ dev_t stx_rdev;
+ struct timespec stx_atime;
+ struct timespec stx_ctime;
+ struct timespec stx_mtime;
+ struct timespec stx_btime;
+ uint64_t stx_version;
+};
+
+#define CEPH_STATX_MODE 0x00000001U /* Want/got stx_mode */
+#define CEPH_STATX_NLINK 0x00000002U /* Want/got stx_nlink */
+#define CEPH_STATX_UID 0x00000004U /* Want/got stx_uid */
+#define CEPH_STATX_GID 0x00000008U /* Want/got stx_gid */
+#define CEPH_STATX_RDEV 0x00000010U /* Want/got stx_rdev */
+#define CEPH_STATX_ATIME 0x00000020U /* Want/got stx_atime */
+#define CEPH_STATX_MTIME 0x00000040U /* Want/got stx_mtime */
+#define CEPH_STATX_CTIME 0x00000080U /* Want/got stx_ctime */
+#define CEPH_STATX_INO 0x00000100U /* Want/got stx_ino */
+#define CEPH_STATX_SIZE 0x00000200U /* Want/got stx_size */
+#define CEPH_STATX_BLOCKS 0x00000400U /* Want/got stx_blocks */
+#define CEPH_STATX_BASIC_STATS 0x000007ffU /* The stuff in the normal stat struct */
+#define CEPH_STATX_BTIME 0x00000800U /* Want/got stx_btime */
+#define CEPH_STATX_VERSION 0x00001000U /* Want/got stx_version */
+#define CEPH_STATX_ALL_STATS 0x00001fffU /* All supported stats */
+
+/*
+ * Compatibility macros until these defines make their way into glibc
+ */
+#ifndef AT_STATX_DONT_SYNC
+#define AT_STATX_SYNC_TYPE 0x6000
+#define AT_STATX_SYNC_AS_STAT 0x0000
+#define AT_STATX_FORCE_SYNC 0x2000
+#define AT_STATX_DONT_SYNC 0x4000 /* Don't sync attributes with the server */
+#endif
+
+/*
+ * This is deprecated and just for backwards compatibility.
+ * Please use AT_STATX_DONT_SYNC instead.
+ */
+#define AT_NO_ATTR_SYNC AT_STATX_DONT_SYNC /* Deprecated */
+
+/*
+ * The statx interfaces only allow these flags. In order to allow us to add
+ * others in the future, we disallow setting any that aren't recognized.
+ */
+#define CEPH_REQ_FLAG_MASK (AT_SYMLINK_NOFOLLOW|AT_STATX_DONT_SYNC)
+
+/* fallocate mode flags */
+#ifndef FALLOC_FL_KEEP_SIZE
+#define FALLOC_FL_KEEP_SIZE 0x01
+#endif
+#ifndef FALLOC_FL_PUNCH_HOLE
+#define FALLOC_FL_PUNCH_HOLE 0x02
+#endif
+
+/** ceph_deleg_cb_t: Delegation recalls
+ *
+ * Called when there is an outstanding Delegation and there is conflicting
+ * access, either locally or via cap activity.
+ * @fh: open filehandle
+ * @priv: private info registered when delegation was acquired
+ */
+typedef void (*ceph_deleg_cb_t)(Fh *fh, void *priv);
+
+/**
+ * client_ino_callback_t: Inode data/metadata invalidation
+ *
+ * Called when the client wants to invalidate the cached data for a range
+ * in the file.
+ * @handle: client callback handle
+ * @ino: vino of inode to be invalidated
+ * @off: starting offset of content to be invalidated
+ * @len: length of region to invalidate
+ */
+typedef void (*client_ino_callback_t)(void *handle, vinodeno_t ino,
+ int64_t off, int64_t len);
+
+/**
+ * client_dentry_callback_t: Dentry invalidation
+ *
+ * Called when the client wants to purge a dentry from its cache.
+ * @handle: client callback handle
+ * @dirino: vino of directory that contains dentry to be invalidate
+ * @ino: vino of inode attached to dentry to be invalidated
+ * @name: name of dentry to be invalidated
+ * @len: length of @name
+ */
+typedef void (*client_dentry_callback_t)(void *handle, vinodeno_t dirino,
+ vinodeno_t ino, const char *name,
+ size_t len);
+
+/**
+ * client_remount_callback_t: Remount entire fs
+ *
+ * Called when the client needs to purge the dentry cache and the application
+ * doesn't have a way to purge an individual dentry. Mostly used for ceph-fuse
+ * on older kernels.
+ * @handle: client callback handle
+ */
+
+typedef int (*client_remount_callback_t)(void *handle);
+
+/**
+ * client_switch_interrupt_callback_t: Lock request interrupted
+ *
+ * Called before file lock request to set the interrupt handler while waiting
+ * After the wait, called with "data" set to NULL pointer.
+ * @handle: client callback handle
+ * @data: opaque data passed to interrupt before call, NULL pointer after.
+ */
+typedef void (*client_switch_interrupt_callback_t)(void *handle, void *data);
+
+/**
+ * client_umask_callback_t: Fetch umask of actor
+ *
+ * Called when the client needs the umask of the requestor.
+ * @handle: client callback handle
+ */
+typedef mode_t (*client_umask_callback_t)(void *handle);
+
+/**
+ * client_ino_release_t: Request that application release Inode references
+ *
+ * Called when the MDS wants to trim caps and Inode records.
+ * @handle: client callback handle
+ * @ino: vino of Inode being released
+ */
+typedef void (*client_ino_release_t)(void *handle, vinodeno_t ino);
+
+/*
+ * The handle is an opaque value that gets passed to some callbacks. Any fields
+ * set to NULL will be left alone. There is no way to unregister callbacks.
+ */
+struct ceph_client_callback_args {
+ void *handle;
+ client_ino_callback_t ino_cb;
+ client_dentry_callback_t dentry_cb;
+ client_switch_interrupt_callback_t switch_intr_cb;
+ client_remount_callback_t remount_cb;
+ client_umask_callback_t umask_cb;
+ client_ino_release_t ino_release_cb;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CEPH_STATX_H */
+
diff --git a/src/include/cephfs/libcephfs.h b/src/include/cephfs/libcephfs.h
new file mode 100644
index 000000000..dc62698fa
--- /dev/null
+++ b/src/include/cephfs/libcephfs.h
@@ -0,0 +1,2201 @@
+// -*- 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) 2009-2011 New Dream Network
+ *
+ * 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_LIB_H
+#define CEPH_LIB_H
+
+#if defined(__linux__)
+#include <features.h>
+#endif
+#include <utime.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/statvfs.h>
+#include <sys/socket.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <fcntl.h>
+#include <dirent.h>
+
+#include "ceph_ll_client.h"
+
+#ifdef __cplusplus
+namespace ceph::common {
+ class CephContext;
+}
+using CephContext = ceph::common::CephContext;
+extern "C" {
+#endif
+
+#define LIBCEPHFS_VER_MAJOR 10
+#define LIBCEPHFS_VER_MINOR 0
+#define LIBCEPHFS_VER_EXTRA 3
+
+#define LIBCEPHFS_VERSION(maj, min, extra) ((maj << 16) + (min << 8) + extra)
+#define LIBCEPHFS_VERSION_CODE LIBCEPHFS_VERSION(LIBCEPHFS_VER_MAJOR, LIBCEPHFS_VER_MINOR, LIBCEPHFS_VER_EXTRA)
+
+#if __GNUC__ >= 4
+ #define LIBCEPHFS_DEPRECATED __attribute__((deprecated))
+ #pragma GCC diagnostic push
+ #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#else
+ #define LIBCEPHFS_DEPRECATED
+#endif
+
+/*
+ * If using glibc check that file offset is 64-bit.
+ */
+#if defined(__GLIBC__) && !defined(__USE_FILE_OFFSET64)
+# error libceph: glibc must define __USE_FILE_OFFSET64 or readdir results will be corrupted
+#endif
+
+/*
+ * XXXX redeclarations from ceph_fs.h, rados.h, etc. We need more of this
+ * in the interface, but shouldn't be re-typing it (and using different
+ * C data types).
+ */
+#ifndef __cplusplus
+
+#define CEPH_INO_ROOT 1
+#define CEPH_NOSNAP ((uint64_t)(-2))
+
+struct ceph_file_layout {
+ /* file -> object mapping */
+ uint32_t fl_stripe_unit; /* stripe unit, in bytes. must be multiple
+ of page size. */
+ uint32_t fl_stripe_count; /* over this many objects */
+ uint32_t fl_object_size; /* until objects are this big, then move to
+ new objects */
+ uint32_t fl_cas_hash; /* 0 = none; 1 = sha256 */
+
+ /* pg -> disk layout */
+ uint32_t fl_object_stripe_unit; /* for per-object parity, if any */
+
+ /* object -> pg layout */
+ uint32_t fl_pg_preferred; /* preferred primary for pg (-1 for none) */
+ uint32_t fl_pg_pool; /* namespace, crush rule, rep level */
+} __attribute__ ((packed));
+
+struct CephContext;
+#endif /* ! __cplusplus */
+
+struct UserPerm;
+typedef struct UserPerm UserPerm;
+
+struct Inode;
+typedef struct Inode Inode;
+
+struct ceph_mount_info;
+struct ceph_dir_result;
+
+// user supplied key,value pair to be associated with a snapshot.
+// callers can supply an array of this struct via ceph_mksnap().
+struct snap_metadata {
+ const char *key;
+ const char *value;
+};
+
+struct snap_info {
+ uint64_t id;
+ size_t nr_snap_metadata;
+ struct snap_metadata *snap_metadata;
+};
+
+struct ceph_snapdiff_entry_t {
+ struct dirent dir_entry;
+ uint64_t snapid; //should be snapid_t but prefer not to exposure it
+};
+
+/* setattr mask bits (up to an int in size) */
+#ifndef CEPH_SETATTR_MODE
+#define CEPH_SETATTR_MODE (1 << 0)
+#define CEPH_SETATTR_UID (1 << 1)
+#define CEPH_SETATTR_GID (1 << 2)
+#define CEPH_SETATTR_MTIME (1 << 3)
+#define CEPH_SETATTR_ATIME (1 << 4)
+#define CEPH_SETATTR_SIZE (1 << 5)
+#define CEPH_SETATTR_CTIME (1 << 6)
+#define CEPH_SETATTR_MTIME_NOW (1 << 7)
+#define CEPH_SETATTR_ATIME_NOW (1 << 8)
+#define CEPH_SETATTR_BTIME (1 << 9)
+#define CEPH_SETATTR_KILL_SGUID (1 << 10)
+#define CEPH_SETATTR_FSCRYPT_AUTH (1 << 11)
+#define CEPH_SETATTR_FSCRYPT_FILE (1 << 12)
+#define CEPH_SETATTR_KILL_SUID (1 << 13)
+#define CEPH_SETATTR_KILL_SGID (1 << 14)
+#endif
+
+/* define error codes for the mount function*/
+# define CEPHFS_ERROR_MON_MAP_BUILD 1000
+# define CEPHFS_ERROR_NEW_CLIENT 1002
+# define CEPHFS_ERROR_MESSENGER_START 1003
+
+/**
+ * Create a UserPerm credential object.
+ *
+ * Some calls (most notably, the ceph_ll_* ones), take a credential object
+ * that represents the credentials that the calling program is using. This
+ * function creates a new credential object for this purpose. Returns a
+ * pointer to the object, or NULL if it can't be allocated.
+ *
+ * Note that the gidlist array is used directly and is not copied. It must
+ * remain valid over the lifetime of the created UserPerm object.
+ *
+ * @param uid uid to be used
+ * @param gid gid to be used
+ * @param ngids number of gids in supplemental grouplist
+ * @param gidlist array of gid_t's in the list of groups
+ */
+UserPerm *ceph_userperm_new(uid_t uid, gid_t gid, int ngids, gid_t *gidlist);
+
+/**
+ * Destroy a UserPerm credential object.
+ *
+ * @param perm pointer to object to be destroyed
+ *
+ * Currently this just frees the object. Note that the gidlist array is not
+ * freed. The caller must do so if it's necessary.
+ */
+void ceph_userperm_destroy(UserPerm *perm);
+
+/**
+ * Get a pointer to the default UserPerm object for the mount.
+ *
+ * @param cmount the mount info handle
+ *
+ * Every cmount has a default set of credentials. This returns a pointer to
+ * that object.
+ *
+ * Unlike with ceph_userperm_new, this object should not be freed.
+ */
+struct UserPerm *ceph_mount_perms(struct ceph_mount_info *cmount);
+
+/**
+ * Set cmount's default permissions
+ *
+ * @param cmount the mount info handle
+ * @param perm permissions to set to default for mount
+ *
+ * Every cmount has a default set of credentials. This does a deep copy of
+ * the given permissions to the ones in the cmount. Must be done after
+ * ceph_init but before ceph_mount.
+ *
+ * Returns 0 on success, and -EISCONN if the cmount is already mounted.
+ */
+int ceph_mount_perms_set(struct ceph_mount_info *cmount, UserPerm *perm);
+
+/**
+ * @defgroup libcephfs_h_init Setup and Teardown
+ * These are the first and last functions that should be called
+ * when using libcephfs.
+ *
+ * @{
+ */
+
+/**
+ * Get the version of libcephfs.
+ *
+ * The version number is major.minor.patch.
+ *
+ * @param major where to store the major version number
+ * @param minor where to store the minor version number
+ * @param patch where to store the extra version number
+ */
+const char *ceph_version(int *major, int *minor, int *patch);
+
+/**
+ * Create a mount handle for interacting with Ceph. All libcephfs
+ * functions operate on a mount info handle.
+ *
+ * @param cmount the mount info handle to initialize
+ * @param id the id of the client. This can be a unique id that identifies
+ * this client, and will get appended onto "client.". Callers can
+ * pass in NULL, and the id will be the process id of the client.
+ * @returns 0 on success, negative error code on failure
+ */
+int ceph_create(struct ceph_mount_info **cmount, const char * const id);
+
+/**
+ * Create a mount handle from a CephContext, which holds the configuration
+ * for the ceph cluster. A CephContext can be acquired from an existing ceph_mount_info
+ * handle, using the @ref ceph_get_mount_context call. Note that using the same CephContext
+ * for two different mount handles results in the same client entity id being used.
+ *
+ * @param cmount the mount info handle to initialize
+ * @param conf reuse this pre-existing CephContext config
+ * @returns 0 on success, negative error code on failure
+ */
+#ifdef __cplusplus
+int ceph_create_with_context(struct ceph_mount_info **cmount, CephContext *conf);
+#else
+int ceph_create_with_context(struct ceph_mount_info **cmount, struct CephContext *conf);
+#endif
+
+#ifndef VOIDPTR_RADOS_T
+#define VOIDPTR_RADOS_T
+typedef void *rados_t;
+#endif // VOIDPTR_RADOS_T
+
+/**
+ * Create a mount handle from a rados_t, for using libcephfs in the
+ * same process as librados.
+ *
+ * @param cmount the mount info handle to initialize
+ * @param cluster reference to already-initialized librados handle
+ * @returns 0 on success, negative error code on failure
+ */
+int ceph_create_from_rados(struct ceph_mount_info **cmount, rados_t cluster);
+
+/**
+ * Initialize the filesystem client (but do not mount the filesystem yet)
+ *
+ * @returns 0 on success, negative error code on failure
+ */
+int ceph_init(struct ceph_mount_info *cmount);
+
+/**
+ * Optionally set which filesystem to mount, before calling mount.
+ *
+ * An error will be returned if this libcephfs instance is already
+ * mounted. This function is an alternative to setting the global
+ * client_fs setting. Using this function enables multiple libcephfs
+ * instances in the same process to mount different filesystems.
+ *
+ * The filesystem name is *not* validated in this function. That happens
+ * during mount(), where an ENOENT error will result if a non-existent
+ * filesystem was specified here.
+ *
+ * @param cmount the mount info handle
+ * @returns 0 on success, negative error code on failure
+ */
+int ceph_select_filesystem(struct ceph_mount_info *cmount, const char *fs_name);
+
+
+/**
+ * Perform a mount using the path for the root of the mount.
+ *
+ * It is optional to call ceph_init before this. If ceph_init has
+ * not already been called, it will be called in the course of this operation.
+ *
+ * @param cmount the mount info handle
+ * @param root the path for the root of the mount. This can be an existing
+ * directory within the ceph cluster, but most likely it will
+ * be "/". Passing in NULL is equivalent to "/".
+ * @returns 0 on success, negative error code on failure
+ */
+int ceph_mount(struct ceph_mount_info *cmount, const char *root);
+
+/**
+ * Return cluster ID for a mounted ceph filesystem
+ *
+ * Every ceph filesystem has a filesystem ID associated with it. This
+ * function returns that value. If the ceph_mount_info does not refer to a
+ * mounted filesystem, this returns a negative error code.
+ */
+int64_t ceph_get_fs_cid(struct ceph_mount_info *cmount);
+
+/**
+ * Execute a management command remotely on an MDS.
+ *
+ * Must have called ceph_init or ceph_mount before calling this.
+ *
+ * @param mds_spec string representing rank, MDS name, GID or '*'
+ * @param cmd array of null-terminated strings
+ * @param cmdlen length of cmd array
+ * @param inbuf non-null-terminated input data to command
+ * @param inbuflen length in octets of inbuf
+ * @param outbuf populated with pointer to buffer (command output data)
+ * @param outbuflen length of allocated outbuf
+ * @param outs populated with pointer to buffer (command error strings)
+ * @param outslen length of allocated outs
+ *
+ * @return 0 on success, negative error code on failure
+ *
+ */
+int ceph_mds_command(struct ceph_mount_info *cmount,
+ const char *mds_spec,
+ const char **cmd,
+ size_t cmdlen,
+ const char *inbuf, size_t inbuflen,
+ char **outbuf, size_t *outbuflen,
+ char **outs, size_t *outslen);
+
+/**
+ * Free a buffer, such as those used for output arrays from ceph_mds_command
+ */
+void ceph_buffer_free(char *buf);
+
+/**
+ * Unmount a mount handle.
+ *
+ * @param cmount the mount handle
+ * @return 0 on success, negative error code on failure
+ */
+int ceph_unmount(struct ceph_mount_info *cmount);
+
+/**
+ * Abort mds connections
+ *
+ * @param cmount the mount handle
+ * @return 0 on success, negative error code on failure
+ */
+int ceph_abort_conn(struct ceph_mount_info *cmount);
+
+/**
+ * Destroy the mount handle.
+ *
+ * The handle should not be mounted. This should be called on completion of
+ * all libcephfs functions.
+ *
+ * @param cmount the mount handle
+ * @return 0 on success, negative error code on failure.
+ */
+int ceph_release(struct ceph_mount_info *cmount);
+
+/**
+ * Deprecated. Unmount and destroy the ceph mount handle. This should be
+ * called on completion of all libcephfs functions.
+ *
+ * Equivalent to ceph_unmount() + ceph_release() without error handling.
+ *
+ * @param cmount the mount handle to shutdown
+ */
+void ceph_shutdown(struct ceph_mount_info *cmount);
+
+/**
+ * Return associated client addresses
+ *
+ * @param cmount the mount handle
+ * @param addrs the output addresses
+ * @returns 0 on success, a negative error code on failure
+ * @note the returned addrs should be free by the caller
+ */
+int ceph_getaddrs(struct ceph_mount_info *cmount, char** addrs);
+
+/**
+ * Get a global id for current instance
+ *
+ * The handle should not be mounted. This should be called on completion of
+ * all libcephfs functions.
+ *
+ * @param cmount the mount handle
+ * @returns instance global id
+ */
+uint64_t ceph_get_instance_id(struct ceph_mount_info *cmount);
+
+/**
+ * Extract the CephContext from the mount point handle.
+ *
+ * @param cmount the ceph mount handle to get the context from.
+ * @returns the CephContext associated with the mount handle.
+ */
+#ifdef __cplusplus
+CephContext *ceph_get_mount_context(struct ceph_mount_info *cmount);
+#else
+struct CephContext *ceph_get_mount_context(struct ceph_mount_info *cmount);
+#endif
+/*
+ * Check mount status.
+ *
+ * Return non-zero value if mounted. Otherwise, zero.
+ */
+int ceph_is_mounted(struct ceph_mount_info *cmount);
+
+/** @} init */
+
+/**
+ * @defgroup libcephfs_h_config Config
+ * Functions for manipulating the Ceph configuration at runtime.
+ *
+ * @{
+ */
+
+/**
+ * Load the ceph configuration from the specified config file.
+ *
+ * @param cmount the mount handle to load the configuration into.
+ * @param path_list the configuration file path
+ * @returns 0 on success, negative error code on failure
+ */
+int ceph_conf_read_file(struct ceph_mount_info *cmount, const char *path_list);
+
+/**
+ * Parse the command line arguments and load the configuration parameters.
+ *
+ * @param cmount the mount handle to load the configuration parameters into.
+ * @param argc count of the arguments in argv
+ * @param argv the argument list
+ * @returns 0 on success, negative error code on failure
+ */
+int ceph_conf_parse_argv(struct ceph_mount_info *cmount, int argc, const char **argv);
+
+/**
+ * Configure the cluster handle based on an environment variable
+ *
+ * The contents of the environment variable are parsed as if they were
+ * Ceph command line options. If var is NULL, the CEPH_ARGS
+ * environment variable is used.
+ *
+ * @pre ceph_mount() has not been called on the handle
+ *
+ * @note BUG: this is not threadsafe - it uses a static buffer
+ *
+ * @param cmount handle to configure
+ * @param var name of the environment variable to read
+ * @returns 0 on success, negative error code on failure
+ */
+int ceph_conf_parse_env(struct ceph_mount_info *cmount, const char *var);
+
+/** Sets a configuration value from a string.
+ *
+ * @param cmount the mount handle to set the configuration value on
+ * @param option the configuration option to set
+ * @param value the value of the configuration option to set
+ *
+ * @returns 0 on success, negative error code otherwise.
+ */
+int ceph_conf_set(struct ceph_mount_info *cmount, const char *option, const char *value);
+
+/** Set mount timeout.
+ *
+ * @param cmount mount handle to set the configuration value on
+ * @param timeout mount timeout interval
+ *
+ * @returns 0 on success, negative error code otherwise.
+ */
+int ceph_set_mount_timeout(struct ceph_mount_info *cmount, uint32_t timeout);
+
+/**
+ * Gets the configuration value as a string.
+ *
+ * @param cmount the mount handle to set the configuration value on
+ * @param option the config option to get
+ * @param buf the buffer to fill with the value
+ * @param len the length of the buffer.
+ * @returns the size of the buffer filled in with the value, or negative error code on failure
+ */
+int ceph_conf_get(struct ceph_mount_info *cmount, const char *option, char *buf, size_t len);
+
+/** @} config */
+
+/**
+ * @defgroup libcephfs_h_fsops File System Operations.
+ * Functions for getting/setting file system wide information specific to a particular
+ * mount handle.
+ *
+ * @{
+ */
+
+/**
+ * Perform a statfs on the ceph file system. This call fills in file system wide statistics
+ * into the passed in buffer.
+ *
+ * @param cmount the ceph mount handle to use for performing the statfs.
+ * @param path can be any path within the mounted filesystem
+ * @param stbuf the file system statistics filled in by this function.
+ * @return 0 on success, negative error code otherwise.
+ */
+int ceph_statfs(struct ceph_mount_info *cmount, const char *path, struct statvfs *stbuf);
+
+/**
+ * Synchronize all filesystem data to persistent media.
+ *
+ * @param cmount the ceph mount handle to use for performing the sync_fs.
+ * @returns 0 on success or negative error code on failure.
+ */
+int ceph_sync_fs(struct ceph_mount_info *cmount);
+
+/**
+ * Get the current working directory.
+ *
+ * @param cmount the ceph mount to get the current working directory for.
+ * @returns the path to the current working directory
+ */
+const char* ceph_getcwd(struct ceph_mount_info *cmount);
+
+/**
+ * Change the current working directory.
+ *
+ * @param cmount the ceph mount to change the current working directory for.
+ * @param path the path to the working directory to change into.
+ * @returns 0 on success, negative error code otherwise.
+ */
+int ceph_chdir(struct ceph_mount_info *cmount, const char *path);
+
+/** @} fsops */
+
+/**
+ * @defgroup libcephfs_h_dir Directory Operations.
+ * Functions for manipulating and listing directories.
+ *
+ * @{
+ */
+
+/**
+ * Open the given directory.
+ *
+ * @param cmount the ceph mount handle to use to open the directory
+ * @param name the path name of the directory to open. Must be either an absolute path
+ * or a path relative to the current working directory.
+ * @param dirpp the directory result pointer structure to fill in.
+ * @returns 0 on success or negative error code otherwise.
+ */
+int ceph_opendir(struct ceph_mount_info *cmount, const char *name, struct ceph_dir_result **dirpp);
+
+/**
+ * Open a directory referred to by a file descriptor
+ *
+ * @param cmount the ceph mount handle to use to open the directory
+ * @param dirfd open file descriptor for the directory
+ * @param dirpp the directory result pointer structure to fill in
+ * @returns 0 on success or negative error code otherwise
+ */
+int ceph_fdopendir(struct ceph_mount_info *cmount, int dirfd, struct ceph_dir_result **dirpp);
+
+/**
+ * Close the open directory.
+ *
+ * @param cmount the ceph mount handle to use for closing the directory
+ * @param dirp the directory result pointer (set by ceph_opendir) to close
+ * @returns 0 on success or negative error code on failure.
+ */
+int ceph_closedir(struct ceph_mount_info *cmount, struct ceph_dir_result *dirp);
+
+/**
+ * Get the next entry in an open directory.
+ *
+ * @param cmount the ceph mount handle to use for performing the readdir.
+ * @param dirp the directory stream pointer from an opendir holding the state of the
+ * next entry to return.
+ * @returns the next directory entry or NULL if at the end of the directory (or the directory
+ * is empty. This pointer should not be freed by the caller, and is only safe to
+ * access between return and the next call to ceph_readdir or ceph_closedir.
+ */
+struct dirent * ceph_readdir(struct ceph_mount_info *cmount, struct ceph_dir_result *dirp);
+
+/**
+ * A safe version of ceph_readdir, where the directory entry struct is allocated by the caller.
+ *
+ * @param cmount the ceph mount handle to use for performing the readdir.
+ * @param dirp the directory stream pointer from an opendir holding the state of the
+ * next entry to return.
+ * @param de the directory entry pointer filled in with the next directory entry of the dirp state.
+ * @returns 1 if the next entry was filled in, 0 if the end of the directory stream was reached,
+ * and a negative error code on failure.
+ */
+int ceph_readdir_r(struct ceph_mount_info *cmount, struct ceph_dir_result *dirp, struct dirent *de);
+
+/**
+ * A safe version of ceph_readdir that also returns the file statistics (readdir+stat).
+ *
+ * @param cmount the ceph mount handle to use for performing the readdir_plus_r.
+ * @param dirp the directory stream pointer from an opendir holding the state of the
+ * next entry to return.
+ * @param de the directory entry pointer filled in with the next directory entry of the dirp state.
+ * @param stx the stats of the file/directory of the entry returned
+ * @param want mask showing desired inode attrs for returned entry
+ * @param flags bitmask of flags to use when filling out attributes
+ * @param out optional returned Inode argument. If non-NULL, then a reference will be taken on
+ * the inode and the pointer set on success.
+ * @returns 1 if the next entry was filled in, 0 if the end of the directory stream was reached,
+ * and a negative error code on failure.
+ */
+int ceph_readdirplus_r(struct ceph_mount_info *cmount, struct ceph_dir_result *dirp, struct dirent *de,
+ struct ceph_statx *stx, unsigned want, unsigned flags, struct Inode **out);
+
+struct ceph_snapdiff_info
+{
+ struct ceph_mount_info* cmount;
+ struct ceph_dir_result* dir1; // primary dir entry to build snapdiff for.
+ struct ceph_dir_result* dir_aux; // aux dir entry to identify the second snapshot.
+ // Can point to the parent dir entry if entry-in-question
+ // doesn't exist in the second snapshot
+};
+
+/**
+ * Opens snapdiff stream to get snapshots delta (aka snapdiff).
+ *
+ * @param cmount the ceph mount handle to use for snapdiff retrieval.
+ * @param root_path root path for snapshots-in-question
+ * @param rel_path subpath under the root to build delta for
+ * @param snap1 the first snapshot name
+ * @param snap2 the second snapshot name
+ * @param out resulting snapdiff stream handle to be used for snapdiff results
+ retrieval via ceph_readdir_snapdiff
+ * @returns 0 on success and negative error code otherwise
+ */
+int ceph_open_snapdiff(struct ceph_mount_info* cmount,
+ const char* root_path,
+ const char* rel_path,
+ const char* snap1,
+ const char* snap2,
+ struct ceph_snapdiff_info* out);
+/**
+ * Get the next snapshot delta entry.
+ *
+ * @param info snapdiff stream handle opened via ceph_open_snapdiff()
+ * @param out the next snapdiff entry which includes directory entry and the
+ * entry's snapshot id - later one for emerged/existing entry or
+ * former snapshot id for the removed entry.
+ * @returns >0 on success, 0 if no more entries in the stream and negative
+ * error code otherwise
+ */
+int ceph_readdir_snapdiff(struct ceph_snapdiff_info* snapdiff,
+ struct ceph_snapdiff_entry_t* out);
+/**
+ * Close snapdiff stream.
+ *
+ * @param info snapdiff stream handle opened via ceph_open_snapdiff()
+ * @returns 0 on success and negative error code otherwise
+ */
+int ceph_close_snapdiff(struct ceph_snapdiff_info* snapdiff);
+
+/**
+ * Gets multiple directory entries.
+ *
+ * @param cmount the ceph mount handle to use for performing the getdents.
+ * @param dirp the directory stream pointer from an opendir holding the state of the
+ * next entry/entries to return.
+ * @param name an array of struct dirent that gets filled in with the to fill returned directory entries into.
+ * @param buflen the length of the buffer, which should be the number of dirent structs * sizeof(struct dirent).
+ * @returns the length of the buffer that was filled in, will always be multiples of sizeof(struct dirent), or a
+ * negative error code. If the buffer is not large enough for a single entry, -ERANGE is returned.
+ */
+int ceph_getdents(struct ceph_mount_info *cmount, struct ceph_dir_result *dirp, char *name, int buflen);
+
+/**
+ * Gets multiple directory names.
+ *
+ * @param cmount the ceph mount handle to use for performing the getdents.
+ * @param dirp the directory stream pointer from an opendir holding the state of the
+ * next entry/entries to return.
+ * @param name a buffer to fill in with directory entry names.
+ * @param buflen the length of the buffer that can be filled in.
+ * @returns the length of the buffer filled in with entry names, or a negative error code on failure.
+ * If the buffer isn't large enough for a single entry, -ERANGE is returned.
+ */
+int ceph_getdnames(struct ceph_mount_info *cmount, struct ceph_dir_result *dirp, char *name, int buflen);
+
+/**
+ * Rewind the directory stream to the beginning of the directory.
+ *
+ * @param cmount the ceph mount handle to use for performing the rewinddir.
+ * @param dirp the directory stream pointer to rewind.
+ */
+void ceph_rewinddir(struct ceph_mount_info *cmount, struct ceph_dir_result *dirp);
+
+/**
+ * Get the current position of a directory stream.
+ *
+ * @param cmount the ceph mount handle to use for performing the telldir.
+ * @param dirp the directory stream pointer to get the current position of.
+ * @returns the position of the directory stream. Note that the offsets returned
+ * by ceph_telldir do not have a particular order (cannot be compared with
+ * inequality).
+ */
+int64_t ceph_telldir(struct ceph_mount_info *cmount, struct ceph_dir_result *dirp);
+
+/**
+ * Move the directory stream to a position specified by the given offset.
+ *
+ * @param cmount the ceph mount handle to use for performing the seekdir.
+ * @param dirp the directory stream pointer to move.
+ * @param offset the position to move the directory stream to. This offset should be
+ * a value returned by telldir. Note that this value does not refer to the nth
+ * entry in a directory, and can not be manipulated with plus or minus.
+ */
+void ceph_seekdir(struct ceph_mount_info *cmount, struct ceph_dir_result *dirp, int64_t offset);
+
+/**
+ * Create a directory.
+ *
+ * @param cmount the ceph mount handle to use for making the directory.
+ * @param path the path of the directory to create. This must be either an
+ * absolute path or a relative path off of the current working directory.
+ * @param mode the permissions the directory should have once created.
+ * @returns 0 on success or a negative return code on error.
+ */
+int ceph_mkdir(struct ceph_mount_info *cmount, const char *path, mode_t mode);
+
+/**
+ * Create a directory relative to a file descriptor
+ *
+ * @param cmount the ceph mount handle to use for making the directory.
+ * @param dirfd open file descriptor for a directory (or CEPHFS_AT_FDCWD)
+ * @param relpath the path of the directory to create.
+ * @param mode the permissions the directory should have once created.
+ * @returns 0 on success or a negative return code on error.
+ */
+int ceph_mkdirat(struct ceph_mount_info *cmount, int dirfd, const char *relpath, mode_t mode);
+
+/**
+ * Create a snapshot
+ *
+ * @param cmount the ceph mount handle to use for making the directory.
+ * @param path the path of the directory to create snapshot. This must be either an
+ * absolute path or a relative path off of the current working directory.
+ * @param name snapshot name
+ * @param mode the permissions the directory should have once created.
+ * @param snap_metadata array of snap metadata structs
+ * @param nr_snap_metadata number of snap metadata struct entries
+ * @returns 0 on success or a negative return code on error.
+ */
+int ceph_mksnap(struct ceph_mount_info *cmount, const char *path, const char *name,
+ mode_t mode, struct snap_metadata *snap_metadata, size_t nr_snap_metadata);
+
+/**
+ * Remove a snapshot
+ *
+ * @param cmount the ceph mount handle to use for making the directory.
+ * @param path the path of the directory to create snapshot. This must be either an
+ * absolute path or a relative path off of the current working directory.
+ * @param name snapshot name
+ * @returns 0 on success or a negative return code on error.
+ */
+int ceph_rmsnap(struct ceph_mount_info *cmount, const char *path, const char *name);
+
+/**
+ * Create multiple directories at once.
+ *
+ * @param cmount the ceph mount handle to use for making the directories.
+ * @param path the full path of directories and sub-directories that should
+ * be created.
+ * @param mode the permissions the directory should have once created.
+ * @returns 0 on success or a negative return code on error.
+ */
+int ceph_mkdirs(struct ceph_mount_info *cmount, const char *path, mode_t mode);
+
+/**
+ * Remove a directory.
+ *
+ * @param cmount the ceph mount handle to use for removing directories.
+ * @param path the path of the directory to remove.
+ * @returns 0 on success or a negative return code on error.
+ */
+int ceph_rmdir(struct ceph_mount_info *cmount, const char *path);
+
+/** @} dir */
+
+/**
+ * @defgroup libcephfs_h_links Links and Link Handling.
+ * Functions for creating and manipulating hard links and symbolic inks.
+ *
+ * @{
+ */
+
+/**
+ * Create a link.
+ *
+ * @param cmount the ceph mount handle to use for creating the link.
+ * @param existing the path to the existing file/directory to link to.
+ * @param newname the path to the new file/directory to link from.
+ * @returns 0 on success or a negative return code on error.
+ */
+int ceph_link(struct ceph_mount_info *cmount, const char *existing, const char *newname);
+
+/**
+ * Read a symbolic link.
+ *
+ * @param cmount the ceph mount handle to use for creating the link.
+ * @param path the path to the symlink to read
+ * @param buf the buffer to hold the path of the file that the symlink points to.
+ * @param size the length of the buffer
+ * @returns number of bytes copied on success or negative error code on failure
+ */
+int ceph_readlink(struct ceph_mount_info *cmount, const char *path, char *buf, int64_t size);
+
+/**
+ * Read a symbolic link relative to a file descriptor
+ *
+ * @param cmount the ceph mount handle to use for creating the link.
+ * @param dirfd open file descriptor (or CEPHFS_AT_FDCWD)
+ * @param relpath the path to the symlink to read
+ * @param buf the buffer to hold the path of the file that the symlink points to.
+ * @param size the length of the buffer
+ * @returns number of bytes copied on success or negative error code on failure
+ */
+int ceph_readlinkat(struct ceph_mount_info *cmount, int dirfd, const char *relpath, char *buf,
+ int64_t size);
+
+/**
+ * Creates a symbolic link.
+ *
+ * @param cmount the ceph mount handle to use for creating the symbolic link.
+ * @param existing the path to the existing file/directory to link to.
+ * @param newname the path to the new file/directory to link from.
+ * @returns 0 on success or a negative return code on failure.
+ */
+int ceph_symlink(struct ceph_mount_info *cmount, const char *existing, const char *newname);
+
+/**
+ * Creates a symbolic link relative to a file descriptor
+ *
+ * @param cmount the ceph mount handle to use for creating the symbolic link.
+ * @param dirfd open file descriptor (or CEPHFS_AT_FDCWD)
+ * @param existing the path to the existing file/directory to link to.
+ * @param newname the path to the new file/directory to link from.
+ * @returns 0 on success or a negative return code on failure.
+ */
+int ceph_symlinkat(struct ceph_mount_info *cmount, const char *existing, int dirfd,
+ const char *newname);
+
+/** @} links */
+
+/**
+ * @defgroup libcephfs_h_files File manipulation and handling.
+ * Functions for creating and manipulating files.
+ *
+ * @{
+ */
+
+
+/**
+ * Checks if deleting a file, link or directory is allowed.
+ *
+ * @param cmount the ceph mount handle to use.
+ * @param path the path of the file, link or directory.
+ * @returns 0 on success or negative error code on failure.
+ */
+int ceph_may_delete(struct ceph_mount_info *cmount, const char *path);
+
+/**
+ * Removes a file, link, or symbolic link. If the file/link has multiple links to it, the
+ * file will not disappear from the namespace until all references to it are removed.
+ *
+ * @param cmount the ceph mount handle to use for performing the unlink.
+ * @param path the path of the file or link to unlink.
+ * @returns 0 on success or negative error code on failure.
+ */
+int ceph_unlink(struct ceph_mount_info *cmount, const char *path);
+
+/**
+ * Removes a file, link, or symbolic link relative to a file descriptor.
+ * If the file/link has multiple links to it, the file will not
+ * disappear from the namespace until all references to it are removed.
+ *
+ * @param cmount the ceph mount handle to use for performing the unlink.
+ * @param dirfd open file descriptor (or CEPHFS_AT_FDCWD)
+ * @param relpath the path of the file or link to unlink.
+ * @param flags bitfield that can be used to set AT_* modifier flags (only AT_REMOVEDIR)
+ * @returns 0 on success or negative error code on failure.
+ */
+int ceph_unlinkat(struct ceph_mount_info *cmount, int dirfd, const char *relpath, int flags);
+
+/**
+ * Rename a file or directory.
+ *
+ * @param cmount the ceph mount handle to use for performing the rename.
+ * @param from the path to the existing file or directory.
+ * @param to the new name of the file or directory
+ * @returns 0 on success or negative error code on failure.
+ */
+int ceph_rename(struct ceph_mount_info *cmount, const char *from, const char *to);
+
+/**
+ * Get an open file's extended statistics and attributes.
+ *
+ * @param cmount the ceph mount handle to use for performing the stat.
+ * @param fd the file descriptor of the file to get statistics of.
+ * @param stx the ceph_statx struct that will be filled in with the file's statistics.
+ * @param want bitfield of CEPH_STATX_* flags showing designed attributes
+ * @param flags bitfield that can be used to set AT_* modifier flags (AT_STATX_SYNC_AS_STAT, AT_STATX_FORCE_SYNC, AT_STATX_DONT_SYNC and AT_SYMLINK_NOFOLLOW)
+ * @returns 0 on success or negative error code on failure.
+ */
+int ceph_fstatx(struct ceph_mount_info *cmount, int fd, struct ceph_statx *stx,
+ unsigned int want, unsigned int flags);
+
+/**
+ * Get attributes of a file relative to a file descriptor
+ *
+ * @param cmount the ceph mount handle to use for performing the stat.
+ * @param dirfd open file descriptor (or CEPHFS_AT_FDCWD)
+ * @param relpath to the file/directory to get statistics of
+ * @param stx the ceph_statx struct that will be filled in with the file's statistics.
+ * @param want bitfield of CEPH_STATX_* flags showing designed attributes
+ * @param flags bitfield that can be used to set AT_* modifier flags (AT_STATX_SYNC_AS_STAT, AT_STATX_FORCE_SYNC, AT_STATX_DONT_SYNC and AT_SYMLINK_NOFOLLOW)
+ * @returns 0 on success or negative error code on failure.
+ */
+int ceph_statxat(struct ceph_mount_info *cmount, int dirfd, const char *relpath,
+ struct ceph_statx *stx, unsigned int want, unsigned int flags);
+
+/**
+ * Get a file's extended statistics and attributes.
+ *
+ * @param cmount the ceph mount handle to use for performing the stat.
+ * @param path the file or directory to get the statistics of.
+ * @param stx the ceph_statx struct that will be filled in with the file's statistics.
+ * @param want bitfield of CEPH_STATX_* flags showing designed attributes
+ * @param flags bitfield that can be used to set AT_* modifier flags (AT_STATX_SYNC_AS_STAT, AT_STATX_FORCE_SYNC, AT_STATX_DONT_SYNC and AT_SYMLINK_NOFOLLOW)
+ * @returns 0 on success or negative error code on failure.
+ */
+int ceph_statx(struct ceph_mount_info *cmount, const char *path, struct ceph_statx *stx,
+ unsigned int want, unsigned int flags);
+
+/**
+ * Get a file's statistics and attributes.
+ *
+ * ceph_stat() is deprecated, use ceph_statx() instead.
+ *
+ * @param cmount the ceph mount handle to use for performing the stat.
+ * @param path the file or directory to get the statistics of.
+ * @param stbuf the stat struct that will be filled in with the file's statistics.
+ * @returns 0 on success or negative error code on failure.
+ */
+int ceph_stat(struct ceph_mount_info *cmount, const char *path, struct stat *stbuf)
+ LIBCEPHFS_DEPRECATED;
+
+/**
+ * Get a file's statistics and attributes, without following symlinks.
+ *
+ * ceph_lstat() is deprecated, use ceph_statx(.., AT_SYMLINK_NOFOLLOW) instead.
+ *
+ * @param cmount the ceph mount handle to use for performing the stat.
+ * @param path the file or directory to get the statistics of.
+ * @param stbuf the stat struct that will be filled in with the file's statistics.
+ * @returns 0 on success or negative error code on failure.
+ */
+int ceph_lstat(struct ceph_mount_info *cmount, const char *path, struct stat *stbuf)
+ LIBCEPHFS_DEPRECATED;
+
+/**
+ * Get the open file's statistics.
+ *
+ * ceph_fstat() is deprecated, use ceph_fstatx() instead.
+ *
+ * @param cmount the ceph mount handle to use for performing the fstat.
+ * @param fd the file descriptor of the file to get statistics of.
+ * @param stbuf the stat struct of the file's statistics, filled in by the
+ * function.
+ * @returns 0 on success or a negative error code on failure
+ */
+int ceph_fstat(struct ceph_mount_info *cmount, int fd, struct stat *stbuf)
+ LIBCEPHFS_DEPRECATED;
+
+/**
+ * Set a file's attributes.
+ *
+ * @param cmount the ceph mount handle to use for performing the setattr.
+ * @param relpath the path to the file/directory to set the attributes of.
+ * @param stx the statx struct that must include attribute values to set on the file.
+ * @param mask a mask of all the CEPH_SETATTR_* values that have been set in the statx struct.
+ * @param flags mask of AT_* flags (only AT_ATTR_NOFOLLOW is respected for now)
+ * @returns 0 on success or negative error code on failure.
+ */
+int ceph_setattrx(struct ceph_mount_info *cmount, const char *relpath, struct ceph_statx *stx, int mask, int flags);
+
+/**
+ * Set a file's attributes (extended version).
+ *
+ * @param cmount the ceph mount handle to use for performing the setattr.
+ * @param fd the fd of the open file/directory to set the attributes of.
+ * @param stx the statx struct that must include attribute values to set on the file.
+ * @param mask a mask of all the stat values that have been set on the stat struct.
+ * @returns 0 on success or negative error code on failure.
+ */
+int ceph_fsetattrx(struct ceph_mount_info *cmount, int fd, struct ceph_statx *stx, int mask);
+
+/**
+ * Change the mode bits (permissions) of a file/directory.
+ *
+ * @param cmount the ceph mount handle to use for performing the chmod.
+ * @param path the path to the file/directory to change the mode bits on.
+ * @param mode the new permissions to set.
+ * @returns 0 on success or a negative error code on failure.
+ */
+int ceph_chmod(struct ceph_mount_info *cmount, const char *path, mode_t mode);
+
+/**
+ * Change the mode bits (permissions) of a file/directory. If the path is a
+ * symbolic link, it's not de-referenced.
+ *
+ * @param cmount the ceph mount handle to use for performing the chmod.
+ * @param path the path of file/directory to change the mode bits on.
+ * @param mode the new permissions to set.
+ * @returns 0 on success or a negative error code on failure.
+ */
+int ceph_lchmod(struct ceph_mount_info *cmount, const char *path, mode_t mode);
+
+/**
+ * Change the mode bits (permissions) of an open file.
+ *
+ * @param cmount the ceph mount handle to use for performing the chmod.
+ * @param fd the open file descriptor to change the mode bits on.
+ * @param mode the new permissions to set.
+ * @returns 0 on success or a negative error code on failure.
+ */
+int ceph_fchmod(struct ceph_mount_info *cmount, int fd, mode_t mode);
+
+/**
+ * Change the mode bits (permissions) of a file relative to a file descriptor.
+ *
+ * @param cmount the ceph mount handle to use for performing the chown.
+ * @param dirfd open file descriptor (or CEPHFS_AT_FDCWD)
+ * @param relpath the relpath of the file/directory to change the ownership of.
+ * @param mode the new permissions to set.
+ * @param flags bitfield that can be used to set AT_* modifier flags (AT_SYMLINK_NOFOLLOW)
+ * @returns 0 on success or negative error code on failure.
+ */
+int ceph_chmodat(struct ceph_mount_info *cmount, int dirfd, const char *relpath,
+ mode_t mode, int flags);
+
+/**
+ * Change the ownership of a file/directory.
+ *
+ * @param cmount the ceph mount handle to use for performing the chown.
+ * @param path the path of the file/directory to change the ownership of.
+ * @param uid the user id to set on the file/directory.
+ * @param gid the group id to set on the file/directory.
+ * @returns 0 on success or negative error code on failure.
+ */
+int ceph_chown(struct ceph_mount_info *cmount, const char *path, int uid, int gid);
+
+/**
+ * Change the ownership of a file from an open file descriptor.
+ *
+ * @param cmount the ceph mount handle to use for performing the chown.
+ * @param fd the fd of the open file/directory to change the ownership of.
+ * @param uid the user id to set on the file/directory.
+ * @param gid the group id to set on the file/directory.
+ * @returns 0 on success or negative error code on failure.
+ */
+int ceph_fchown(struct ceph_mount_info *cmount, int fd, int uid, int gid);
+
+/**
+ * Change the ownership of a file/directory, don't follow symlinks.
+ *
+ * @param cmount the ceph mount handle to use for performing the chown.
+ * @param path the path of the file/directory to change the ownership of.
+ * @param uid the user id to set on the file/directory.
+ * @param gid the group id to set on the file/directory.
+ * @returns 0 on success or negative error code on failure.
+ */
+int ceph_lchown(struct ceph_mount_info *cmount, const char *path, int uid, int gid);
+
+/**
+ * Change the ownership of a file/directory releative to a file descriptor.
+ *
+ * @param cmount the ceph mount handle to use for performing the chown.
+ * @param dirfd open file descriptor (or CEPHFS_AT_FDCWD)
+ * @param relpath the relpath of the file/directory to change the ownership of.
+ * @param uid the user id to set on the file/directory.
+ * @param gid the group id to set on the file/directory.
+ * @param flags bitfield that can be used to set AT_* modifier flags (AT_SYMLINK_NOFOLLOW)
+ * @returns 0 on success or negative error code on failure.
+ */
+int ceph_chownat(struct ceph_mount_info *cmount, int dirfd, const char *relpath,
+ uid_t uid, gid_t gid, int flags);
+
+/**
+ * Change file/directory last access and modification times.
+ *
+ * @param cmount the ceph mount handle to use for performing the utime.
+ * @param path the path to the file/directory to set the time values of.
+ * @param buf holding the access and modification times to set on the file.
+ * @returns 0 on success or negative error code on failure.
+ */
+int ceph_utime(struct ceph_mount_info *cmount, const char *path, struct utimbuf *buf);
+
+/**
+ * Change file/directory last access and modification times.
+ *
+ * @param cmount the ceph mount handle to use for performing the utime.
+ * @param fd the fd of the open file/directory to set the time values of.
+ * @param buf holding the access and modification times to set on the file.
+ * @returns 0 on success or negative error code on failure.
+ */
+int ceph_futime(struct ceph_mount_info *cmount, int fd, struct utimbuf *buf);
+
+/**
+ * Change file/directory last access and modification times.
+ *
+ * @param cmount the ceph mount handle to use for performing the utime.
+ * @param path the path to the file/directory to set the time values of.
+ * @param times holding the access and modification times to set on the file.
+ * @returns 0 on success or negative error code on failure.
+ */
+int ceph_utimes(struct ceph_mount_info *cmount, const char *path, struct timeval times[2]);
+
+/**
+ * Change file/directory last access and modification times, don't follow symlinks.
+ *
+ * @param cmount the ceph mount handle to use for performing the utime.
+ * @param path the path to the file/directory to set the time values of.
+ * @param times holding the access and modification times to set on the file.
+ * @returns 0 on success or negative error code on failure.
+ */
+int ceph_lutimes(struct ceph_mount_info *cmount, const char *path, struct timeval times[2]);
+
+/**
+ * Change file/directory last access and modification times.
+ *
+ * @param cmount the ceph mount handle to use for performing the utime.
+ * @param fd the fd of the open file/directory to set the time values of.
+ * @param times holding the access and modification times to set on the file.
+ * @returns 0 on success or negative error code on failure.
+ */
+int ceph_futimes(struct ceph_mount_info *cmount, int fd, struct timeval times[2]);
+
+/**
+ * Change file/directory last access and modification times.
+ *
+ * @param cmount the ceph mount handle to use for performing the utime.
+ * @param fd the fd of the open file/directory to set the time values of.
+ * @param times holding the access and modification times to set on the file.
+ * @returns 0 on success or negative error code on failure.
+ */
+int ceph_futimens(struct ceph_mount_info *cmount, int fd, struct timespec times[2]);
+
+/**
+ * Change file/directory last access and modification times relative
+ * to a file descriptor.
+ *
+ * @param cmount the ceph mount handle to use for performing the utime.
+ * @param dirfd open file descriptor (or CEPHFS_AT_FDCWD)
+ * @param relpath the relpath of the file/directory to change the ownership of.
+ * @param dirfd the fd of the open file/directory to set the time values of.
+ * @param times holding the access and modification times to set on the file.
+ * @param flags bitfield that can be used to set AT_* modifier flags (AT_SYMLINK_NOFOLLOW)
+ * @returns 0 on success or negative error code on failure.
+ */
+int ceph_utimensat(struct ceph_mount_info *cmount, int dirfd, const char *relpath,
+ struct timespec times[2], int flags);
+
+/**
+ * Apply or remove an advisory lock.
+ *
+ * @param cmount the ceph mount handle to use for performing the lock.
+ * @param fd the open file descriptor to change advisory lock.
+ * @param operation the advisory lock operation to be performed on the file
+ * descriptor among LOCK_SH (shared lock), LOCK_EX (exclusive lock),
+ * or LOCK_UN (remove lock). The LOCK_NB value can be ORed to perform a
+ * non-blocking operation.
+ * @param owner the user-supplied owner identifier (an arbitrary integer)
+ * @returns 0 on success or negative error code on failure.
+ */
+int ceph_flock(struct ceph_mount_info *cmount, int fd, int operation,
+ uint64_t owner);
+
+/**
+ * Truncate the file to the given size. If this operation causes the
+ * file to expand, the empty bytes will be filled in with zeros.
+ *
+ * @param cmount the ceph mount handle to use for performing the truncate.
+ * @param path the path to the file to truncate.
+ * @param size the new size of the file.
+ * @returns 0 on success or a negative error code on failure.
+ */
+int ceph_truncate(struct ceph_mount_info *cmount, const char *path, int64_t size);
+
+/**
+ * Make a block or character special file.
+ *
+ * @param cmount the ceph mount handle to use for performing the mknod.
+ * @param path the path to the special file.
+ * @param mode the permissions to use and the type of special file. The type can be
+ * one of S_IFREG, S_IFCHR, S_IFBLK, S_IFIFO.
+ * @param rdev If the file type is S_IFCHR or S_IFBLK then this parameter specifies the
+ * major and minor numbers of the newly created device special file. Otherwise,
+ * it is ignored.
+ * @returns 0 on success or negative error code on failure.
+ */
+int ceph_mknod(struct ceph_mount_info *cmount, const char *path, mode_t mode, dev_t rdev);
+/**
+ * Create and/or open a file.
+ *
+ * @param cmount the ceph mount handle to use for performing the open.
+ * @param path the path of the file to open. If the flags parameter includes O_CREAT,
+ * the file will first be created before opening.
+ * @param flags a set of option masks that control how the file is created/opened.
+ * @param mode the permissions to place on the file if the file does not exist and O_CREAT
+ * is specified in the flags.
+ * @returns a non-negative file descriptor number on success or a negative error code on failure.
+ */
+int ceph_open(struct ceph_mount_info *cmount, const char *path, int flags, mode_t mode);
+
+/**
+ * Create and/or open a file relative to a directory
+ *
+ * @param cmount the ceph mount handle to use for performing the open.
+ * @param dirfd open file descriptor (or CEPHFS_AT_FDCWD)
+ * @param relpath the path of the file to open. If the flags parameter includes O_CREAT,
+ * the file will first be created before opening.
+ * @param flags a set of option masks that control how the file is created/opened.
+ * @param mode the permissions to place on the file if the file does not exist and O_CREAT
+ * is specified in the flags.
+ * @returns a non-negative file descriptor number on success or a negative error code on failure.
+ */
+int ceph_openat(struct ceph_mount_info *cmount, int dirfd, const char *relpath, int flags, mode_t mode);
+
+/**
+ * Create and/or open a file with a specific file layout.
+ *
+ * @param cmount the ceph mount handle to use for performing the open.
+ * @param path the path of the file to open. If the flags parameter includes O_CREAT,
+ * the file will first be created before opening.
+ * @param flags a set of option masks that control how the file is created/opened.
+ * @param mode the permissions to place on the file if the file does not exist and O_CREAT
+ * is specified in the flags.
+ * @param stripe_unit the stripe unit size (option, 0 for default)
+ * @param stripe_count the stripe count (optional, 0 for default)
+ * @param object_size the object size (optional, 0 for default)
+ * @param data_pool name of target data pool name (optional, NULL or empty string for default)
+ * @returns a non-negative file descriptor number on success or a negative error code on failure.
+ */
+int ceph_open_layout(struct ceph_mount_info *cmount, const char *path, int flags,
+ mode_t mode, int stripe_unit, int stripe_count, int object_size,
+ const char *data_pool);
+
+/**
+ * Close the open file.
+ *
+ * @param cmount the ceph mount handle to use for performing the close.
+ * @param fd the file descriptor referring to the open file.
+ * @returns 0 on success or a negative error code on failure.
+ */
+int ceph_close(struct ceph_mount_info *cmount, int fd);
+
+/**
+ * Reposition the open file stream based on the given offset.
+ *
+ * @param cmount the ceph mount handle to use for performing the lseek.
+ * @param fd the open file descriptor referring to the open file and holding the
+ * current position of the stream.
+ * @param offset the offset to set the stream to
+ * @param whence the flag to indicate what type of seeking to perform:
+ * SEEK_SET: the offset is set to the given offset in the file.
+ * SEEK_CUR: the offset is set to the current location plus @e offset bytes.
+ * SEEK_END: the offset is set to the end of the file plus @e offset bytes.
+ * @returns 0 on success or a negative error code on failure.
+ */
+int64_t ceph_lseek(struct ceph_mount_info *cmount, int fd, int64_t offset, int whence);
+/**
+ * Read data from the file.
+ *
+ * @param cmount the ceph mount handle to use for performing the read.
+ * @param fd the file descriptor of the open file to read from.
+ * @param buf the buffer to read data into
+ * @param size the initial size of the buffer
+ * @param offset the offset in the file to read from. If this value is negative, the
+ * function reads from the current offset of the file descriptor.
+ * @returns the number of bytes read into buf, or a negative error code on failure.
+ */
+int ceph_read(struct ceph_mount_info *cmount, int fd, char *buf, int64_t size, int64_t offset);
+
+/**
+ * Read data from the file.
+ * @param cmount the ceph mount handle to use for performing the read.
+ * @param fd the file descriptor of the open file to read from.
+ * @param iov the iov structure to read data into
+ * @param iovcnt the number of items that iov includes
+ * @param offset the offset in the file to read from. If this value is negative, the
+ * function reads from the current offset of the file descriptor.
+ * @returns the number of bytes read into buf, or a negative error code on failure.
+ */
+int ceph_preadv(struct ceph_mount_info *cmount, int fd, const struct iovec *iov, int iovcnt,
+ int64_t offset);
+
+/**
+ * Write data to a file.
+ *
+ * @param cmount the ceph mount handle to use for performing the write.
+ * @param fd the file descriptor of the open file to write to
+ * @param buf the bytes to write to the file
+ * @param size the size of the buf array
+ * @param offset the offset of the file write into. If this value is negative, the
+ * function writes to the current offset of the file descriptor.
+ * @returns the number of bytes written, or a negative error code
+ */
+int ceph_write(struct ceph_mount_info *cmount, int fd, const char *buf, int64_t size,
+ int64_t offset);
+
+/**
+ * Write data to a file.
+ *
+ * @param cmount the ceph mount handle to use for performing the write.
+ * @param fd the file descriptor of the open file to write to
+ * @param iov the iov structure to read data into
+ * @param iovcnt the number of items that iov includes
+ * @param offset the offset of the file write into. If this value is negative, the
+ * function writes to the current offset of the file descriptor.
+ * @returns the number of bytes written, or a negative error code
+ */
+int ceph_pwritev(struct ceph_mount_info *cmount, int fd, const struct iovec *iov, int iovcnt,
+ int64_t offset);
+
+/**
+ * Truncate a file to the given size.
+ *
+ * @param cmount the ceph mount handle to use for performing the ftruncate.
+ * @param fd the file descriptor of the file to truncate
+ * @param size the new size of the file
+ * @returns 0 on success or a negative error code on failure.
+ */
+int ceph_ftruncate(struct ceph_mount_info *cmount, int fd, int64_t size);
+
+/**
+ * Synchronize an open file to persistent media.
+ *
+ * @param cmount the ceph mount handle to use for performing the fsync.
+ * @param fd the file descriptor of the file to sync.
+ * @param syncdataonly a boolean whether to synchronize metadata and data (0)
+ * or just data (1).
+ * @return 0 on success or a negative error code on failure.
+ */
+int ceph_fsync(struct ceph_mount_info *cmount, int fd, int syncdataonly);
+
+/**
+ * Preallocate or release disk space for the file for the byte range.
+ *
+ * @param cmount the ceph mount handle to use for performing the fallocate.
+ * @param fd the file descriptor of the file to fallocate.
+ * @param mode the flags determines the operation to be performed on the given range.
+ * default operation (0) allocate and initialize to zero the file in the byte range,
+ * and the file size will be changed if offset + length is greater than
+ * the file size. if the FALLOC_FL_KEEP_SIZE flag is specified in the mode,
+ * the file size will not be changed. if the FALLOC_FL_PUNCH_HOLE flag is
+ * specified in the mode, the operation is deallocate space and zero the byte range.
+ * @param offset the byte range starting.
+ * @param length the length of the range.
+ * @return 0 on success or a negative error code on failure.
+ */
+int ceph_fallocate(struct ceph_mount_info *cmount, int fd, int mode,
+ int64_t offset, int64_t length);
+
+/**
+ * Enable/disable lazyio for the file.
+ *
+ * @param cmount the ceph mount handle to use for performing the fsync.
+ * @param fd the file descriptor of the file to sync.
+ * @param enable a boolean to enable lazyio or disable lazyio.
+ * @returns 0 on success or a negative error code on failure.
+ */
+int ceph_lazyio(struct ceph_mount_info *cmount, int fd, int enable);
+
+
+/**
+ * Flushes the write buffer for the file thereby propogating the buffered write to the file.
+ *
+ * @param cmount the ceph mount handle to use for performing the fsync.
+ * @param fd the file descriptor of the file to sync.
+ * @param offset a boolean to enable lazyio or disable lazyio.
+ * @returns 0 on success or a negative error code on failure.
+ */
+int ceph_lazyio_propagate(struct ceph_mount_info *cmount, int fd, int64_t offset, size_t count);
+
+
+/**
+ * Flushes the write buffer for the file and invalidate the read cache. This allows a subsequent read operation to read and cache data directly from the file and hence everyone's propagated writes would be visible.
+ *
+ * @param cmount the ceph mount handle to use for performing the fsync.
+ * @param fd the file descriptor of the file to sync.
+ * @param offset a boolean to enable lazyio or disable lazyio.
+ * @returns 0 on success or a negative error code on failure.
+ */
+int ceph_lazyio_synchronize(struct ceph_mount_info *cmount, int fd, int64_t offset, size_t count);
+
+/** @} file */
+
+/**
+ * @defgroup libcephfs_h_xattr Extended Attribute manipulation and handling.
+ * Functions for creating and manipulating extended attributes on files.
+ *
+ * @{
+ */
+
+/**
+ * Get an extended attribute.
+ *
+ * @param cmount the ceph mount handle to use for performing the getxattr.
+ * @param path the path to the file
+ * @param name the name of the extended attribute to get
+ * @param value a pre-allocated buffer to hold the xattr's value
+ * @param size the size of the pre-allocated buffer
+ * @returns the size of the value or a negative error code on failure.
+ */
+int ceph_getxattr(struct ceph_mount_info *cmount, const char *path, const char *name,
+ void *value, size_t size);
+
+/**
+ * Get an extended attribute.
+ *
+ * @param cmount the ceph mount handle to use for performing the getxattr.
+ * @param fd the open file descriptor referring to the file to get extended attribute from.
+ * @param name the name of the extended attribute to get
+ * @param value a pre-allocated buffer to hold the xattr's value
+ * @param size the size of the pre-allocated buffer
+ * @returns the size of the value or a negative error code on failure.
+ */
+int ceph_fgetxattr(struct ceph_mount_info *cmount, int fd, const char *name,
+ void *value, size_t size);
+
+/**
+ * Get an extended attribute without following symbolic links. This function is
+ * identical to ceph_getxattr, but if the path refers to a symbolic link,
+ * we get the extended attributes of the symlink rather than the attributes
+ * of the link itself.
+ *
+ * @param cmount the ceph mount handle to use for performing the lgetxattr.
+ * @param path the path to the file
+ * @param name the name of the extended attribute to get
+ * @param value a pre-allocated buffer to hold the xattr's value
+ * @param size the size of the pre-allocated buffer
+ * @returns the size of the value or a negative error code on failure.
+ */
+int ceph_lgetxattr(struct ceph_mount_info *cmount, const char *path, const char *name,
+ void *value, size_t size);
+
+/**
+ * List the extended attribute keys on a file.
+ *
+ * @param cmount the ceph mount handle to use for performing the listxattr.
+ * @param path the path to the file.
+ * @param list a buffer to be filled in with the list of extended attributes keys.
+ * @param size the size of the list buffer.
+ * @returns the size of the resulting list filled in.
+ */
+int ceph_listxattr(struct ceph_mount_info *cmount, const char *path, char *list, size_t size);
+
+/**
+ * List the extended attribute keys on a file.
+ *
+ * @param cmount the ceph mount handle to use for performing the listxattr.
+ * @param fd the open file descriptor referring to the file to list extended attributes on.
+ * @param list a buffer to be filled in with the list of extended attributes keys.
+ * @param size the size of the list buffer.
+ * @returns the size of the resulting list filled in.
+ */
+int ceph_flistxattr(struct ceph_mount_info *cmount, int fd, char *list, size_t size);
+
+/**
+ * Get the list of extended attribute keys on a file, but do not follow symbolic links.
+ *
+ * @param cmount the ceph mount handle to use for performing the llistxattr.
+ * @param path the path to the file.
+ * @param list a buffer to be filled in with the list of extended attributes keys.
+ * @param size the size of the list buffer.
+ * @returns the size of the resulting list filled in.
+ */
+int ceph_llistxattr(struct ceph_mount_info *cmount, const char *path, char *list, size_t size);
+
+/**
+ * Remove an extended attribute from a file.
+ *
+ * @param cmount the ceph mount handle to use for performing the removexattr.
+ * @param path the path to the file.
+ * @param name the name of the extended attribute to remove.
+ * @returns 0 on success or a negative error code on failure.
+ */
+int ceph_removexattr(struct ceph_mount_info *cmount, const char *path, const char *name);
+
+/**
+ * Remove an extended attribute from a file.
+ *
+ * @param cmount the ceph mount handle to use for performing the removexattr.
+ * @param fd the open file descriptor referring to the file to remove extended attribute from.
+ * @param name the name of the extended attribute to remove.
+ * @returns 0 on success or a negative error code on failure.
+ */
+int ceph_fremovexattr(struct ceph_mount_info *cmount, int fd, const char *name);
+
+/**
+ * Remove the extended attribute from a file, do not follow symbolic links.
+ *
+ * @param cmount the ceph mount handle to use for performing the lremovexattr.
+ * @param path the path to the file.
+ * @param name the name of the extended attribute to remove.
+ * @returns 0 on success or a negative error code on failure.
+ */
+int ceph_lremovexattr(struct ceph_mount_info *cmount, const char *path, const char *name);
+
+/**
+ * Set an extended attribute on a file.
+ *
+ * @param cmount the ceph mount handle to use for performing the setxattr.
+ * @param path the path to the file.
+ * @param name the name of the extended attribute to set.
+ * @param value the bytes of the extended attribute value
+ * @param size the size of the extended attribute value
+ * @param flags the flags can be:
+ * CEPH_XATTR_CREATE: create the extended attribute. Must not exist.
+ * CEPH_XATTR_REPLACE: replace the extended attribute, Must already exist.
+ * @returns 0 on success or a negative error code on failure.
+ */
+int ceph_setxattr(struct ceph_mount_info *cmount, const char *path, const char *name,
+ const void *value, size_t size, int flags);
+
+/**
+ * Set an extended attribute on a file.
+ *
+ * @param cmount the ceph mount handle to use for performing the setxattr.
+ * @param fd the open file descriptor referring to the file to set extended attribute on.
+ * @param name the name of the extended attribute to set.
+ * @param value the bytes of the extended attribute value
+ * @param size the size of the extended attribute value
+ * @param flags the flags can be:
+ * CEPH_XATTR_CREATE: create the extended attribute. Must not exist.
+ * CEPH_XATTR_REPLACE: replace the extended attribute, Must already exist.
+ * @returns 0 on success or a negative error code on failure.
+ */
+int ceph_fsetxattr(struct ceph_mount_info *cmount, int fd, const char *name,
+ const void *value, size_t size, int flags);
+
+/**
+ * Set an extended attribute on a file, do not follow symbolic links.
+ *
+ * @param cmount the ceph mount handle to use for performing the lsetxattr.
+ * @param path the path to the file.
+ * @param name the name of the extended attribute to set.
+ * @param value the bytes of the extended attribute value
+ * @param size the size of the extended attribute value
+ * @param flags the flags can be:
+ * CEPH_XATTR_CREATE: create the extended attribute. Must not exist.
+ * CEPH_XATTR_REPLACE: replace the extended attribute, Must already exist.
+ * @returns 0 on success or a negative error code on failure.
+ */
+int ceph_lsetxattr(struct ceph_mount_info *cmount, const char *path, const char *name,
+ const void *value, size_t size, int flags);
+
+/** @} xattr */
+
+/**
+ * @defgroup libcephfs_h_filelayout Control File Layout.
+ * Functions for setting and getting the file layout of existing files.
+ *
+ * @{
+ */
+
+/**
+ * Get the file striping unit from an open file descriptor.
+ *
+ * @param cmount the ceph mount handle to use.
+ * @param fh the open file descriptor referring to the file to get the striping unit of.
+ * @returns the striping unit of the file or a negative error code on failure.
+ */
+int ceph_get_file_stripe_unit(struct ceph_mount_info *cmount, int fh);
+
+/**
+ * Get the file striping unit.
+ *
+ * @param cmount the ceph mount handle to use.
+ * @param path the path of the file/directory get the striping unit of.
+ * @returns the striping unit of the file or a negative error code on failure.
+ */
+int ceph_get_path_stripe_unit(struct ceph_mount_info *cmount, const char *path);
+
+/**
+ * Get the file striping count from an open file descriptor.
+ *
+ * @param cmount the ceph mount handle to use.
+ * @param fh the open file descriptor referring to the file to get the striping count of.
+ * @returns the striping count of the file or a negative error code on failure.
+ */
+int ceph_get_file_stripe_count(struct ceph_mount_info *cmount, int fh);
+
+/**
+ * Get the file striping count.
+ *
+ * @param cmount the ceph mount handle to use.
+ * @param path the path of the file/directory get the striping count of.
+ * @returns the striping count of the file or a negative error code on failure.
+ */
+int ceph_get_path_stripe_count(struct ceph_mount_info *cmount, const char *path);
+
+/**
+ * Get the file object size from an open file descriptor.
+ *
+ * @param cmount the ceph mount handle to use.
+ * @param fh the open file descriptor referring to the file to get the object size of.
+ * @returns the object size of the file or a negative error code on failure.
+ */
+int ceph_get_file_object_size(struct ceph_mount_info *cmount, int fh);
+
+/**
+ * Get the file object size.
+ *
+ * @param cmount the ceph mount handle to use.
+ * @param path the path of the file/directory get the object size of.
+ * @returns the object size of the file or a negative error code on failure.
+ */
+int ceph_get_path_object_size(struct ceph_mount_info *cmount, const char *path);
+
+/**
+ * Get the file pool information from an open file descriptor.
+ *
+ * @param cmount the ceph mount handle to use.
+ * @param fh the open file descriptor referring to the file to get the pool information of.
+ * @returns the ceph pool id that the file is in
+ */
+int ceph_get_file_pool(struct ceph_mount_info *cmount, int fh);
+
+/**
+ * Get the file pool information.
+ *
+ * @param cmount the ceph mount handle to use.
+ * @param path the path of the file/directory get the pool information of.
+ * @returns the ceph pool id that the file is in
+ */
+int ceph_get_path_pool(struct ceph_mount_info *cmount, const char *path);
+
+/**
+ * Get the name of the pool a opened file is stored in,
+ *
+ * Write the name of the file's pool to the buffer. If buflen is 0, return
+ * a suggested length for the buffer.
+ *
+ * @param cmount the ceph mount handle to use.
+ * @param fh the open file descriptor referring to the file
+ * @param buf buffer to store the name in
+ * @param buflen size of the buffer
+ * @returns length in bytes of the pool name, or -ERANGE if the buffer is not large enough.
+ */
+int ceph_get_file_pool_name(struct ceph_mount_info *cmount, int fh, char *buf, size_t buflen);
+
+/**
+ * get the name of a pool by id
+ *
+ * Given a pool's numeric identifier, get the pool's alphanumeric name.
+ *
+ * @param cmount the ceph mount handle to use
+ * @param pool the numeric pool id
+ * @param buf buffer to sore the name in
+ * @param buflen size of the buffer
+ * @returns length in bytes of the pool name, or -ERANGE if the buffer is not large enough
+ */
+int ceph_get_pool_name(struct ceph_mount_info *cmount, int pool, char *buf, size_t buflen);
+
+/**
+ * Get the name of the pool a file is stored in
+ *
+ * Write the name of the file's pool to the buffer. If buflen is 0, return
+ * a suggested length for the buffer.
+ *
+ * @param cmount the ceph mount handle to use.
+ * @param path the path of the file/directory
+ * @param buf buffer to store the name in
+ * @param buflen size of the buffer
+ * @returns length in bytes of the pool name, or -ERANGE if the buffer is not large enough.
+ */
+int ceph_get_path_pool_name(struct ceph_mount_info *cmount, const char *path, char *buf, size_t buflen);
+
+/**
+ * Get the default pool name of cephfs
+ * Write the name of the default pool to the buffer. If buflen is 0, return
+ * a suggested length for the buffer.
+ * @param cmount the ceph mount handle to use.
+ * @param buf buffer to store the name in
+ * @param buflen size of the buffer
+ * @returns length in bytes of the pool name, or -ERANGE if the buffer is not large enough.
+ */
+int ceph_get_default_data_pool_name(struct ceph_mount_info *cmount, char *buf, size_t buflen);
+
+/**
+ * Get the file layout from an open file descriptor.
+ *
+ * @param cmount the ceph mount handle to use.
+ * @param fh the open file descriptor referring to the file to get the layout of.
+ * @param stripe_unit where to store the striping unit of the file
+ * @param stripe_count where to store the striping count of the file
+ * @param object_size where to store the object size of the file
+ * @param pg_pool where to store the ceph pool id that the file is in
+ * @returns 0 on success or a negative error code on failure.
+ */
+int ceph_get_file_layout(struct ceph_mount_info *cmount, int fh, int *stripe_unit, int *stripe_count, int *object_size, int *pg_pool);
+
+/**
+ * Get the file layout.
+ *
+ * @param cmount the ceph mount handle to use.
+ * @param path the path of the file/directory get the layout of.
+ * @param stripe_unit where to store the striping unit of the file
+ * @param stripe_count where to store the striping count of the file
+ * @param object_size where to store the object size of the file
+ * @param pg_pool where to store the ceph pool id that the file is in
+ * @returns 0 on success or a negative error code on failure.
+ */
+int ceph_get_path_layout(struct ceph_mount_info *cmount, const char *path, int *stripe_unit, int *stripe_count, int *object_size, int *pg_pool);
+
+/**
+ * Get the file replication information from an open file descriptor.
+ *
+ * @param cmount the ceph mount handle to use.
+ * @param fh the open file descriptor referring to the file to get the replication information of.
+ * @returns the replication factor of the file.
+ */
+int ceph_get_file_replication(struct ceph_mount_info *cmount, int fh);
+
+/**
+ * Get the file replication information.
+ *
+ * @param cmount the ceph mount handle to use.
+ * @param path the path of the file/directory get the replication information of.
+ * @returns the replication factor of the file.
+ */
+int ceph_get_path_replication(struct ceph_mount_info *cmount, const char *path);
+
+/**
+ * Get the id of the named pool.
+ *
+ * @param cmount the ceph mount handle to use.
+ * @param pool_name the name of the pool.
+ * @returns the pool id, or a negative error code on failure.
+ */
+int ceph_get_pool_id(struct ceph_mount_info *cmount, const char *pool_name);
+
+/**
+ * Get the pool replication factor.
+ *
+ * @param cmount the ceph mount handle to use.
+ * @param pool_id the pool id to look up
+ * @returns the replication factor, or a negative error code on failure.
+ */
+int ceph_get_pool_replication(struct ceph_mount_info *cmount, int pool_id);
+
+/**
+ * Get the OSD address where the primary copy of a file stripe is located.
+ *
+ * @param cmount the ceph mount handle to use.
+ * @param fd the open file descriptor referring to the file to get the striping unit of.
+ * @param offset the offset into the file to specify the stripe. The offset can be
+ * anywhere within the stripe unit.
+ * @param addr the address of the OSD holding that stripe
+ * @param naddr the capacity of the address passed in.
+ * @returns the size of the addressed filled into the @e addr parameter, or a negative
+ * error code on failure.
+ */
+int ceph_get_file_stripe_address(struct ceph_mount_info *cmount, int fd, int64_t offset,
+ struct sockaddr_storage *addr, int naddr);
+
+/**
+ * Get the list of OSDs where the objects containing a file offset are located.
+ *
+ * @param cmount the ceph mount handle to use.
+ * @param fd the open file descriptor referring to the file.
+ * @param offset the offset within the file.
+ * @param length return the number of bytes between the offset and the end of
+ * the stripe unit (optional).
+ * @param osds an integer array to hold the OSD ids.
+ * @param nosds the size of the integer array.
+ * @returns the number of items stored in the output array, or -ERANGE if the
+ * array is not large enough.
+ */
+int ceph_get_file_extent_osds(struct ceph_mount_info *cmount, int fd,
+ int64_t offset, int64_t *length, int *osds, int nosds);
+
+/**
+ * Get the fully qualified CRUSH location of an OSD.
+ *
+ * Returns (type, name) string pairs for each device in the CRUSH bucket
+ * hierarchy starting from the given osd to the root. Each pair element is
+ * separated by a NULL character.
+ *
+ * @param cmount the ceph mount handle to use.
+ * @param osd the OSD id.
+ * @param path buffer to store location.
+ * @param len size of buffer.
+ * @returns the amount of bytes written into the buffer, or -ERANGE if the
+ * array is not large enough.
+ */
+int ceph_get_osd_crush_location(struct ceph_mount_info *cmount,
+ int osd, char *path, size_t len);
+
+/**
+ * Get the network address of an OSD.
+ *
+ * @param cmount the ceph mount handle.
+ * @param osd the OSD id.
+ * @param addr the OSD network address.
+ * @returns zero on success, other returns a negative error code.
+ */
+int ceph_get_osd_addr(struct ceph_mount_info *cmount, int osd,
+ struct sockaddr_storage *addr);
+
+/**
+ * Get the file layout stripe unit granularity.
+ * @param cmount the ceph mount handle.
+ * @returns the stripe unit granularity or a negative error code on failure.
+ */
+int ceph_get_stripe_unit_granularity(struct ceph_mount_info *cmount);
+
+/** @} filelayout */
+
+/**
+ * No longer available. Do not use.
+ * These functions will return -EOPNOTSUPP.
+ */
+int ceph_set_default_file_stripe_unit(struct ceph_mount_info *cmount, int stripe);
+int ceph_set_default_file_stripe_count(struct ceph_mount_info *cmount, int count);
+int ceph_set_default_object_size(struct ceph_mount_info *cmount, int size);
+int ceph_set_default_preferred_pg(struct ceph_mount_info *cmount, int osd);
+int ceph_set_default_file_replication(struct ceph_mount_info *cmount, int replication);
+
+/**
+ * Read from local replicas when possible.
+ *
+ * @param cmount the ceph mount handle to use.
+ * @param val a boolean to set (1) or clear (0) the option to favor local objects
+ * for reads.
+ * @returns 0
+ */
+int ceph_localize_reads(struct ceph_mount_info *cmount, int val);
+
+/**
+ * Get the osd id of the local osd (if any)
+ *
+ * @param cmount the ceph mount handle to use.
+ * @returns the osd (if any) local to the node where this call is made, otherwise
+ * -1 is returned.
+ */
+int ceph_get_local_osd(struct ceph_mount_info *cmount);
+
+/** @} default_filelayout */
+
+/**
+ * Get the capabilities currently issued to the client.
+ *
+ * @param cmount the ceph mount handle to use.
+ * @param fd the file descriptor to get issued
+ * @returns the current capabilities issued to this client
+ * for the open file
+ */
+int ceph_debug_get_fd_caps(struct ceph_mount_info *cmount, int fd);
+
+/**
+ * Get the capabilities currently issued to the client.
+ *
+ * @param cmount the ceph mount handle to use.
+ * @param path the path to the file
+ * @returns the current capabilities issued to this client
+ * for the file
+ */
+int ceph_debug_get_file_caps(struct ceph_mount_info *cmount, const char *path);
+
+/* Low Level */
+struct Inode *ceph_ll_get_inode(struct ceph_mount_info *cmount,
+ vinodeno_t vino);
+
+int ceph_ll_lookup_vino(struct ceph_mount_info *cmount, vinodeno_t vino,
+ Inode **inode);
+
+int ceph_ll_lookup_inode(
+ struct ceph_mount_info *cmount,
+ struct inodeno_t ino,
+ Inode **inode);
+
+/**
+ * Get the root inode of FS. Increase counter of references for root Inode. You must call ceph_ll_forget for it!
+ *
+ * @param cmount the ceph mount handle to use.
+ * @param parent pointer to pointer to Inode struct. Pointer to root inode will be returned
+ * @returns 0 if all good
+ */
+int ceph_ll_lookup_root(struct ceph_mount_info *cmount,
+ Inode **parent);
+int ceph_ll_lookup(struct ceph_mount_info *cmount, Inode *parent,
+ const char *name, Inode **out, struct ceph_statx *stx,
+ unsigned want, unsigned flags, const UserPerm *perms);
+int ceph_ll_put(struct ceph_mount_info *cmount, struct Inode *in);
+int ceph_ll_forget(struct ceph_mount_info *cmount, struct Inode *in,
+ int count);
+int ceph_ll_walk(struct ceph_mount_info *cmount, const char* name, Inode **i,
+ struct ceph_statx *stx, unsigned int want, unsigned int flags,
+ const UserPerm *perms);
+int ceph_ll_getattr(struct ceph_mount_info *cmount, struct Inode *in,
+ struct ceph_statx *stx, unsigned int want, unsigned int flags,
+ const UserPerm *perms);
+int ceph_ll_setattr(struct ceph_mount_info *cmount, struct Inode *in,
+ struct ceph_statx *stx, int mask, const UserPerm *perms);
+int ceph_ll_open(struct ceph_mount_info *cmount, struct Inode *in, int flags,
+ struct Fh **fh, const UserPerm *perms);
+off_t ceph_ll_lseek(struct ceph_mount_info *cmount, struct Fh* filehandle,
+ off_t offset, int whence);
+int ceph_ll_read(struct ceph_mount_info *cmount, struct Fh* filehandle,
+ int64_t off, uint64_t len, char* buf);
+int ceph_ll_fsync(struct ceph_mount_info *cmount, struct Fh *fh,
+ int syncdataonly);
+int ceph_ll_sync_inode(struct ceph_mount_info *cmount, struct Inode *in,
+ int syncdataonly);
+int ceph_ll_fallocate(struct ceph_mount_info *cmount, struct Fh *fh,
+ int mode, int64_t offset, int64_t length);
+int ceph_ll_write(struct ceph_mount_info *cmount, struct Fh* filehandle,
+ int64_t off, uint64_t len, const char *data);
+int64_t ceph_ll_readv(struct ceph_mount_info *cmount, struct Fh *fh,
+ const struct iovec *iov, int iovcnt, int64_t off);
+int64_t ceph_ll_writev(struct ceph_mount_info *cmount, struct Fh *fh,
+ const struct iovec *iov, int iovcnt, int64_t off);
+int ceph_ll_close(struct ceph_mount_info *cmount, struct Fh* filehandle);
+int ceph_ll_iclose(struct ceph_mount_info *cmount, struct Inode *in, int mode);
+/**
+ * Get xattr value by xattr name.
+ *
+ * @param cmount the ceph mount handle to use.
+ * @param in file handle
+ * @param name name of attribute
+ * @param value pointer to begin buffer
+ * @param size buffer size
+ * @param perms pointer to UserPerms object
+ * @returns size of returned buffer. Negative number in error case
+ */
+int ceph_ll_getxattr(struct ceph_mount_info *cmount, struct Inode *in,
+ const char *name, void *value, size_t size,
+ const UserPerm *perms);
+int ceph_ll_setxattr(struct ceph_mount_info *cmount, struct Inode *in,
+ const char *name, const void *value, size_t size,
+ int flags, const UserPerm *perms);
+int ceph_ll_listxattr(struct ceph_mount_info *cmount, struct Inode *in,
+ char *list, size_t buf_size, size_t *list_size,
+ const UserPerm *perms);
+int ceph_ll_removexattr(struct ceph_mount_info *cmount, struct Inode *in,
+ const char *name, const UserPerm *perms);
+int ceph_ll_create(struct ceph_mount_info *cmount, Inode *parent,
+ const char *name, mode_t mode, int oflags, Inode **outp,
+ Fh **fhp, struct ceph_statx *stx, unsigned want,
+ unsigned lflags, const UserPerm *perms);
+int ceph_ll_mknod(struct ceph_mount_info *cmount, Inode *parent,
+ const char *name, mode_t mode, dev_t rdev, Inode **out,
+ struct ceph_statx *stx, unsigned want, unsigned flags,
+ const UserPerm *perms);
+int ceph_ll_mkdir(struct ceph_mount_info *cmount, Inode *parent,
+ const char *name, mode_t mode, Inode **out,
+ struct ceph_statx *stx, unsigned want,
+ unsigned flags, const UserPerm *perms);
+int ceph_ll_link(struct ceph_mount_info *cmount, struct Inode *in,
+ struct Inode *newparent, const char *name,
+ const UserPerm *perms);
+int ceph_ll_opendir(struct ceph_mount_info *cmount, struct Inode *in,
+ struct ceph_dir_result **dirpp, const UserPerm *perms);
+int ceph_ll_releasedir(struct ceph_mount_info *cmount,
+ struct ceph_dir_result* dir);
+int ceph_ll_rename(struct ceph_mount_info *cmount, struct Inode *parent,
+ const char *name, struct Inode *newparent,
+ const char *newname, const UserPerm *perms);
+int ceph_ll_unlink(struct ceph_mount_info *cmount, struct Inode *in,
+ const char *name, const UserPerm *perms);
+int ceph_ll_statfs(struct ceph_mount_info *cmount, struct Inode *in,
+ struct statvfs *stbuf);
+int ceph_ll_readlink(struct ceph_mount_info *cmount, struct Inode *in,
+ char *buf, size_t bufsize, const UserPerm *perms);
+int ceph_ll_symlink(struct ceph_mount_info *cmount,
+ Inode *in, const char *name, const char *value,
+ Inode **out, struct ceph_statx *stx,
+ unsigned want, unsigned flags,
+ const UserPerm *perms);
+int ceph_ll_rmdir(struct ceph_mount_info *cmount, struct Inode *in,
+ const char *name, const UserPerm *perms);
+uint32_t ceph_ll_stripe_unit(struct ceph_mount_info *cmount,
+ struct Inode *in);
+uint32_t ceph_ll_file_layout(struct ceph_mount_info *cmount,
+ struct Inode *in,
+ struct ceph_file_layout *layout);
+uint64_t ceph_ll_snap_seq(struct ceph_mount_info *cmount,
+ struct Inode *in);
+int ceph_ll_get_stripe_osd(struct ceph_mount_info *cmount,
+ struct Inode *in,
+ uint64_t blockno,
+ struct ceph_file_layout* layout);
+int ceph_ll_num_osds(struct ceph_mount_info *cmount);
+int ceph_ll_osdaddr(struct ceph_mount_info *cmount,
+ int osd, uint32_t *addr);
+uint64_t ceph_ll_get_internal_offset(struct ceph_mount_info *cmount,
+ struct Inode *in, uint64_t blockno);
+int ceph_ll_read_block(struct ceph_mount_info *cmount,
+ struct Inode *in, uint64_t blockid,
+ char* bl, uint64_t offset, uint64_t length,
+ struct ceph_file_layout* layout);
+int ceph_ll_write_block(struct ceph_mount_info *cmount,
+ struct Inode *in, uint64_t blockid,
+ char* buf, uint64_t offset,
+ uint64_t length, struct ceph_file_layout* layout,
+ uint64_t snapseq, uint32_t sync);
+int ceph_ll_commit_blocks(struct ceph_mount_info *cmount,
+ struct Inode *in, uint64_t offset, uint64_t range);
+
+
+int ceph_ll_getlk(struct ceph_mount_info *cmount,
+ Fh *fh, struct flock *fl, uint64_t owner);
+int ceph_ll_setlk(struct ceph_mount_info *cmount,
+ Fh *fh, struct flock *fl, uint64_t owner, int sleep);
+
+int ceph_ll_lazyio(struct ceph_mount_info *cmount, Fh *fh, int enable);
+
+/*
+ * Delegation support
+ *
+ * Delegations are way for an application to request exclusive or
+ * semi-exclusive access to an Inode. The client requests the delegation and
+ * if it's successful it can reliably cache file data and metadata until the
+ * delegation is recalled.
+ *
+ * Recalls are issued via a callback function, provided by the application.
+ * Callback functions should act something like signal handlers. You want to
+ * do as little as possible in the callback. Any major work should be deferred
+ * in some fashion as it's difficult to predict the context in which this
+ * function will be called.
+ *
+ * Once the delegation has been recalled, the application should return it as
+ * soon as possible. The application has client_deleg_timeout seconds to
+ * return it, after which the cmount structure is forcibly unmounted and
+ * further calls into it fail.
+ *
+ * The application can set the client_deleg_timeout config option to suit its
+ * needs, but it should take care to choose a value that allows it to avoid
+ * forcible eviction from the cluster in the event of an application bug.
+ */
+
+/* Commands for manipulating delegation state */
+#ifndef CEPH_DELEGATION_NONE
+# define CEPH_DELEGATION_NONE 0
+# define CEPH_DELEGATION_RD 1
+# define CEPH_DELEGATION_WR 2
+#endif
+
+/**
+ * Get the amount of time that the client has to return caps
+ * @param cmount the ceph mount handle to use.
+ *
+ * In the event that a client does not return its caps, the MDS may blocklist
+ * it after this timeout. Applications should check this value and ensure
+ * that they set the delegation timeout to a value lower than this.
+ *
+ * This call returns the cap return timeout (in seconds) for this cmount, or
+ * zero if it's not mounted.
+ */
+uint32_t ceph_get_cap_return_timeout(struct ceph_mount_info *cmount);
+
+/**
+ * Set the delegation timeout for the mount (thereby enabling delegations)
+ * @param cmount the ceph mount handle to use.
+ * @param timeout the delegation timeout (in seconds)
+ *
+ * Since the client could end up blocklisted if it doesn't return delegations
+ * in time, we mandate that any application wanting to use delegations
+ * explicitly set the timeout beforehand. Until this call is done on the
+ * mount, attempts to set a delegation will return -ETIME.
+ *
+ * Once a delegation is recalled, if it is not returned in this amount of
+ * time, the cmount will be forcibly unmounted and further access attempts
+ * will fail (usually with -ENOTCONN errors).
+ *
+ * This value is further vetted against the cap return timeout, and this call
+ * can fail with -EINVAL if the timeout value is too long. Delegations can be
+ * disabled again by setting the timeout to 0.
+ */
+int ceph_set_deleg_timeout(struct ceph_mount_info *cmount, uint32_t timeout);
+
+/**
+ * Request a delegation on an open Fh
+ * @param cmount the ceph mount handle to use.
+ * @param fh file handle
+ * @param cmd CEPH_DELEGATION_* command
+ * @param cb callback function for recalling delegation
+ * @param priv opaque token passed back during recalls
+ *
+ * Returns 0 if the delegation was granted, -EAGAIN if there was a conflict
+ * and other error codes if there is a fatal error of some sort (e.g. -ENOMEM,
+ * -ETIME)
+ */
+int ceph_ll_delegation(struct ceph_mount_info *cmount, Fh *fh,
+ unsigned int cmd, ceph_deleg_cb_t cb, void *priv);
+
+mode_t ceph_umask(struct ceph_mount_info *cmount, mode_t mode);
+
+/* state reclaim */
+#define CEPH_RECLAIM_RESET 1
+
+/**
+ * Set ceph client uuid
+ * @param cmount the ceph mount handle to use.
+ * @param uuid the uuid to set
+ *
+ * Must be called before mount.
+ */
+void ceph_set_uuid(struct ceph_mount_info *cmount, const char *uuid);
+
+/**
+ * Set ceph client session timeout
+ * @param cmount the ceph mount handle to use.
+ * @param timeout the timeout to set
+ *
+ * Must be called before mount.
+ */
+void ceph_set_session_timeout(struct ceph_mount_info *cmount, unsigned timeout);
+
+/**
+ * Start to reclaim states of other client
+ * @param cmount the ceph mount handle to use.
+ * @param uuid uuid of client whose states need to be reclaimed
+ * @param flags flags that control how states get reclaimed
+ *
+ * Returns 0 success, -EOPNOTSUPP if mds does not support the operation,
+ * -ENOENT if CEPH_RECLAIM_RESET is specified and there is no client
+ * with the given uuid, -ENOTRECOVERABLE in all other error cases.
+ */
+int ceph_start_reclaim(struct ceph_mount_info *cmount,
+ const char *uuid, unsigned flags);
+
+/**
+ * finish reclaiming states of other client (
+ * @param cmount the ceph mount handle to use.
+ */
+void ceph_finish_reclaim(struct ceph_mount_info *cmount);
+
+/**
+ * Register a set of callbacks to be used with this cmount
+ *
+ * This is deprecated, use ceph_ll_register_callbacks2() instead.
+ *
+ * @param cmount the ceph mount handle on which the cb's should be registerd
+ * @param args callback arguments to register with the cmount
+ *
+ * Any fields set to NULL will be ignored. There currently is no way to
+ * unregister these callbacks, so this is a one-way change.
+ */
+void ceph_ll_register_callbacks(struct ceph_mount_info *cmount,
+ struct ceph_client_callback_args *args);
+
+/**
+ * Register a set of callbacks to be used with this cmount
+ * @param cmount the ceph mount handle on which the cb's should be registerd
+ * @param args callback arguments to register with the cmount
+ *
+ * Any fields set to NULL will be ignored. There currently is no way to
+ * unregister these callbacks, so this is a one-way change.
+ *
+ * Returns 0 on success or -EBUSY if the cmount is mounting or already mounted.
+ */
+int ceph_ll_register_callbacks2(struct ceph_mount_info *cmount,
+ struct ceph_client_callback_args *args);
+
+/**
+ * Get snapshot info
+ *
+ * @param cmount the ceph mount handle to use for making the directory.
+ * @param path the path of the snapshot. This must be either an
+ * absolute path or a relative path off of the current working directory.
+ * @returns 0 on success or a negative return code on error.
+ */
+int ceph_get_snap_info(struct ceph_mount_info *cmount,
+ const char *path, struct snap_info *snap_info);
+
+/**
+ * Free snapshot info buffers
+ *
+ * @param snap_info snapshot info struct (fetched via call to ceph_get_snap_info()).
+ */
+void ceph_free_snap_info_buffer(struct snap_info *snap_info);
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/include/cephfs/metrics/Types.h b/src/include/cephfs/metrics/Types.h
new file mode 100644
index 000000000..d7cf56138
--- /dev/null
+++ b/src/include/cephfs/metrics/Types.h
@@ -0,0 +1,699 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_INCLUDE_CEPHFS_METRICS_TYPES_H
+#define CEPH_INCLUDE_CEPHFS_METRICS_TYPES_H
+
+#include <string>
+#include <boost/variant.hpp>
+
+#include "common/Formatter.h"
+#include "include/buffer_fwd.h"
+#include "include/encoding.h"
+#include "include/int_types.h"
+#include "include/stringify.h"
+#include "include/utime.h"
+
+namespace ceph { class Formatter; }
+
+enum ClientMetricType {
+ CLIENT_METRIC_TYPE_CAP_INFO,
+ CLIENT_METRIC_TYPE_READ_LATENCY,
+ CLIENT_METRIC_TYPE_WRITE_LATENCY,
+ CLIENT_METRIC_TYPE_METADATA_LATENCY,
+ CLIENT_METRIC_TYPE_DENTRY_LEASE,
+ CLIENT_METRIC_TYPE_OPENED_FILES,
+ CLIENT_METRIC_TYPE_PINNED_ICAPS,
+ CLIENT_METRIC_TYPE_OPENED_INODES,
+ CLIENT_METRIC_TYPE_READ_IO_SIZES,
+ CLIENT_METRIC_TYPE_WRITE_IO_SIZES,
+ CLIENT_METRIC_TYPE_AVG_READ_LATENCY,
+ CLIENT_METRIC_TYPE_STDEV_READ_LATENCY,
+ CLIENT_METRIC_TYPE_AVG_WRITE_LATENCY,
+ CLIENT_METRIC_TYPE_STDEV_WRITE_LATENCY,
+ CLIENT_METRIC_TYPE_AVG_METADATA_LATENCY,
+ CLIENT_METRIC_TYPE_STDEV_METADATA_LATENCY,
+};
+inline std::ostream &operator<<(std::ostream &os, const ClientMetricType &type) {
+ switch(type) {
+ case ClientMetricType::CLIENT_METRIC_TYPE_CAP_INFO:
+ os << "CAP_INFO";
+ break;
+ case ClientMetricType::CLIENT_METRIC_TYPE_READ_LATENCY:
+ os << "READ_LATENCY";
+ break;
+ case ClientMetricType::CLIENT_METRIC_TYPE_WRITE_LATENCY:
+ os << "WRITE_LATENCY";
+ break;
+ case ClientMetricType::CLIENT_METRIC_TYPE_METADATA_LATENCY:
+ os << "METADATA_LATENCY";
+ break;
+ case ClientMetricType::CLIENT_METRIC_TYPE_DENTRY_LEASE:
+ os << "DENTRY_LEASE";
+ break;
+ case ClientMetricType::CLIENT_METRIC_TYPE_OPENED_FILES:
+ os << "OPENED_FILES";
+ break;
+ case ClientMetricType::CLIENT_METRIC_TYPE_PINNED_ICAPS:
+ os << "PINNED_ICAPS";
+ break;
+ case ClientMetricType::CLIENT_METRIC_TYPE_OPENED_INODES:
+ os << "OPENED_INODES";
+ break;
+ case ClientMetricType::CLIENT_METRIC_TYPE_READ_IO_SIZES:
+ os << "READ_IO_SIZES";
+ break;
+ case ClientMetricType::CLIENT_METRIC_TYPE_WRITE_IO_SIZES:
+ os << "WRITE_IO_SIZES";
+ break;
+ case ClientMetricType::CLIENT_METRIC_TYPE_AVG_READ_LATENCY:
+ os << "AVG_READ_LATENCY";
+ break;
+ case ClientMetricType::CLIENT_METRIC_TYPE_STDEV_READ_LATENCY:
+ os << "STDEV_READ_LATENCY";
+ break;
+ case ClientMetricType::CLIENT_METRIC_TYPE_AVG_WRITE_LATENCY:
+ os << "AVG_WRITE_LATENCY";
+ break;
+ case ClientMetricType::CLIENT_METRIC_TYPE_STDEV_WRITE_LATENCY:
+ os << "STDEV_WRITE_LATENCY";
+ break;
+ case ClientMetricType::CLIENT_METRIC_TYPE_AVG_METADATA_LATENCY:
+ os << "AVG_METADATA_LATENCY";
+ break;
+ case ClientMetricType::CLIENT_METRIC_TYPE_STDEV_METADATA_LATENCY:
+ os << "STDEV_METADATA_LATENCY";
+ break;
+ default:
+ os << "(UNKNOWN:" << static_cast<std::underlying_type<ClientMetricType>::type>(type) << ")";
+ break;
+ }
+
+ return os;
+}
+
+struct ClientMetricPayloadBase {
+ ClientMetricPayloadBase(ClientMetricType type) : metric_type(type) {}
+
+ ClientMetricType get_type() const {
+ return metric_type;
+ }
+
+ void print_type(std::ostream *out) const {
+ *out << metric_type;
+ }
+
+ private:
+ ClientMetricType metric_type;
+};
+
+struct CapInfoPayload : public ClientMetricPayloadBase {
+ uint64_t cap_hits = 0;
+ uint64_t cap_misses = 0;
+ uint64_t nr_caps = 0;
+
+ CapInfoPayload()
+ : ClientMetricPayloadBase(ClientMetricType::CLIENT_METRIC_TYPE_CAP_INFO) { }
+ CapInfoPayload(uint64_t cap_hits, uint64_t cap_misses, uint64_t nr_caps)
+ : ClientMetricPayloadBase(ClientMetricType::CLIENT_METRIC_TYPE_CAP_INFO),
+ cap_hits(cap_hits), cap_misses(cap_misses), nr_caps(nr_caps) {
+ }
+
+ void encode(bufferlist &bl) const {
+ using ceph::encode;
+ ENCODE_START(1, 1, bl);
+ encode(cap_hits, bl);
+ encode(cap_misses, bl);
+ encode(nr_caps, bl);
+ ENCODE_FINISH(bl);
+ }
+
+ void decode(bufferlist::const_iterator &iter) {
+ using ceph::decode;
+ DECODE_START(1, iter);
+ decode(cap_hits, iter);
+ decode(cap_misses, iter);
+ decode(nr_caps, iter);
+ DECODE_FINISH(iter);
+ }
+
+ void dump(Formatter *f) const {
+ f->dump_int("cap_hits", cap_hits);
+ f->dump_int("cap_misses", cap_misses);
+ f->dump_int("num_caps", nr_caps);
+ }
+
+ void print(std::ostream *out) const {
+ *out << "cap_hits: " << cap_hits << " "
+ << "cap_misses: " << cap_misses << " "
+ << "num_caps: " << nr_caps;
+ }
+};
+
+struct ReadLatencyPayload : public ClientMetricPayloadBase {
+ utime_t lat;
+ utime_t mean;
+ uint64_t sq_sum; // sum of squares
+ uint64_t count; // IO count
+
+ ReadLatencyPayload()
+ : ClientMetricPayloadBase(ClientMetricType::CLIENT_METRIC_TYPE_READ_LATENCY) { }
+ ReadLatencyPayload(utime_t lat, utime_t mean, uint64_t sq_sum, uint64_t count)
+ : ClientMetricPayloadBase(ClientMetricType::CLIENT_METRIC_TYPE_READ_LATENCY),
+ lat(lat),
+ mean(mean),
+ sq_sum(sq_sum),
+ count(count) {
+ }
+
+ void encode(bufferlist &bl) const {
+ using ceph::encode;
+ ENCODE_START(2, 1, bl);
+ encode(lat, bl);
+ encode(mean, bl);
+ encode(sq_sum, bl);
+ encode(count, bl);
+ ENCODE_FINISH(bl);
+ }
+
+ void decode(bufferlist::const_iterator &iter) {
+ using ceph::decode;
+ DECODE_START(2, iter);
+ decode(lat, iter);
+ if (struct_v >= 2) {
+ decode(mean, iter);
+ decode(sq_sum, iter);
+ decode(count, iter);
+ }
+ DECODE_FINISH(iter);
+ }
+
+ void dump(Formatter *f) const {
+ f->dump_int("latency", lat);
+ f->dump_int("avg_latency", mean);
+ f->dump_unsigned("sq_sum", sq_sum);
+ f->dump_unsigned("count", count);
+ }
+
+ void print(std::ostream *out) const {
+ *out << "latency: " << lat << ", avg_latency: " << mean
+ << ", sq_sum: " << sq_sum << ", count=" << count;
+ }
+};
+
+struct WriteLatencyPayload : public ClientMetricPayloadBase {
+ utime_t lat;
+ utime_t mean;
+ uint64_t sq_sum; // sum of squares
+ uint64_t count; // IO count
+
+ WriteLatencyPayload()
+ : ClientMetricPayloadBase(ClientMetricType::CLIENT_METRIC_TYPE_WRITE_LATENCY) { }
+ WriteLatencyPayload(utime_t lat, utime_t mean, uint64_t sq_sum, uint64_t count)
+ : ClientMetricPayloadBase(ClientMetricType::CLIENT_METRIC_TYPE_WRITE_LATENCY),
+ lat(lat),
+ mean(mean),
+ sq_sum(sq_sum),
+ count(count){
+ }
+
+ void encode(bufferlist &bl) const {
+ using ceph::encode;
+ ENCODE_START(2, 1, bl);
+ encode(lat, bl);
+ encode(mean, bl);
+ encode(sq_sum, bl);
+ encode(count, bl);
+ ENCODE_FINISH(bl);
+ }
+
+ void decode(bufferlist::const_iterator &iter) {
+ using ceph::decode;
+ DECODE_START(2, iter);
+ decode(lat, iter);
+ if (struct_v >= 2) {
+ decode(mean, iter);
+ decode(sq_sum, iter);
+ decode(count, iter);
+ }
+ DECODE_FINISH(iter);
+ }
+
+ void dump(Formatter *f) const {
+ f->dump_int("latency", lat);
+ f->dump_int("avg_latency", mean);
+ f->dump_unsigned("sq_sum", sq_sum);
+ f->dump_unsigned("count", count);
+ }
+
+ void print(std::ostream *out) const {
+ *out << "latency: " << lat << ", avg_latency: " << mean
+ << ", sq_sum: " << sq_sum << ", count=" << count;
+ }
+};
+
+struct MetadataLatencyPayload : public ClientMetricPayloadBase {
+ utime_t lat;
+ utime_t mean;
+ uint64_t sq_sum; // sum of squares
+ uint64_t count; // IO count
+
+ MetadataLatencyPayload()
+ : ClientMetricPayloadBase(ClientMetricType::CLIENT_METRIC_TYPE_METADATA_LATENCY) { }
+ MetadataLatencyPayload(utime_t lat, utime_t mean, uint64_t sq_sum, uint64_t count)
+ : ClientMetricPayloadBase(ClientMetricType::CLIENT_METRIC_TYPE_METADATA_LATENCY),
+ lat(lat),
+ mean(mean),
+ sq_sum(sq_sum),
+ count(count) {
+ }
+
+ void encode(bufferlist &bl) const {
+ using ceph::encode;
+ ENCODE_START(2, 1, bl);
+ encode(lat, bl);
+ encode(mean, bl);
+ encode(sq_sum, bl);
+ encode(count, bl);
+ ENCODE_FINISH(bl);
+ }
+
+ void decode(bufferlist::const_iterator &iter) {
+ using ceph::decode;
+ DECODE_START(2, iter);
+ decode(lat, iter);
+ if (struct_v >= 2) {
+ decode(mean, iter);
+ decode(sq_sum, iter);
+ decode(count, iter);
+ }
+ DECODE_FINISH(iter);
+ }
+
+ void dump(Formatter *f) const {
+ f->dump_int("latency", lat);
+ f->dump_int("avg_latency", mean);
+ f->dump_unsigned("sq_sum", sq_sum);
+ f->dump_unsigned("count", count);
+ }
+
+ void print(std::ostream *out) const {
+ *out << "latency: " << lat << ", avg_latency: " << mean
+ << ", sq_sum: " << sq_sum << ", count=" << count;
+ }
+};
+
+struct DentryLeasePayload : public ClientMetricPayloadBase {
+ uint64_t dlease_hits = 0;
+ uint64_t dlease_misses = 0;
+ uint64_t nr_dentries = 0;
+
+ DentryLeasePayload()
+ : ClientMetricPayloadBase(ClientMetricType::CLIENT_METRIC_TYPE_DENTRY_LEASE) { }
+ DentryLeasePayload(uint64_t dlease_hits, uint64_t dlease_misses, uint64_t nr_dentries)
+ : ClientMetricPayloadBase(ClientMetricType::CLIENT_METRIC_TYPE_DENTRY_LEASE),
+ dlease_hits(dlease_hits), dlease_misses(dlease_misses), nr_dentries(nr_dentries) { }
+
+ void encode(bufferlist &bl) const {
+ using ceph::encode;
+ ENCODE_START(1, 1, bl);
+ encode(dlease_hits, bl);
+ encode(dlease_misses, bl);
+ encode(nr_dentries, bl);
+ ENCODE_FINISH(bl);
+ }
+
+ void decode(bufferlist::const_iterator &iter) {
+ using ceph::decode;
+ DECODE_START(1, iter);
+ decode(dlease_hits, iter);
+ decode(dlease_misses, iter);
+ decode(nr_dentries, iter);
+ DECODE_FINISH(iter);
+ }
+
+ void dump(Formatter *f) const {
+ f->dump_int("dlease_hits", dlease_hits);
+ f->dump_int("dlease_misses", dlease_misses);
+ f->dump_int("num_dentries", nr_dentries);
+ }
+
+ void print(std::ostream *out) const {
+ *out << "dlease_hits: " << dlease_hits << " "
+ << "dlease_misses: " << dlease_misses << " "
+ << "num_dentries: " << nr_dentries;
+ }
+};
+
+struct OpenedFilesPayload : public ClientMetricPayloadBase {
+ uint64_t opened_files = 0;
+ uint64_t total_inodes = 0;
+
+ OpenedFilesPayload()
+ : ClientMetricPayloadBase(ClientMetricType::CLIENT_METRIC_TYPE_OPENED_FILES) { }
+ OpenedFilesPayload(uint64_t opened_files, uint64_t total_inodes)
+ : ClientMetricPayloadBase(ClientMetricType::CLIENT_METRIC_TYPE_OPENED_FILES),
+ opened_files(opened_files), total_inodes(total_inodes) { }
+
+ void encode(bufferlist &bl) const {
+ using ceph::encode;
+ ENCODE_START(1, 1, bl);
+ encode(opened_files, bl);
+ encode(total_inodes, bl);
+ ENCODE_FINISH(bl);
+ }
+
+ void decode(bufferlist::const_iterator &iter) {
+ using ceph::decode;
+ DECODE_START(1, iter);
+ decode(opened_files, iter);
+ decode(total_inodes, iter);
+ DECODE_FINISH(iter);
+ }
+
+ void dump(Formatter *f) const {
+ f->dump_int("opened_files", opened_files);
+ f->dump_int("total_inodes", total_inodes);
+ }
+
+ void print(std::ostream *out) const {
+ *out << "opened_files: " << opened_files << " "
+ << "total_inodes: " << total_inodes;
+ }
+};
+
+struct PinnedIcapsPayload : public ClientMetricPayloadBase {
+ uint64_t pinned_icaps = 0;
+ uint64_t total_inodes = 0;
+
+ PinnedIcapsPayload()
+ : ClientMetricPayloadBase(ClientMetricType::CLIENT_METRIC_TYPE_PINNED_ICAPS) { }
+ PinnedIcapsPayload(uint64_t pinned_icaps, uint64_t total_inodes)
+ : ClientMetricPayloadBase(ClientMetricType::CLIENT_METRIC_TYPE_PINNED_ICAPS),
+ pinned_icaps(pinned_icaps), total_inodes(total_inodes) { }
+
+ void encode(bufferlist &bl) const {
+ using ceph::encode;
+ ENCODE_START(1, 1, bl);
+ encode(pinned_icaps, bl);
+ encode(total_inodes, bl);
+ ENCODE_FINISH(bl);
+ }
+
+ void decode(bufferlist::const_iterator &iter) {
+ using ceph::decode;
+ DECODE_START(1, iter);
+ decode(pinned_icaps, iter);
+ decode(total_inodes, iter);
+ DECODE_FINISH(iter);
+ }
+
+ void dump(Formatter *f) const {
+ f->dump_int("pinned_icaps", pinned_icaps);
+ f->dump_int("total_inodes", total_inodes);
+ }
+
+ void print(std::ostream *out) const {
+ *out << "pinned_icaps: " << pinned_icaps << " "
+ << "total_inodes: " << total_inodes;
+ }
+};
+
+struct OpenedInodesPayload : public ClientMetricPayloadBase {
+ uint64_t opened_inodes = 0;
+ uint64_t total_inodes = 0;
+
+ OpenedInodesPayload()
+ : ClientMetricPayloadBase(ClientMetricType::CLIENT_METRIC_TYPE_OPENED_INODES) { }
+ OpenedInodesPayload(uint64_t opened_inodes, uint64_t total_inodes)
+ : ClientMetricPayloadBase(ClientMetricType::CLIENT_METRIC_TYPE_OPENED_INODES),
+ opened_inodes(opened_inodes), total_inodes(total_inodes) { }
+
+ void encode(bufferlist &bl) const {
+ using ceph::encode;
+ ENCODE_START(1, 1, bl);
+ encode(opened_inodes, bl);
+ encode(total_inodes, bl);
+ ENCODE_FINISH(bl);
+ }
+
+ void decode(bufferlist::const_iterator &iter) {
+ using ceph::decode;
+ DECODE_START(1, iter);
+ decode(opened_inodes, iter);
+ decode(total_inodes, iter);
+ DECODE_FINISH(iter);
+ }
+
+ void dump(Formatter *f) const {
+ f->dump_int("opened_inodes", opened_inodes);
+ f->dump_int("total_inodes", total_inodes);
+ }
+
+ void print(std::ostream *out) const {
+ *out << "opened_inodes: " << opened_inodes << " "
+ << "total_inodes: " << total_inodes;
+ }
+};
+
+struct ReadIoSizesPayload : public ClientMetricPayloadBase {
+ uint64_t total_ops = 0;
+ uint64_t total_size = 0;
+
+ ReadIoSizesPayload()
+ : ClientMetricPayloadBase(ClientMetricType::CLIENT_METRIC_TYPE_READ_IO_SIZES) { }
+ ReadIoSizesPayload(uint64_t total_ops, uint64_t total_size)
+ : ClientMetricPayloadBase(ClientMetricType::CLIENT_METRIC_TYPE_READ_IO_SIZES),
+ total_ops(total_ops), total_size(total_size) { }
+
+ void encode(bufferlist &bl) const {
+ using ceph::encode;
+ ENCODE_START(1, 1, bl);
+ encode(total_ops, bl);
+ encode(total_size, bl);
+ ENCODE_FINISH(bl);
+ }
+
+ void decode(bufferlist::const_iterator &iter) {
+ using ceph::decode;
+ DECODE_START(1, iter);
+ decode(total_ops, iter);
+ decode(total_size, iter);
+ DECODE_FINISH(iter);
+ }
+
+ void dump(Formatter *f) const {
+ f->dump_int("total_ops", total_ops);
+ f->dump_int("total_size", total_size);
+ }
+
+ void print(std::ostream *out) const {
+ *out << "total_ops: " << total_ops << " total_size: " << total_size;
+ }
+};
+
+struct WriteIoSizesPayload : public ClientMetricPayloadBase {
+ uint64_t total_ops = 0;
+ uint64_t total_size = 0;
+
+ WriteIoSizesPayload()
+ : ClientMetricPayloadBase(ClientMetricType::CLIENT_METRIC_TYPE_WRITE_IO_SIZES) { }
+ WriteIoSizesPayload(uint64_t total_ops, uint64_t total_size)
+ : ClientMetricPayloadBase(ClientMetricType::CLIENT_METRIC_TYPE_WRITE_IO_SIZES),
+ total_ops(total_ops), total_size(total_size) {
+ }
+
+ void encode(bufferlist &bl) const {
+ using ceph::encode;
+ ENCODE_START(1, 1, bl);
+ encode(total_ops, bl);
+ encode(total_size, bl);
+ ENCODE_FINISH(bl);
+ }
+
+ void decode(bufferlist::const_iterator &iter) {
+ using ceph::decode;
+ DECODE_START(1, iter);
+ decode(total_ops, iter);
+ decode(total_size, iter);
+ DECODE_FINISH(iter);
+ }
+
+ void dump(Formatter *f) const {
+ f->dump_int("total_ops", total_ops);
+ f->dump_int("total_size", total_size);
+ }
+
+ void print(std::ostream *out) const {
+ *out << "total_ops: " << total_ops << " total_size: " << total_size;
+ }
+};
+
+struct UnknownPayload : public ClientMetricPayloadBase {
+ UnknownPayload()
+ : ClientMetricPayloadBase(static_cast<ClientMetricType>(-1)) { }
+ UnknownPayload(ClientMetricType metric_type)
+ : ClientMetricPayloadBase(metric_type) { }
+
+ void encode(bufferlist &bl) const {
+ }
+
+ void decode(bufferlist::const_iterator &iter) {
+ using ceph::decode;
+ DECODE_START(254, iter);
+ iter.seek(struct_len);
+ DECODE_FINISH(iter);
+ }
+
+ void dump(Formatter *f) const {
+ }
+
+ void print(std::ostream *out) const {
+ }
+};
+
+typedef boost::variant<CapInfoPayload,
+ ReadLatencyPayload,
+ WriteLatencyPayload,
+ MetadataLatencyPayload,
+ DentryLeasePayload,
+ OpenedFilesPayload,
+ PinnedIcapsPayload,
+ OpenedInodesPayload,
+ ReadIoSizesPayload,
+ WriteIoSizesPayload,
+ UnknownPayload> ClientMetricPayload;
+
+// metric update message sent by clients
+struct ClientMetricMessage {
+public:
+ ClientMetricMessage(const ClientMetricPayload &payload = UnknownPayload())
+ : payload(payload) {
+ }
+
+ class EncodePayloadVisitor : public boost::static_visitor<void> {
+ public:
+ explicit EncodePayloadVisitor(bufferlist &bl) : m_bl(bl) {
+ }
+
+ template <typename ClientMetricPayload>
+ inline void operator()(const ClientMetricPayload &payload) const {
+ using ceph::encode;
+ encode(static_cast<uint32_t>(payload.get_type()), m_bl);
+ payload.encode(m_bl);
+ }
+
+ private:
+ bufferlist &m_bl;
+ };
+
+ class DecodePayloadVisitor : public boost::static_visitor<void> {
+ public:
+ DecodePayloadVisitor(bufferlist::const_iterator &iter) : m_iter(iter) {
+ }
+
+ template <typename ClientMetricPayload>
+ inline void operator()(ClientMetricPayload &payload) const {
+ using ceph::decode;
+ payload.decode(m_iter);
+ }
+
+ private:
+ bufferlist::const_iterator &m_iter;
+ };
+
+ class DumpPayloadVisitor : public boost::static_visitor<void> {
+ public:
+ explicit DumpPayloadVisitor(Formatter *formatter) : m_formatter(formatter) {
+ }
+
+ template <typename ClientMetricPayload>
+ inline void operator()(const ClientMetricPayload &payload) const {
+ m_formatter->dump_string("client_metric_type", stringify(payload.get_type()));
+ payload.dump(m_formatter);
+ }
+
+ private:
+ Formatter *m_formatter;
+ };
+
+ class PrintPayloadVisitor : public boost::static_visitor<void> {
+ public:
+ explicit PrintPayloadVisitor(std::ostream *out) : _out(out) {
+ }
+
+ template <typename ClientMetricPayload>
+ inline void operator()(const ClientMetricPayload &payload) const {
+ *_out << "[client_metric_type: ";
+ payload.print_type(_out);
+ *_out << " ";
+ payload.print(_out);
+ *_out << "]";
+ }
+
+ private:
+ std::ostream *_out;
+ };
+
+ void encode(bufferlist &bl) const {
+ boost::apply_visitor(EncodePayloadVisitor(bl), payload);
+ }
+
+ void decode(bufferlist::const_iterator &iter) {
+ using ceph::decode;
+
+ uint32_t metric_type;
+ decode(metric_type, iter);
+
+ switch (metric_type) {
+ case ClientMetricType::CLIENT_METRIC_TYPE_CAP_INFO:
+ payload = CapInfoPayload();
+ break;
+ case ClientMetricType::CLIENT_METRIC_TYPE_READ_LATENCY:
+ payload = ReadLatencyPayload();
+ break;
+ case ClientMetricType::CLIENT_METRIC_TYPE_WRITE_LATENCY:
+ payload = WriteLatencyPayload();
+ break;
+ case ClientMetricType::CLIENT_METRIC_TYPE_METADATA_LATENCY:
+ payload = MetadataLatencyPayload();
+ break;
+ case ClientMetricType::CLIENT_METRIC_TYPE_DENTRY_LEASE:
+ payload = DentryLeasePayload();
+ break;
+ case ClientMetricType::CLIENT_METRIC_TYPE_OPENED_FILES:
+ payload = OpenedFilesPayload();
+ break;
+ case ClientMetricType::CLIENT_METRIC_TYPE_PINNED_ICAPS:
+ payload = PinnedIcapsPayload();
+ break;
+ case ClientMetricType::CLIENT_METRIC_TYPE_OPENED_INODES:
+ payload = OpenedInodesPayload();
+ break;
+ case ClientMetricType::CLIENT_METRIC_TYPE_READ_IO_SIZES:
+ payload = ReadIoSizesPayload();
+ break;
+ case ClientMetricType::CLIENT_METRIC_TYPE_WRITE_IO_SIZES:
+ payload = WriteIoSizesPayload();
+ break;
+ default:
+ payload = UnknownPayload(static_cast<ClientMetricType>(metric_type));
+ break;
+ }
+
+ boost::apply_visitor(DecodePayloadVisitor(iter), payload);
+ }
+
+ void dump(Formatter *f) const {
+ apply_visitor(DumpPayloadVisitor(f), payload);
+ }
+
+ void print(std::ostream *out) const {
+ apply_visitor(PrintPayloadVisitor(out), payload);
+ }
+
+ ClientMetricPayload payload;
+};
+WRITE_CLASS_ENCODER(ClientMetricMessage);
+
+#endif // CEPH_INCLUDE_CEPHFS_METRICS_TYPES_H
diff --git a/src/include/cephfs/types.h b/src/include/cephfs/types.h
new file mode 100644
index 000000000..cca0a6193
--- /dev/null
+++ b/src/include/cephfs/types.h
@@ -0,0 +1,970 @@
+// -*- 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) 2020 Red Hat, Inc.
+ *
+ * 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_CEPHFS_TYPES_H
+#define CEPH_CEPHFS_TYPES_H
+#include "include/int_types.h"
+
+#include <ostream>
+#include <set>
+#include <map>
+#include <string_view>
+
+#include "common/config.h"
+#include "common/Clock.h"
+#include "common/DecayCounter.h"
+#include "common/StackStringStream.h"
+#include "common/entity_name.h"
+
+#include "include/compat.h"
+#include "include/Context.h"
+#include "include/frag.h"
+#include "include/xlist.h"
+#include "include/interval_set.h"
+#include "include/compact_set.h"
+#include "include/fs_types.h"
+#include "include/ceph_fs.h"
+
+#include "mds/inode_backtrace.h"
+
+#include <boost/spirit/include/qi.hpp>
+#include <boost/pool/pool.hpp>
+#include "include/ceph_assert.h"
+#include <boost/serialization/strong_typedef.hpp>
+#include "common/ceph_json.h"
+
+#define CEPH_FS_ONDISK_MAGIC "ceph fs volume v011"
+#define MAX_MDS 0x100
+
+BOOST_STRONG_TYPEDEF(uint64_t, mds_gid_t)
+extern const mds_gid_t MDS_GID_NONE;
+
+typedef int32_t fs_cluster_id_t;
+constexpr fs_cluster_id_t FS_CLUSTER_ID_NONE = -1;
+
+// The namespace ID of the anonymous default filesystem from legacy systems
+constexpr fs_cluster_id_t FS_CLUSTER_ID_ANONYMOUS = 0;
+
+typedef int32_t mds_rank_t;
+constexpr mds_rank_t MDS_RANK_NONE = -1;
+constexpr mds_rank_t MDS_RANK_EPHEMERAL_DIST = -2;
+constexpr mds_rank_t MDS_RANK_EPHEMERAL_RAND = -3;
+
+struct scatter_info_t {
+ version_t version = 0;
+};
+
+struct frag_info_t : public scatter_info_t {
+ int64_t size() const { return nfiles + nsubdirs; }
+
+ void zero() {
+ *this = frag_info_t();
+ }
+
+ // *this += cur - acc;
+ void add_delta(const frag_info_t &cur, const frag_info_t &acc, bool *touched_mtime=0, bool *touched_chattr=0) {
+ if (cur.mtime > mtime) {
+ mtime = cur.mtime;
+ if (touched_mtime)
+ *touched_mtime = true;
+ }
+ if (cur.change_attr > change_attr) {
+ change_attr = cur.change_attr;
+ if (touched_chattr)
+ *touched_chattr = true;
+ }
+ nfiles += cur.nfiles - acc.nfiles;
+ nsubdirs += cur.nsubdirs - acc.nsubdirs;
+ }
+
+ void add(const frag_info_t& other) {
+ if (other.mtime > mtime)
+ mtime = other.mtime;
+ if (other.change_attr > change_attr)
+ change_attr = other.change_attr;
+ nfiles += other.nfiles;
+ nsubdirs += other.nsubdirs;
+ }
+
+ bool same_sums(const frag_info_t &o) const {
+ return mtime <= o.mtime &&
+ nfiles == o.nfiles &&
+ nsubdirs == o.nsubdirs;
+ }
+
+ void encode(ceph::buffer::list &bl) const;
+ void decode(ceph::buffer::list::const_iterator& bl);
+ void dump(ceph::Formatter *f) const;
+ void decode_json(JSONObj *obj);
+ static void generate_test_instances(std::list<frag_info_t*>& ls);
+
+ // this frag
+ utime_t mtime;
+ uint64_t change_attr = 0;
+ int64_t nfiles = 0; // files
+ int64_t nsubdirs = 0; // subdirs
+};
+WRITE_CLASS_ENCODER(frag_info_t)
+
+inline bool operator==(const frag_info_t &l, const frag_info_t &r) {
+ return memcmp(&l, &r, sizeof(l)) == 0;
+}
+inline bool operator!=(const frag_info_t &l, const frag_info_t &r) {
+ return !(l == r);
+}
+
+std::ostream& operator<<(std::ostream &out, const frag_info_t &f);
+
+struct nest_info_t : public scatter_info_t {
+ int64_t rsize() const { return rfiles + rsubdirs; }
+
+ void zero() {
+ *this = nest_info_t();
+ }
+
+ void sub(const nest_info_t &other) {
+ add(other, -1);
+ }
+ void add(const nest_info_t &other, int fac=1) {
+ if (other.rctime > rctime)
+ rctime = other.rctime;
+ rbytes += fac*other.rbytes;
+ rfiles += fac*other.rfiles;
+ rsubdirs += fac*other.rsubdirs;
+ rsnaps += fac*other.rsnaps;
+ }
+
+ // *this += cur - acc;
+ void add_delta(const nest_info_t &cur, const nest_info_t &acc) {
+ if (cur.rctime > rctime)
+ rctime = cur.rctime;
+ rbytes += cur.rbytes - acc.rbytes;
+ rfiles += cur.rfiles - acc.rfiles;
+ rsubdirs += cur.rsubdirs - acc.rsubdirs;
+ rsnaps += cur.rsnaps - acc.rsnaps;
+ }
+
+ bool same_sums(const nest_info_t &o) const {
+ return rctime <= o.rctime &&
+ rbytes == o.rbytes &&
+ rfiles == o.rfiles &&
+ rsubdirs == o.rsubdirs &&
+ rsnaps == o.rsnaps;
+ }
+
+ void encode(ceph::buffer::list &bl) const;
+ void decode(ceph::buffer::list::const_iterator& bl);
+ void dump(ceph::Formatter *f) const;
+ void decode_json(JSONObj *obj);
+ static void generate_test_instances(std::list<nest_info_t*>& ls);
+
+ // this frag + children
+ utime_t rctime;
+ int64_t rbytes = 0;
+ int64_t rfiles = 0;
+ int64_t rsubdirs = 0;
+ int64_t rsnaps = 0;
+};
+WRITE_CLASS_ENCODER(nest_info_t)
+
+inline bool operator==(const nest_info_t &l, const nest_info_t &r) {
+ return memcmp(&l, &r, sizeof(l)) == 0;
+}
+inline bool operator!=(const nest_info_t &l, const nest_info_t &r) {
+ return !(l == r);
+}
+
+std::ostream& operator<<(std::ostream &out, const nest_info_t &n);
+
+struct vinodeno_t {
+ vinodeno_t() {}
+ vinodeno_t(inodeno_t i, snapid_t s) : ino(i), snapid(s) {}
+
+ void encode(ceph::buffer::list& bl) const {
+ using ceph::encode;
+ encode(ino, bl);
+ encode(snapid, bl);
+ }
+ void decode(ceph::buffer::list::const_iterator& p) {
+ using ceph::decode;
+ decode(ino, p);
+ decode(snapid, p);
+ }
+
+ inodeno_t ino;
+ snapid_t snapid;
+};
+WRITE_CLASS_ENCODER(vinodeno_t)
+
+inline bool operator==(const vinodeno_t &l, const vinodeno_t &r) {
+ return l.ino == r.ino && l.snapid == r.snapid;
+}
+inline bool operator!=(const vinodeno_t &l, const vinodeno_t &r) {
+ return !(l == r);
+}
+inline bool operator<(const vinodeno_t &l, const vinodeno_t &r) {
+ return
+ l.ino < r.ino ||
+ (l.ino == r.ino && l.snapid < r.snapid);
+}
+
+typedef enum {
+ QUOTA_MAX_FILES,
+ QUOTA_MAX_BYTES,
+ QUOTA_ANY
+} quota_max_t;
+
+struct quota_info_t
+{
+ void encode(ceph::buffer::list& bl) const {
+ ENCODE_START(1, 1, bl);
+ encode(max_bytes, bl);
+ encode(max_files, bl);
+ ENCODE_FINISH(bl);
+ }
+ void decode(ceph::buffer::list::const_iterator& p) {
+ DECODE_START_LEGACY_COMPAT_LEN(1, 1, 1, p);
+ decode(max_bytes, p);
+ decode(max_files, p);
+ DECODE_FINISH(p);
+ }
+
+ void dump(ceph::Formatter *f) const;
+ static void generate_test_instances(std::list<quota_info_t *>& ls);
+
+ bool is_valid() const {
+ return max_bytes >=0 && max_files >=0;
+ }
+ bool is_enabled(quota_max_t type=QUOTA_ANY) const {
+ switch (type) {
+ case QUOTA_MAX_FILES:
+ return !!max_files;
+ case QUOTA_MAX_BYTES:
+ return !!max_bytes;
+ case QUOTA_ANY:
+ default:
+ return !!max_bytes || !!max_files;
+ }
+ }
+ void decode_json(JSONObj *obj);
+
+ int64_t max_bytes = 0;
+ int64_t max_files = 0;
+};
+WRITE_CLASS_ENCODER(quota_info_t)
+
+inline bool operator==(const quota_info_t &l, const quota_info_t &r) {
+ return memcmp(&l, &r, sizeof(l)) == 0;
+}
+
+std::ostream& operator<<(std::ostream &out, const quota_info_t &n);
+
+struct client_writeable_range_t {
+ struct byte_range_t {
+ uint64_t first = 0, last = 0; // interval client can write to
+ byte_range_t() {}
+ void decode_json(JSONObj *obj);
+ };
+
+ void encode(ceph::buffer::list &bl) const;
+ void decode(ceph::buffer::list::const_iterator& bl);
+ void dump(ceph::Formatter *f) const;
+ static void generate_test_instances(std::list<client_writeable_range_t*>& ls);
+
+ byte_range_t range;
+ snapid_t follows = 0; // aka "data+metadata flushed thru"
+};
+
+inline void decode(client_writeable_range_t::byte_range_t& range, ceph::buffer::list::const_iterator& bl) {
+ using ceph::decode;
+ decode(range.first, bl);
+ decode(range.last, bl);
+}
+
+WRITE_CLASS_ENCODER(client_writeable_range_t)
+
+std::ostream& operator<<(std::ostream& out, const client_writeable_range_t& r);
+
+inline bool operator==(const client_writeable_range_t& l,
+ const client_writeable_range_t& r) {
+ return l.range.first == r.range.first && l.range.last == r.range.last &&
+ l.follows == r.follows;
+}
+
+struct inline_data_t {
+public:
+ inline_data_t() {}
+ inline_data_t(const inline_data_t& o) : version(o.version) {
+ if (o.blp)
+ set_data(*o.blp);
+ }
+ inline_data_t& operator=(const inline_data_t& o) {
+ version = o.version;
+ if (o.blp)
+ set_data(*o.blp);
+ else
+ free_data();
+ return *this;
+ }
+
+ void free_data() {
+ blp.reset();
+ }
+ void get_data(ceph::buffer::list& ret) const {
+ if (blp)
+ ret = *blp;
+ else
+ ret.clear();
+ }
+ void set_data(const ceph::buffer::list& bl) {
+ if (!blp)
+ blp.reset(new ceph::buffer::list);
+ *blp = bl;
+ }
+ size_t length() const { return blp ? blp->length() : 0; }
+
+ bool operator==(const inline_data_t& o) const {
+ return length() == o.length() &&
+ (length() == 0 ||
+ (*const_cast<ceph::buffer::list*>(blp.get()) == *const_cast<ceph::buffer::list*>(o.blp.get())));
+ }
+ bool operator!=(const inline_data_t& o) const {
+ return !(*this == o);
+ }
+ void encode(ceph::buffer::list &bl) const;
+ void decode(ceph::buffer::list::const_iterator& bl);
+
+ version_t version = 1;
+
+private:
+ std::unique_ptr<ceph::buffer::list> blp;
+};
+WRITE_CLASS_ENCODER(inline_data_t)
+
+enum {
+ DAMAGE_STATS, // statistics (dirstat, size, etc)
+ DAMAGE_RSTATS, // recursive statistics (rstat, accounted_rstat)
+ DAMAGE_FRAGTREE // fragtree -- repair by searching
+};
+
+template<template<typename> class Allocator = std::allocator>
+struct inode_t {
+ /**
+ * ***************
+ * Do not forget to add any new fields to the compare() function.
+ * ***************
+ */
+ using client_range_map = std::map<client_t,client_writeable_range_t,std::less<client_t>,Allocator<std::pair<const client_t,client_writeable_range_t>>>;
+
+ inode_t()
+ {
+ clear_layout();
+ }
+
+ // file type
+ bool is_symlink() const { return (mode & S_IFMT) == S_IFLNK; }
+ bool is_dir() const { return (mode & S_IFMT) == S_IFDIR; }
+ bool is_file() const { return (mode & S_IFMT) == S_IFREG; }
+
+ bool is_truncating() const { return (truncate_pending > 0); }
+ void truncate(uint64_t old_size, uint64_t new_size, const bufferlist &fbl) {
+ truncate(old_size, new_size);
+ fscrypt_last_block = fbl;
+ }
+ void truncate(uint64_t old_size, uint64_t new_size) {
+ ceph_assert(new_size <= old_size);
+ if (old_size > max_size_ever)
+ max_size_ever = old_size;
+ truncate_from = old_size;
+ size = new_size;
+ rstat.rbytes = new_size;
+ truncate_size = size;
+ truncate_seq++;
+ truncate_pending++;
+ }
+
+ bool has_layout() const {
+ return layout != file_layout_t();
+ }
+
+ void clear_layout() {
+ layout = file_layout_t();
+ }
+
+ uint64_t get_layout_size_increment() const {
+ return layout.get_period();
+ }
+
+ bool is_dirty_rstat() const { return !(rstat == accounted_rstat); }
+
+ uint64_t get_client_range(client_t client) const {
+ auto it = client_ranges.find(client);
+ return it != client_ranges.end() ? it->second.range.last : 0;
+ }
+
+ uint64_t get_max_size() const {
+ uint64_t max = 0;
+ for (std::map<client_t,client_writeable_range_t>::const_iterator p = client_ranges.begin();
+ p != client_ranges.end();
+ ++p)
+ if (p->second.range.last > max)
+ max = p->second.range.last;
+ return max;
+ }
+ void set_max_size(uint64_t new_max) {
+ if (new_max == 0) {
+ client_ranges.clear();
+ } else {
+ for (std::map<client_t,client_writeable_range_t>::iterator p = client_ranges.begin();
+ p != client_ranges.end();
+ ++p)
+ p->second.range.last = new_max;
+ }
+ }
+
+ void trim_client_ranges(snapid_t last) {
+ std::map<client_t, client_writeable_range_t>::iterator p = client_ranges.begin();
+ while (p != client_ranges.end()) {
+ if (p->second.follows >= last)
+ client_ranges.erase(p++);
+ else
+ ++p;
+ }
+ }
+
+ bool is_backtrace_updated() const {
+ return backtrace_version == version;
+ }
+ void update_backtrace(version_t pv=0) {
+ backtrace_version = pv ? pv : version;
+ }
+
+ void add_old_pool(int64_t l) {
+ backtrace_version = version;
+ old_pools.insert(l);
+ }
+
+ void encode(ceph::buffer::list &bl, uint64_t features) const;
+ void decode(ceph::buffer::list::const_iterator& bl);
+ void dump(ceph::Formatter *f) const;
+ static void client_ranges_cb(client_range_map& c, JSONObj *obj);
+ static void old_pools_cb(compact_set<int64_t, std::less<int64_t>, Allocator<int64_t> >& c, JSONObj *obj);
+ void decode_json(JSONObj *obj);
+ static void generate_test_instances(std::list<inode_t*>& ls);
+ /**
+ * Compare this inode_t with another that represent *the same inode*
+ * at different points in time.
+ * @pre The inodes are the same ino
+ *
+ * @param other The inode_t to compare ourselves with
+ * @param divergent A bool pointer which will be set to true
+ * if the values are different in a way that can't be explained
+ * by one being a newer version than the other.
+ *
+ * @returns 1 if we are newer than the other, 0 if equal, -1 if older.
+ */
+ int compare(const inode_t &other, bool *divergent) const;
+
+ // base (immutable)
+ inodeno_t ino = 0;
+ uint32_t rdev = 0; // if special file
+
+ // affected by any inode change...
+ utime_t ctime; // inode change time
+ utime_t btime; // birth time
+
+ // perm (namespace permissions)
+ uint32_t mode = 0;
+ uid_t uid = 0;
+ gid_t gid = 0;
+
+ // nlink
+ int32_t nlink = 0;
+
+ // file (data access)
+ ceph_dir_layout dir_layout = {}; // [dir only]
+ file_layout_t layout;
+ compact_set<int64_t, std::less<int64_t>, Allocator<int64_t>> old_pools;
+ uint64_t size = 0; // on directory, # dentries
+ uint64_t max_size_ever = 0; // max size the file has ever been
+ uint32_t truncate_seq = 0;
+ uint64_t truncate_size = 0, truncate_from = 0;
+ uint32_t truncate_pending = 0;
+ utime_t mtime; // file data modify time.
+ utime_t atime; // file data access time.
+ uint32_t time_warp_seq = 0; // count of (potential) mtime/atime timewarps (i.e., utimes())
+ inline_data_t inline_data; // FIXME check
+
+ // change attribute
+ uint64_t change_attr = 0;
+
+ client_range_map client_ranges; // client(s) can write to these ranges
+
+ // dirfrag, recursive accountin
+ frag_info_t dirstat; // protected by my filelock
+ nest_info_t rstat; // protected by my nestlock
+ nest_info_t accounted_rstat; // protected by parent's nestlock
+
+ quota_info_t quota;
+
+ mds_rank_t export_pin = MDS_RANK_NONE;
+
+ double export_ephemeral_random_pin = 0;
+ bool export_ephemeral_distributed_pin = false;
+
+ // special stuff
+ version_t version = 0; // auth only
+ version_t file_data_version = 0; // auth only
+ version_t xattr_version = 0;
+
+ utime_t last_scrub_stamp; // start time of last complete scrub
+ version_t last_scrub_version = 0;// (parent) start version of last complete scrub
+
+ version_t backtrace_version = 0;
+
+ snapid_t oldest_snap;
+
+ std::basic_string<char,std::char_traits<char>,Allocator<char>> stray_prior_path; //stores path before unlink
+
+ std::vector<uint8_t> fscrypt_auth;
+ std::vector<uint8_t> fscrypt_file;
+
+ bufferlist fscrypt_last_block;
+
+private:
+ bool older_is_consistent(const inode_t &other) const;
+};
+
+// These methods may be moved back to mdstypes.cc when we have pmr
+template<template<typename> class Allocator>
+void inode_t<Allocator>::encode(ceph::buffer::list &bl, uint64_t features) const
+{
+ ENCODE_START(19, 6, bl);
+
+ encode(ino, bl);
+ encode(rdev, bl);
+ encode(ctime, bl);
+
+ encode(mode, bl);
+ encode(uid, bl);
+ encode(gid, bl);
+
+ encode(nlink, bl);
+ {
+ // removed field
+ bool anchored = 0;
+ encode(anchored, bl);
+ }
+
+ encode(dir_layout, bl);
+ encode(layout, bl, features);
+ encode(size, bl);
+ encode(truncate_seq, bl);
+ encode(truncate_size, bl);
+ encode(truncate_from, bl);
+ encode(truncate_pending, bl);
+ encode(mtime, bl);
+ encode(atime, bl);
+ encode(time_warp_seq, bl);
+ encode(client_ranges, bl);
+
+ encode(dirstat, bl);
+ encode(rstat, bl);
+ encode(accounted_rstat, bl);
+
+ encode(version, bl);
+ encode(file_data_version, bl);
+ encode(xattr_version, bl);
+ encode(backtrace_version, bl);
+ encode(old_pools, bl);
+ encode(max_size_ever, bl);
+ encode(inline_data, bl);
+ encode(quota, bl);
+
+ encode(stray_prior_path, bl);
+
+ encode(last_scrub_version, bl);
+ encode(last_scrub_stamp, bl);
+
+ encode(btime, bl);
+ encode(change_attr, bl);
+
+ encode(export_pin, bl);
+
+ encode(export_ephemeral_random_pin, bl);
+ encode(export_ephemeral_distributed_pin, bl);
+
+ encode(!fscrypt_auth.empty(), bl);
+ encode(fscrypt_auth, bl);
+ encode(fscrypt_file, bl);
+ encode(fscrypt_last_block, bl);
+ ENCODE_FINISH(bl);
+}
+
+template<template<typename> class Allocator>
+void inode_t<Allocator>::decode(ceph::buffer::list::const_iterator &p)
+{
+ DECODE_START_LEGACY_COMPAT_LEN(19, 6, 6, p);
+
+ decode(ino, p);
+ decode(rdev, p);
+ decode(ctime, p);
+
+ decode(mode, p);
+ decode(uid, p);
+ decode(gid, p);
+
+ decode(nlink, p);
+ {
+ bool anchored;
+ decode(anchored, p);
+ }
+
+ if (struct_v >= 4)
+ decode(dir_layout, p);
+ else {
+ // FIPS zeroization audit 20191117: this memset is not security related.
+ memset(&dir_layout, 0, sizeof(dir_layout));
+ }
+ decode(layout, p);
+ decode(size, p);
+ decode(truncate_seq, p);
+ decode(truncate_size, p);
+ decode(truncate_from, p);
+ if (struct_v >= 5)
+ decode(truncate_pending, p);
+ else
+ truncate_pending = 0;
+ decode(mtime, p);
+ decode(atime, p);
+ decode(time_warp_seq, p);
+ if (struct_v >= 3) {
+ decode(client_ranges, p);
+ } else {
+ std::map<client_t, client_writeable_range_t::byte_range_t> m;
+ decode(m, p);
+ for (auto q = m.begin(); q != m.end(); ++q)
+ client_ranges[q->first].range = q->second;
+ }
+
+ decode(dirstat, p);
+ decode(rstat, p);
+ decode(accounted_rstat, p);
+
+ decode(version, p);
+ decode(file_data_version, p);
+ decode(xattr_version, p);
+ if (struct_v >= 2)
+ decode(backtrace_version, p);
+ if (struct_v >= 7)
+ decode(old_pools, p);
+ if (struct_v >= 8)
+ decode(max_size_ever, p);
+ if (struct_v >= 9) {
+ decode(inline_data, p);
+ } else {
+ inline_data.version = CEPH_INLINE_NONE;
+ }
+ if (struct_v < 10)
+ backtrace_version = 0; // force update backtrace
+ if (struct_v >= 11)
+ decode(quota, p);
+
+ if (struct_v >= 12) {
+ std::string tmp;
+ decode(tmp, p);
+ stray_prior_path = std::string_view(tmp);
+ }
+
+ if (struct_v >= 13) {
+ decode(last_scrub_version, p);
+ decode(last_scrub_stamp, p);
+ }
+ if (struct_v >= 14) {
+ decode(btime, p);
+ decode(change_attr, p);
+ } else {
+ btime = utime_t();
+ change_attr = 0;
+ }
+
+ if (struct_v >= 15) {
+ decode(export_pin, p);
+ } else {
+ export_pin = MDS_RANK_NONE;
+ }
+
+ if (struct_v >= 16) {
+ decode(export_ephemeral_random_pin, p);
+ decode(export_ephemeral_distributed_pin, p);
+ } else {
+ export_ephemeral_random_pin = 0;
+ export_ephemeral_distributed_pin = false;
+ }
+
+ if (struct_v >= 17) {
+ bool fscrypt_flag;
+ decode(fscrypt_flag, p); // ignored
+ }
+
+ if (struct_v >= 18) {
+ decode(fscrypt_auth, p);
+ decode(fscrypt_file, p);
+ }
+
+ if (struct_v >= 19) {
+ decode(fscrypt_last_block, p);
+ }
+ DECODE_FINISH(p);
+}
+
+template<template<typename> class Allocator>
+void inode_t<Allocator>::dump(ceph::Formatter *f) const
+{
+ f->dump_unsigned("ino", ino);
+ f->dump_unsigned("rdev", rdev);
+ f->dump_stream("ctime") << ctime;
+ f->dump_stream("btime") << btime;
+ f->dump_unsigned("mode", mode);
+ f->dump_unsigned("uid", uid);
+ f->dump_unsigned("gid", gid);
+ f->dump_unsigned("nlink", nlink);
+
+ f->open_object_section("dir_layout");
+ ::dump(dir_layout, f);
+ f->close_section();
+
+ f->dump_object("layout", layout);
+
+ f->open_array_section("old_pools");
+ for (const auto &p : old_pools) {
+ f->dump_int("pool", p);
+ }
+ f->close_section();
+
+ f->dump_unsigned("size", size);
+ f->dump_unsigned("truncate_seq", truncate_seq);
+ f->dump_unsigned("truncate_size", truncate_size);
+ f->dump_unsigned("truncate_from", truncate_from);
+ f->dump_unsigned("truncate_pending", truncate_pending);
+ f->dump_stream("mtime") << mtime;
+ f->dump_stream("atime") << atime;
+ f->dump_unsigned("time_warp_seq", time_warp_seq);
+ f->dump_unsigned("change_attr", change_attr);
+ f->dump_int("export_pin", export_pin);
+ f->dump_int("export_ephemeral_random_pin", export_ephemeral_random_pin);
+ f->dump_bool("export_ephemeral_distributed_pin", export_ephemeral_distributed_pin);
+
+ f->open_array_section("client_ranges");
+ for (const auto &p : client_ranges) {
+ f->open_object_section("client");
+ f->dump_unsigned("client", p.first.v);
+ p.second.dump(f);
+ f->close_section();
+ }
+ f->close_section();
+
+ f->open_object_section("dirstat");
+ dirstat.dump(f);
+ f->close_section();
+
+ f->open_object_section("rstat");
+ rstat.dump(f);
+ f->close_section();
+
+ f->open_object_section("accounted_rstat");
+ accounted_rstat.dump(f);
+ f->close_section();
+
+ f->dump_unsigned("version", version);
+ f->dump_unsigned("file_data_version", file_data_version);
+ f->dump_unsigned("xattr_version", xattr_version);
+ f->dump_unsigned("backtrace_version", backtrace_version);
+
+ f->dump_string("stray_prior_path", stray_prior_path);
+ f->dump_unsigned("max_size_ever", max_size_ever);
+
+ f->open_object_section("quota");
+ quota.dump(f);
+ f->close_section();
+
+ f->dump_stream("last_scrub_stamp") << last_scrub_stamp;
+ f->dump_unsigned("last_scrub_version", last_scrub_version);
+}
+
+template<template<typename> class Allocator>
+void inode_t<Allocator>::client_ranges_cb(typename inode_t<Allocator>::client_range_map& c, JSONObj *obj){
+
+ int64_t client;
+ JSONDecoder::decode_json("client", client, obj, true);
+ client_writeable_range_t client_range_tmp;
+ JSONDecoder::decode_json("byte range", client_range_tmp.range, obj, true);
+ JSONDecoder::decode_json("follows", client_range_tmp.follows.val, obj, true);
+ c[client] = client_range_tmp;
+}
+
+template<template<typename> class Allocator>
+void inode_t<Allocator>::old_pools_cb(compact_set<int64_t, std::less<int64_t>, Allocator<int64_t> >& c, JSONObj *obj){
+
+ int64_t tmp;
+ decode_json_obj(tmp, obj);
+ c.insert(tmp);
+}
+
+template<template<typename> class Allocator>
+void inode_t<Allocator>::decode_json(JSONObj *obj)
+{
+
+ JSONDecoder::decode_json("ino", ino.val, obj, true);
+ JSONDecoder::decode_json("rdev", rdev, obj, true);
+ //JSONDecoder::decode_json("ctime", ctime, obj, true);
+ //JSONDecoder::decode_json("btime", btime, obj, true);
+ JSONDecoder::decode_json("mode", mode, obj, true);
+ JSONDecoder::decode_json("uid", uid, obj, true);
+ JSONDecoder::decode_json("gid", gid, obj, true);
+ JSONDecoder::decode_json("nlink", nlink, obj, true);
+ JSONDecoder::decode_json("dir_layout", dir_layout, obj, true);
+ JSONDecoder::decode_json("layout", layout, obj, true);
+ JSONDecoder::decode_json("old_pools", old_pools, inode_t<Allocator>::old_pools_cb, obj, true);
+ JSONDecoder::decode_json("size", size, obj, true);
+ JSONDecoder::decode_json("truncate_seq", truncate_seq, obj, true);
+ JSONDecoder::decode_json("truncate_size", truncate_size, obj, true);
+ JSONDecoder::decode_json("truncate_from", truncate_from, obj, true);
+ JSONDecoder::decode_json("truncate_pending", truncate_pending, obj, true);
+ //JSONDecoder::decode_json("mtime", mtime, obj, true);
+ //JSONDecoder::decode_json("atime", atime, obj, true);
+ JSONDecoder::decode_json("time_warp_seq", time_warp_seq, obj, true);
+ JSONDecoder::decode_json("change_attr", change_attr, obj, true);
+ JSONDecoder::decode_json("export_pin", export_pin, obj, true);
+ JSONDecoder::decode_json("client_ranges", client_ranges, inode_t<Allocator>::client_ranges_cb, obj, true);
+ JSONDecoder::decode_json("dirstat", dirstat, obj, true);
+ JSONDecoder::decode_json("rstat", rstat, obj, true);
+ JSONDecoder::decode_json("accounted_rstat", accounted_rstat, obj, true);
+ JSONDecoder::decode_json("version", version, obj, true);
+ JSONDecoder::decode_json("file_data_version", file_data_version, obj, true);
+ JSONDecoder::decode_json("xattr_version", xattr_version, obj, true);
+ JSONDecoder::decode_json("backtrace_version", backtrace_version, obj, true);
+ JSONDecoder::decode_json("stray_prior_path", stray_prior_path, obj, true);
+ JSONDecoder::decode_json("max_size_ever", max_size_ever, obj, true);
+ JSONDecoder::decode_json("quota", quota, obj, true);
+ JSONDecoder::decode_json("last_scrub_stamp", last_scrub_stamp, obj, true);
+ JSONDecoder::decode_json("last_scrub_version", last_scrub_version, obj, true);
+}
+
+template<template<typename> class Allocator>
+void inode_t<Allocator>::generate_test_instances(std::list<inode_t*>& ls)
+{
+ ls.push_back(new inode_t<Allocator>);
+ ls.push_back(new inode_t<Allocator>);
+ ls.back()->ino = 1;
+ // i am lazy.
+}
+
+template<template<typename> class Allocator>
+int inode_t<Allocator>::compare(const inode_t<Allocator> &other, bool *divergent) const
+{
+ ceph_assert(ino == other.ino);
+ *divergent = false;
+ if (version == other.version) {
+ if (rdev != other.rdev ||
+ ctime != other.ctime ||
+ btime != other.btime ||
+ mode != other.mode ||
+ uid != other.uid ||
+ gid != other.gid ||
+ nlink != other.nlink ||
+ memcmp(&dir_layout, &other.dir_layout, sizeof(dir_layout)) ||
+ layout != other.layout ||
+ old_pools != other.old_pools ||
+ size != other.size ||
+ max_size_ever != other.max_size_ever ||
+ truncate_seq != other.truncate_seq ||
+ truncate_size != other.truncate_size ||
+ truncate_from != other.truncate_from ||
+ truncate_pending != other.truncate_pending ||
+ change_attr != other.change_attr ||
+ mtime != other.mtime ||
+ atime != other.atime ||
+ time_warp_seq != other.time_warp_seq ||
+ inline_data != other.inline_data ||
+ client_ranges != other.client_ranges ||
+ !(dirstat == other.dirstat) ||
+ !(rstat == other.rstat) ||
+ !(accounted_rstat == other.accounted_rstat) ||
+ file_data_version != other.file_data_version ||
+ xattr_version != other.xattr_version ||
+ backtrace_version != other.backtrace_version) {
+ *divergent = true;
+ }
+ return 0;
+ } else if (version > other.version) {
+ *divergent = !older_is_consistent(other);
+ return 1;
+ } else {
+ ceph_assert(version < other.version);
+ *divergent = !other.older_is_consistent(*this);
+ return -1;
+ }
+}
+
+template<template<typename> class Allocator>
+bool inode_t<Allocator>::older_is_consistent(const inode_t<Allocator> &other) const
+{
+ if (max_size_ever < other.max_size_ever ||
+ truncate_seq < other.truncate_seq ||
+ time_warp_seq < other.time_warp_seq ||
+ inline_data.version < other.inline_data.version ||
+ dirstat.version < other.dirstat.version ||
+ rstat.version < other.rstat.version ||
+ accounted_rstat.version < other.accounted_rstat.version ||
+ file_data_version < other.file_data_version ||
+ xattr_version < other.xattr_version ||
+ backtrace_version < other.backtrace_version) {
+ return false;
+ }
+ return true;
+}
+
+template<template<typename> class Allocator>
+inline void encode(const inode_t<Allocator> &c, ::ceph::buffer::list &bl, uint64_t features)
+{
+ ENCODE_DUMP_PRE();
+ c.encode(bl, features);
+ ENCODE_DUMP_POST(cl);
+}
+template<template<typename> class Allocator>
+inline void decode(inode_t<Allocator> &c, ::ceph::buffer::list::const_iterator &p)
+{
+ c.decode(p);
+}
+
+// parse a map of keys/values.
+namespace qi = boost::spirit::qi;
+
+template <typename Iterator>
+struct keys_and_values
+ : qi::grammar<Iterator, std::map<std::string, std::string>()>
+{
+ keys_and_values()
+ : keys_and_values::base_type(query)
+ {
+ query = pair >> *(qi::lit(' ') >> pair);
+ pair = key >> '=' >> value;
+ key = qi::char_("a-zA-Z_") >> *qi::char_("a-zA-Z_0-9");
+ value = +qi::char_("a-zA-Z0-9-_.");
+ }
+ qi::rule<Iterator, std::map<std::string, std::string>()> query;
+ qi::rule<Iterator, std::pair<std::string, std::string>()> pair;
+ qi::rule<Iterator, std::string()> key, value;
+};
+
+#endif
diff --git a/src/include/color.h b/src/include/color.h
new file mode 100644
index 000000000..6c8df40e0
--- /dev/null
+++ b/src/include/color.h
@@ -0,0 +1,13 @@
+#ifndef CEPH_COLOR_H
+#define CEPH_COLOR_H
+
+#define TEXT_NORMAL "\033[0m"
+/*#define TEXT_HAZARD "\033[5;31m"*/
+#define TEXT_RED "\033[0;31m"
+#define TEXT_GREEN "\033[0;32m"
+#define TEXT_YELLOW "\033[0;33m"
+#define TEXT_BLUE "\033[0;34m"
+#define TEXT_MAGENTA "\033[0;35m"
+#define TEXT_CYAN "\033[0;36m"
+
+#endif
diff --git a/src/include/common_fwd.h b/src/include/common_fwd.h
new file mode 100644
index 000000000..d906aadfa
--- /dev/null
+++ b/src/include/common_fwd.h
@@ -0,0 +1,32 @@
+#pragma once
+
+#if defined(WITH_SEASTAR) && !defined(WITH_ALIEN)
+#define TOPNSPC crimson
+#else
+#define TOPNSPC ceph
+#endif
+
+namespace TOPNSPC::common {
+ class CephContext;
+ class PerfCounters;
+ class PerfCountersBuilder;
+ class PerfCountersCollection;
+ class PerfCountersCollectionImpl;
+ class PerfGuard;
+ class RefCountedObject;
+ class RefCountedObjectSafe;
+ class RefCountedCond;
+ class RefCountedWaitObject;
+ class ConfigProxy;
+}
+using TOPNSPC::common::CephContext;
+using TOPNSPC::common::PerfCounters;
+using TOPNSPC::common::PerfCountersBuilder;
+using TOPNSPC::common::PerfCountersCollection;
+using TOPNSPC::common::PerfCountersCollectionImpl;
+using TOPNSPC::common::PerfGuard;
+using TOPNSPC::common::RefCountedObject;
+using TOPNSPC::common::RefCountedObjectSafe;
+using TOPNSPC::common::RefCountedCond;
+using TOPNSPC::common::RefCountedWaitObject;
+using TOPNSPC::common::ConfigProxy;
diff --git a/src/include/compact_map.h b/src/include/compact_map.h
new file mode 100644
index 000000000..21645e3d1
--- /dev/null
+++ b/src/include/compact_map.h
@@ -0,0 +1,383 @@
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2015 Red Hat, Inc
+ *
+ * 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_COMPACT_MAP_H
+#define CEPH_COMPACT_MAP_H
+
+#include "buffer.h"
+#include "encoding.h"
+
+#include <map>
+#include <memory>
+
+#include "include/encoding.h"
+
+template <class Key, class T, class Map>
+class compact_map_base {
+protected:
+ std::unique_ptr<Map> map;
+ void alloc_internal() {
+ if (!map)
+ map.reset(new Map);
+ }
+ void free_internal() {
+ map.reset();
+ }
+ template <class It>
+ class const_iterator_base {
+ const compact_map_base *map;
+ It it;
+ const_iterator_base() : map(0) { }
+ const_iterator_base(const compact_map_base* m) : map(m) { }
+ const_iterator_base(const compact_map_base *m, const It& i) : map(m), it(i) { }
+ friend class compact_map_base;
+ friend class iterator_base;
+ public:
+ const_iterator_base(const const_iterator_base& o) {
+ map = o.map;
+ it = o.it;
+ }
+ bool operator==(const const_iterator_base& o) const {
+ return (map == o.map) && (!map->map || it == o.it);
+ }
+ bool operator!=(const const_iterator_base& o) const {
+ return !(*this == o);;
+ }
+ const_iterator_base& operator=(const const_iterator_base& o) {
+ map = o.map;
+ it = o.it;
+ return *this;
+ }
+ const_iterator_base& operator++() {
+ ++it;
+ return *this;
+ }
+ const_iterator_base& operator--() {
+ --it;
+ return *this;
+ }
+ const std::pair<const Key,T>& operator*() {
+ return *it;
+ }
+ const std::pair<const Key,T>* operator->() {
+ return it.operator->();
+ }
+ };
+ template <class It>
+ class iterator_base {
+ private:
+ const compact_map_base* map;
+ It it;
+ iterator_base() : map(0) { }
+ iterator_base(compact_map_base* m) : map(m) { }
+ iterator_base(compact_map_base* m, const It& i) : map(m), it(i) { }
+ friend class compact_map_base;
+ public:
+ iterator_base(const iterator_base& o) {
+ map = o.map;
+ it = o.it;
+ }
+ bool operator==(const iterator_base& o) const {
+ return (map == o.map) && (!map->map || it == o.it);
+ }
+ bool operator!=(const iterator_base& o) const {
+ return !(*this == o);;
+ }
+ iterator_base& operator=(const iterator_base& o) {
+ map = o.map;
+ it = o.it;
+ return *this;
+ }
+ iterator_base& operator++() {
+ ++it;
+ return *this;
+ }
+ iterator_base operator++(int) {
+ iterator_base tmp = *this;
+ ++it;
+ return tmp;
+ }
+ iterator_base& operator--() {
+ --it;
+ return *this;
+ }
+ std::pair<const Key,T>& operator*() {
+ return *it;
+ }
+ std::pair<const Key,T>* operator->() {
+ return it.operator->();
+ }
+ operator const_iterator_base<It>() const {
+ return const_iterator_base<It>(map, it);
+ }
+ };
+
+public:
+ class iterator : public iterator_base<typename Map::iterator> {
+ public:
+ iterator() { }
+ iterator(const iterator_base<typename Map::iterator>& o)
+ : iterator_base<typename Map::iterator>(o) { }
+ iterator(compact_map_base* m) : iterator_base<typename Map::iterator>(m) { }
+ iterator(compact_map_base* m, const typename Map::iterator& i)
+ : iterator_base<typename Map::iterator>(m, i) { }
+ };
+ class const_iterator : public const_iterator_base<typename Map::const_iterator> {
+ public:
+ const_iterator() { }
+ const_iterator(const iterator_base<typename Map::const_iterator>& o)
+ : const_iterator_base<typename Map::const_iterator>(o) { }
+ const_iterator(const compact_map_base* m) : const_iterator_base<typename Map::const_iterator>(m) { }
+ const_iterator(const compact_map_base* m, const typename Map::const_iterator& i)
+ : const_iterator_base<typename Map::const_iterator>(m, i) { }
+ };
+ class reverse_iterator : public iterator_base<typename Map::reverse_iterator> {
+ public:
+ reverse_iterator() { }
+ reverse_iterator(const iterator_base<typename Map::reverse_iterator>& o)
+ : iterator_base<typename Map::reverse_iterator>(o) { }
+ reverse_iterator(compact_map_base* m) : iterator_base<typename Map::reverse_iterator>(m) { }
+ reverse_iterator(compact_map_base* m, const typename Map::reverse_iterator& i)
+ : iterator_base<typename Map::reverse_iterator>(m, i) { }
+ };
+ class const_reverse_iterator : public const_iterator_base<typename Map::const_reverse_iterator> {
+ public:
+ const_reverse_iterator() { }
+ const_reverse_iterator(const iterator_base<typename Map::const_reverse_iterator>& o)
+ : iterator_base<typename Map::const_reverse_iterator>(o) { }
+ const_reverse_iterator(const compact_map_base* m) : const_iterator_base<typename Map::const_reverse_iterator>(m) { }
+ const_reverse_iterator(const compact_map_base* m, const typename Map::const_reverse_iterator& i)
+ : const_iterator_base<typename Map::const_reverse_iterator>(m, i) { }
+ };
+ compact_map_base(const compact_map_base& o) {
+ if (o.map) {
+ alloc_internal();
+ *map = *o.map;
+ }
+ }
+ compact_map_base() {}
+ ~compact_map_base() {}
+
+ bool empty() const {
+ return !map || map->empty();
+ }
+ size_t size() const {
+ return map ? map->size() : 0;
+ }
+ bool operator==(const compact_map_base& o) const {
+ return (empty() && o.empty()) || (map && o.map && *map == *o.map);
+ }
+ bool operator!=(const compact_map_base& o) const {
+ return !(*this == o);
+ }
+ size_t count (const Key& k) const {
+ return map ? map->count(k) : 0;
+ }
+ iterator erase (iterator p) {
+ if (map) {
+ ceph_assert(this == p.map);
+ auto it = map->erase(p.it);
+ if (map->empty()) {
+ free_internal();
+ return iterator(this);
+ } else {
+ return iterator(this, it);
+ }
+ } else {
+ return iterator(this);
+ }
+ }
+ size_t erase (const Key& k) {
+ if (!map)
+ return 0;
+ size_t r = map->erase(k);
+ if (map->empty())
+ free_internal();
+ return r;
+ }
+ void clear() {
+ free_internal();
+ }
+ void swap(compact_map_base& o) {
+ map.swap(o.map);
+ }
+ compact_map_base& operator=(const compact_map_base& o) {
+ if (o.map) {
+ alloc_internal();
+ *map = *o.map;
+ } else
+ free_internal();
+ return *this;
+ }
+ iterator insert(const std::pair<const Key, T>& val) {
+ alloc_internal();
+ return iterator(this, map->insert(val));
+ }
+ template <class... Args>
+ std::pair<iterator,bool> emplace ( Args&&... args ) {
+ alloc_internal();
+ auto em = map->emplace(std::forward<Args>(args)...);
+ return std::pair<iterator,bool>(iterator(this, em.first), em.second);
+ }
+ iterator begin() {
+ if (!map)
+ return iterator(this);
+ return iterator(this, map->begin());
+ }
+ iterator end() {
+ if (!map)
+ return iterator(this);
+ return iterator(this, map->end());
+ }
+ reverse_iterator rbegin() {
+ if (!map)
+ return reverse_iterator(this);
+ return reverse_iterator(this, map->rbegin());
+ }
+ reverse_iterator rend() {
+ if (!map)
+ return reverse_iterator(this);
+ return reverse_iterator(this, map->rend());
+ }
+ iterator find(const Key& k) {
+ if (!map)
+ return iterator(this);
+ return iterator(this, map->find(k));
+ }
+ iterator lower_bound(const Key& k) {
+ if (!map)
+ return iterator(this);
+ return iterator(this, map->lower_bound(k));
+ }
+ iterator upper_bound(const Key& k) {
+ if (!map)
+ return iterator(this);
+ return iterator(this, map->upper_bound(k));
+ }
+ const_iterator begin() const {
+ if (!map)
+ return const_iterator(this);
+ return const_iterator(this, map->begin());
+ }
+ const_iterator end() const {
+ if (!map)
+ return const_iterator(this);
+ return const_iterator(this, map->end());
+ }
+ const_reverse_iterator rbegin() const {
+ if (!map)
+ return const_reverse_iterator(this);
+ return const_reverse_iterator(this, map->rbegin());
+ }
+ const_reverse_iterator rend() const {
+ if (!map)
+ return const_reverse_iterator(this);
+ return const_reverse_iterator(this, map->rend());
+ }
+ const_iterator find(const Key& k) const {
+ if (!map)
+ return const_iterator(this);
+ return const_iterator(this, map->find(k));
+ }
+ const_iterator lower_bound(const Key& k) const {
+ if (!map)
+ return const_iterator(this);
+ return const_iterator(this, map->lower_bound(k));
+ }
+ const_iterator upper_bound(const Key& k) const {
+ if (!map)
+ return const_iterator(this);
+ return const_iterator(this, map->upper_bound(k));
+ }
+ void encode(ceph::buffer::list &bl) const {
+ using ceph::encode;
+ if (map)
+ encode(*map, bl);
+ else
+ encode((uint32_t)0, bl);
+ }
+ void encode(ceph::buffer::list &bl, uint64_t features) const {
+ using ceph::encode;
+ if (map)
+ encode(*map, bl, features);
+ else
+ encode((uint32_t)0, bl);
+ }
+ void decode(ceph::buffer::list::const_iterator& p) {
+ using ceph::decode;
+ using ceph::decode_nohead;
+ uint32_t n;
+ decode(n, p);
+ if (n > 0) {
+ alloc_internal();
+ decode_nohead(n, *map, p);
+ } else
+ free_internal();
+ }
+};
+
+template<class Key, class T, class Map>
+inline void encode(const compact_map_base<Key, T, Map>& m, ceph::buffer::list& bl) {
+ m.encode(bl);
+}
+template<class Key, class T, class Map>
+inline void encode(const compact_map_base<Key, T, Map>& m, ceph::buffer::list& bl,
+ uint64_t features) {
+ m.encode(bl, features);
+}
+template<class Key, class T, class Map>
+inline void decode(compact_map_base<Key, T, Map>& m, ceph::buffer::list::const_iterator& p) {
+ m.decode(p);
+}
+
+template <class Key, class T, class Compare = std::less<Key>, class Alloc = std::allocator< std::pair<const Key, T> > >
+class compact_map : public compact_map_base<Key, T, std::map<Key,T,Compare,Alloc> > {
+public:
+ T& operator[](const Key& k) {
+ this->alloc_internal();
+ return (*(this->map))[k];
+ }
+};
+
+template <class Key, class T, class Compare = std::less<Key>, class Alloc = std::allocator< std::pair<const Key, T> > >
+inline std::ostream& operator<<(std::ostream& out, const compact_map<Key, T, Compare, Alloc>& m)
+{
+ out << "{";
+ bool first = true;
+ for (const auto &p : m) {
+ if (!first)
+ out << ",";
+ out << p.first << "=" << p.second;
+ first = false;
+ }
+ out << "}";
+ return out;
+}
+
+template <class Key, class T, class Compare = std::less<Key>, class Alloc = std::allocator< std::pair<const Key, T> > >
+class compact_multimap : public compact_map_base<Key, T, std::multimap<Key,T,Compare,Alloc> > {
+};
+
+template <class Key, class T, class Compare = std::less<Key>, class Alloc = std::allocator< std::pair<const Key, T> > >
+inline std::ostream& operator<<(std::ostream& out, const compact_multimap<Key, T, Compare, Alloc>& m)
+{
+ out << "{{";
+ bool first = true;
+ for (const auto &p : m) {
+ if (!first)
+ out << ",";
+ out << p.first << "=" << p.second;
+ first = false;
+ }
+ out << "}}";
+ return out;
+}
+#endif
diff --git a/src/include/compact_set.h b/src/include/compact_set.h
new file mode 100644
index 000000000..a364fd8c4
--- /dev/null
+++ b/src/include/compact_set.h
@@ -0,0 +1,305 @@
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2015 Red Hat, Inc
+ *
+ * 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_COMPACT_SET_H
+#define CEPH_COMPACT_SET_H
+
+#include "buffer.h"
+#include "encoding.h"
+
+#include <memory>
+#include <set>
+
+template <class T, class Set>
+class compact_set_base {
+protected:
+ std::unique_ptr<Set> set;
+ void alloc_internal() {
+ if (!set)
+ set.reset(new Set);
+ }
+ void free_internal() {
+ set.reset();
+ }
+ template <class It>
+ class iterator_base {
+ private:
+ const compact_set_base* set;
+ It it;
+ iterator_base() : set(0) { }
+ iterator_base(const compact_set_base* s) : set(s) { }
+ iterator_base(const compact_set_base* s, const It& i) : set(s), it(i) { }
+ friend class compact_set_base;
+ public:
+ iterator_base(const iterator_base& o) {
+ set = o.set;
+ it = o.it;
+ }
+ bool operator==(const iterator_base& o) const {
+ return (set == o.set) && (!set->set || it == o.it);
+ }
+ bool operator!=(const iterator_base& o) const {
+ return !(*this == o);;
+ }
+ iterator_base& operator=(const iterator_base& o) {
+ set->set = o.set;
+ it = o.it;
+ return *this;
+ }
+ iterator_base& operator++() {
+ ++it;
+ return *this;
+ }
+ iterator_base operator++(int) {
+ iterator_base tmp = *this;
+ ++it;
+ return tmp;
+ }
+ iterator_base& operator--() {
+ --it;
+ return *this;
+ }
+ const T& operator*() {
+ return *it;
+ }
+ };
+public:
+ class const_iterator : public iterator_base<typename Set::const_iterator> {
+ public:
+ const_iterator() { }
+ const_iterator(const iterator_base<typename Set::const_iterator>& o)
+ : iterator_base<typename Set::const_iterator>(o) { }
+ const_iterator(const compact_set_base* s) : iterator_base<typename Set::const_iterator>(s) { }
+ const_iterator(const compact_set_base* s, const typename Set::const_iterator& i)
+ : iterator_base<typename Set::const_iterator>(s, i) { }
+ };
+ class iterator : public iterator_base<typename Set::iterator> {
+ public:
+ iterator() { }
+ iterator(const iterator_base<typename Set::iterator>& o)
+ : iterator_base<typename Set::iterator>(o) { }
+ iterator(compact_set_base* s) : iterator_base<typename Set::iterator>(s) { }
+ iterator(compact_set_base* s, const typename Set::iterator& i)
+ : iterator_base<typename Set::iterator>(s, i) { }
+ operator const_iterator() const {
+ return const_iterator(this->set, this->it);
+ }
+ };
+ class const_reverse_iterator : public iterator_base<typename Set::const_reverse_iterator> {
+ public:
+ const_reverse_iterator() { }
+ const_reverse_iterator(const iterator_base<typename Set::const_reverse_iterator>& o)
+ : iterator_base<typename Set::const_reverse_iterator>(o) { }
+ const_reverse_iterator(const compact_set_base* s) : iterator_base<typename Set::const_reverse_iterator>(s) { }
+ const_reverse_iterator(const compact_set_base* s, const typename Set::const_reverse_iterator& i)
+ : iterator_base<typename Set::const_reverse_iterator>(s, i) { }
+ };
+ class reverse_iterator : public iterator_base<typename Set::reverse_iterator> {
+ public:
+ reverse_iterator() { }
+ reverse_iterator(const iterator_base<typename Set::reverse_iterator>& o)
+ : iterator_base<typename Set::reverse_iterator>(o) { }
+ reverse_iterator(compact_set_base* s) : iterator_base<typename Set::reverse_iterator>(s) { }
+ reverse_iterator(compact_set_base* s, const typename Set::reverse_iterator& i)
+ : iterator_base<typename Set::reverse_iterator>(s, i) { }
+ operator const_iterator() const {
+ return const_iterator(this->set, this->it);
+ }
+ };
+
+ compact_set_base() {}
+ compact_set_base(const compact_set_base& o) {
+ if (o.set) {
+ alloc_internal();
+ *set = *o.set;
+ }
+ }
+ ~compact_set_base() {}
+
+
+ bool empty() const {
+ return !set || set->empty();
+ }
+ size_t size() const {
+ return set ? set->size() : 0;
+ }
+ bool operator==(const compact_set_base& o) const {
+ return (empty() && o.empty()) || (set && o.set && *set == *o.set);
+ }
+ bool operator!=(const compact_set_base& o) const {
+ return !(*this == o);
+ }
+ size_t count(const T& t) const {
+ return set ? set->count(t) : 0;
+ }
+ iterator erase (iterator p) {
+ if (set) {
+ ceph_assert(this == p.set);
+ auto it = set->erase(p.it);
+ if (set->empty()) {
+ free_internal();
+ return iterator(this);
+ } else {
+ return iterator(this, it);
+ }
+ } else {
+ return iterator(this);
+ }
+ }
+ size_t erase (const T& t) {
+ if (!set)
+ return 0;
+ size_t r = set->erase(t);
+ if (set->empty())
+ free_internal();
+ return r;
+ }
+ void clear() {
+ free_internal();
+ }
+ void swap(compact_set_base& o) {
+ set.swap(o.set);
+ }
+ compact_set_base& operator=(const compact_set_base& o) {
+ if (o.set) {
+ alloc_internal();
+ *set = *o.set;
+ } else
+ free_internal();
+ return *this;
+ }
+ std::pair<iterator,bool> insert(const T& t) {
+ alloc_internal();
+ std::pair<typename Set::iterator,bool> r = set->insert(t);
+ return std::make_pair(iterator(this, r.first), r.second);
+ }
+ template <class... Args>
+ std::pair<iterator,bool> emplace ( Args&&... args ) {
+ alloc_internal();
+ auto em = set->emplace(std::forward<Args>(args)...);
+ return std::pair<iterator,bool>(iterator(this, em.first), em.second);
+ }
+
+ iterator begin() {
+ if (!set)
+ return iterator(this);
+ return iterator(this, set->begin());
+ }
+ iterator end() {
+ if (!set)
+ return iterator(this);
+ return iterator(this, set->end());
+ }
+ reverse_iterator rbegin() {
+ if (!set)
+ return reverse_iterator(this);
+ return reverse_iterator(this, set->rbegin());
+ }
+ reverse_iterator rend() {
+ if (!set)
+ return reverse_iterator(this);
+ return reverse_iterator(this, set->rend());
+ }
+ iterator find(const T& t) {
+ if (!set)
+ return iterator(this);
+ return iterator(this, set->find(t));
+ }
+ iterator lower_bound(const T& t) {
+ if (!set)
+ return iterator(this);
+ return iterator(this, set->lower_bound(t));
+ }
+ iterator upper_bound(const T& t) {
+ if (!set)
+ return iterator(this);
+ return iterator(this, set->upper_bound(t));
+ }
+ const_iterator begin() const {
+ if (!set)
+ return const_iterator(this);
+ return const_iterator(this, set->begin());
+ }
+ const_iterator end() const {
+ if (!set)
+ return const_iterator(this);
+ return const_iterator(this, set->end());
+ }
+ const_reverse_iterator rbegin() const {
+ if (!set)
+ return const_reverse_iterator(this);
+ return const_reverse_iterator(this, set->rbegin());
+ }
+ const_reverse_iterator rend() const {
+ if (!set)
+ return const_reverse_iterator(this);
+ return const_reverse_iterator(this, set->rend());
+ }
+ const_iterator find(const T& t) const {
+ if (!set)
+ return const_iterator(this);
+ return const_iterator(this, set->find(t));
+ }
+ const_iterator lower_bound(const T& t) const {
+ if (!set)
+ return const_iterator(this);
+ return const_iterator(this, set->lower_bound(t));
+ }
+ const_iterator upper_bound(const T& t) const {
+ if (!set)
+ return const_iterator(this);
+ return const_iterator(this, set->upper_bound(t));
+ }
+ void encode(ceph::buffer::list &bl) const {
+ using ceph::encode;
+ if (set)
+ encode(*set, bl);
+ else
+ encode((uint32_t)0, bl);
+ }
+ void decode(ceph::buffer::list::const_iterator& p) {
+ using ceph::decode;
+ uint32_t n;
+ decode(n, p);
+ if (n > 0) {
+ alloc_internal();
+ ceph::decode_nohead(n, *set, p);
+ } else
+ free_internal();
+ }
+};
+
+template<class T, class Set>
+inline void encode(const compact_set_base<T, Set>& m, ceph::buffer::list& bl) {
+ m.encode(bl);
+}
+template<class T, class Set>
+inline void decode(compact_set_base<T, Set>& m, ceph::buffer::list::const_iterator& p) {
+ m.decode(p);
+}
+
+template <class T, class Compare = std::less<T>, class Alloc = std::allocator<T> >
+class compact_set : public compact_set_base<T, std::set<T, Compare, Alloc> > {
+};
+
+template <class T, class Compare = std::less<T>, class Alloc = std::allocator<T> >
+inline std::ostream& operator<<(std::ostream& out, const compact_set<T,Compare,Alloc>& s)
+{
+ bool first = true;
+ for (auto &v : s) {
+ if (!first)
+ out << ",";
+ out << v;
+ first = false;
+ }
+ return out;
+}
+#endif
diff --git a/src/include/compat.h b/src/include/compat.h
new file mode 100644
index 000000000..1100d69eb
--- /dev/null
+++ b/src/include/compat.h
@@ -0,0 +1,420 @@
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2011 Stanislav Sedov <stas@FreeBSD.org>
+ *
+ * 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_COMPAT_H
+#define CEPH_COMPAT_H
+
+#include "acconfig.h"
+#include <sys/types.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#if defined(__linux__)
+#define PROCPREFIX
+#endif
+
+#include <fcntl.h>
+#ifndef F_OFD_SETLK
+#define F_OFD_SETLK F_SETLK
+#endif
+
+#include <sys/stat.h>
+
+#ifdef _WIN32
+#include "include/win32/fs_compat.h"
+#endif
+
+#ifndef ACCESSPERMS
+#define ACCESSPERMS (S_IRWXU|S_IRWXG|S_IRWXO)
+#endif
+
+#ifndef ALLPERMS
+#define ALLPERMS (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)
+#endif
+
+#if defined(__FreeBSD__)
+
+// FreeBSD supports Linux procfs with its compatibility module
+// And all compatibility stuff is standard mounted on this
+#define PROCPREFIX "/compat/linux"
+
+#ifndef MSG_MORE
+#define MSG_MORE 0
+#endif
+
+#ifndef O_DSYNC
+#define O_DSYNC O_SYNC
+#endif
+
+/* And include the extra required include file */
+#include <pthread_np.h>
+
+#include <sys/param.h>
+#include <sys/cpuset.h>
+#define cpu_set_t cpuset_t
+int sched_setaffinity(pid_t pid, size_t cpusetsize,
+ cpu_set_t *mask);
+
+#endif /* __FreeBSD__ */
+
+#if defined(__APPLE__)
+struct cpu_set_t;
+#endif
+
+#if defined(__APPLE__) || defined(__FreeBSD__)
+/* Make sure that ENODATA is defined in the correct way */
+#ifdef ENODATA
+#if (ENODATA == 9919)
+// #warning ENODATA already defined to be 9919, redefining to fix
+// Silencing this warning because it fires at all files where compat.h
+// is included after boost files.
+//
+// This value stems from the definition in the boost library
+// And when this case occurs it is due to the fact that boost files
+// are included before this file. Redefinition might not help in this
+// case since already parsed code has evaluated to the wrong value.
+// This would warrrant for d definition that would actually be evaluated
+// at the location of usage and report a possible conflict.
+// This is left up to a future improvement
+#elif (ENODATA != 87)
+// #warning ENODATA already defined to a value different from 87 (ENOATRR), refining to fix
+#endif
+#undef ENODATA
+#endif
+#define ENODATA ENOATTR
+
+// Fix clock accuracy
+#if !defined(CLOCK_MONOTONIC_COARSE)
+#if defined(CLOCK_MONOTONIC_FAST)
+#define CLOCK_MONOTONIC_COARSE CLOCK_MONOTONIC_FAST
+#else
+#define CLOCK_MONOTONIC_COARSE CLOCK_MONOTONIC
+#endif
+#endif
+#if !defined(CLOCK_REALTIME_COARSE)
+#if defined(CLOCK_REALTIME_FAST)
+#define CLOCK_REALTIME_COARSE CLOCK_REALTIME_FAST
+#else
+#define CLOCK_REALTIME_COARSE CLOCK_REALTIME
+#endif
+#endif
+
+/* get PATH_MAX */
+#include <limits.h>
+
+#ifndef EUCLEAN
+#define EUCLEAN 117
+#endif
+#ifndef EREMOTEIO
+#define EREMOTEIO 121
+#endif
+#ifndef EKEYREJECTED
+#define EKEYREJECTED 129
+#endif
+#ifndef XATTR_CREATE
+#define XATTR_CREATE 1
+#endif
+
+#endif /* __APPLE__ */
+
+#ifndef HOST_NAME_MAX
+#ifdef MAXHOSTNAMELEN
+#define HOST_NAME_MAX MAXHOSTNAMELEN
+#else
+#define HOST_NAME_MAX 255
+#endif
+#endif /* HOST_NAME_MAX */
+
+/* O_LARGEFILE is not defined/required on OSX/FreeBSD */
+#ifndef O_LARGEFILE
+#define O_LARGEFILE 0
+#endif
+
+/* Could be relevant for other platforms */
+#ifndef ERESTART
+#define ERESTART EINTR
+#endif
+
+#ifndef TEMP_FAILURE_RETRY
+#define TEMP_FAILURE_RETRY(expression) ({ \
+ __typeof(expression) __result; \
+ do { \
+ __result = (expression); \
+ } while (__result == -1 && errno == EINTR); \
+ __result; })
+#endif
+
+#ifdef __cplusplus
+# define VOID_TEMP_FAILURE_RETRY(expression) \
+ static_cast<void>(TEMP_FAILURE_RETRY(expression))
+#else
+# define VOID_TEMP_FAILURE_RETRY(expression) \
+ do { (void)TEMP_FAILURE_RETRY(expression); } while (0)
+#endif
+
+#if defined(__FreeBSD__) || defined(__APPLE__)
+#define lseek64(fd, offset, whence) lseek(fd, offset, whence)
+#endif
+
+#if defined(__sun) || defined(_AIX)
+#define LOG_AUTHPRIV (10<<3)
+#define LOG_FTP (11<<3)
+#define __STRING(x) "x"
+#endif
+
+#if defined(__sun) || defined(_AIX) || defined(_WIN32)
+#define IFTODT(mode) (((mode) & 0170000) >> 12)
+#endif
+
+#if defined(_AIX)
+#define MSG_DONTWAIT MSG_NONBLOCK
+#endif
+
+#if defined(HAVE_PTHREAD_SETNAME_NP)
+ #if defined(__APPLE__)
+ #define ceph_pthread_setname(thread, name) ({ \
+ int __result = 0; \
+ if (thread == pthread_self()) \
+ __result = pthread_setname_np(name); \
+ __result; })
+ #else
+ #define ceph_pthread_setname pthread_setname_np
+ #endif
+#elif defined(HAVE_PTHREAD_SET_NAME_NP)
+ /* Fix a small name diff and return 0 */
+ #define ceph_pthread_setname(thread, name) ({ \
+ pthread_set_name_np(thread, name); \
+ 0; })
+#else
+ /* compiler warning free success noop */
+ #define ceph_pthread_setname(thread, name) ({ \
+ int __i = 0; \
+ __i; })
+#endif
+
+#if defined(HAVE_PTHREAD_GETNAME_NP)
+ #define ceph_pthread_getname pthread_getname_np
+#elif defined(HAVE_PTHREAD_GET_NAME_NP)
+ #define ceph_pthread_getname(thread, name, len) ({ \
+ pthread_get_name_np(thread, name, len); \
+ 0; })
+#else
+ /* compiler warning free success noop */
+ #define ceph_pthread_getname(thread, name, len) ({ \
+ if (name != NULL) \
+ *name = '\0'; \
+ 0; })
+#endif
+
+int ceph_posix_fallocate(int fd, off_t offset, off_t len);
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int pipe_cloexec(int pipefd[2], int flags);
+char *ceph_strerror_r(int errnum, char *buf, size_t buflen);
+unsigned get_page_size();
+// On success, returns the number of bytes written to the buffer. On
+// failure, returns -1.
+ssize_t get_self_exe_path(char* path, int buff_length);
+
+int ceph_memzero_s(void *dest, size_t destsz, size_t count);
+
+#ifdef __cplusplus
+}
+#endif
+
+#if defined(_WIN32)
+
+#include "include/win32/winsock_compat.h"
+
+#include <windows.h>
+#include <time.h>
+
+#include "include/win32/win32_errno.h"
+
+// There are a few name collisions between Windows headers and Ceph.
+// Updating Ceph definitions would be the prefferable fix in order to avoid
+// confussion, unless it requires too many changes, in which case we're going
+// to redefine Windows values by adding the "WIN32_" prefix.
+#define WIN32_DELETE 0x00010000L
+#undef DELETE
+
+#define WIN32_ERROR 0
+#undef ERROR
+
+#ifndef uint
+typedef unsigned int uint;
+#endif
+
+typedef _sigset_t sigset_t;
+
+typedef unsigned int blksize_t;
+typedef unsigned __int64 blkcnt_t;
+typedef unsigned short nlink_t;
+
+typedef long long loff_t;
+
+#define CPU_SETSIZE (sizeof(size_t)*8)
+
+typedef union
+{
+ char cpuset[CPU_SETSIZE/8];
+ size_t _align;
+} cpu_set_t;
+
+struct iovec {
+ void *iov_base;
+ size_t iov_len;
+};
+
+#define SHUT_RD SD_RECEIVE
+#define SHUT_WR SD_SEND
+#define SHUT_RDWR SD_BOTH
+
+#ifndef SIGINT
+#define SIGINT 2
+#endif
+
+#ifndef SIGKILL
+#define SIGKILL 9
+#endif
+
+#define IOV_MAX 1024
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ssize_t readv(int fd, const struct iovec *iov, int iov_cnt);
+ssize_t writev(int fd, const struct iovec *iov, int iov_cnt);
+
+int fsync(int fd);
+ssize_t pread(int fd, void *buf, size_t count, off_t offset);
+ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset);
+
+long int lrand48(void);
+int random();
+
+int pipe(int pipefd[2]);
+
+int posix_memalign(void **memptr, size_t alignment, size_t size);
+
+char *strptime(const char *s, const char *format, struct tm *tm);
+
+int chown(const char *path, uid_t owner, gid_t group);
+int fchown(int fd, uid_t owner, gid_t group);
+int lchown(const char *path, uid_t owner, gid_t group);
+int setenv(const char *name, const char *value, int overwrite);
+
+int geteuid();
+int getegid();
+int getuid();
+int getgid();
+
+#define unsetenv(name) _putenv_s(name, "")
+
+int win_socketpair(int socks[2]);
+
+#ifdef __MINGW32__
+extern _CRTIMP errno_t __cdecl _putenv_s(const char *_Name,const char *_Value);
+
+#if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
+#define htobe16(x) __builtin_bswap16(x)
+#define htole16(x) (x)
+#define be16toh(x) __builtin_bswap16(x)
+#define le16toh(x) (x)
+
+#define htobe32(x) __builtin_bswap32(x)
+#define htole32(x) (x)
+#define be32toh(x) __builtin_bswap32(x)
+#define le32toh(x) (x)
+
+#define htobe64(x) __builtin_bswap64(x)
+#define htole64(x) (x)
+#define be64toh(x) __builtin_bswap64(x)
+#define le64toh(x) (x)
+#endif // defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
+
+#endif // __MINGW32__
+
+#ifdef __cplusplus
+}
+#endif
+
+#define compat_closesocket closesocket
+// Use "aligned_free" when freeing memory allocated using posix_memalign or
+// _aligned_malloc. Using "free" will crash.
+static inline void aligned_free(void* ptr) {
+ _aligned_free(ptr);
+}
+
+// O_CLOEXEC is not defined on Windows. Since handles aren't inherited
+// with subprocesses unless explicitly requested, we'll define this
+// flag as a no-op.
+#define O_CLOEXEC 0
+#define SOCKOPT_VAL_TYPE char*
+
+#define DEV_NULL "nul"
+
+#else /* WIN32 */
+
+#define SOCKOPT_VAL_TYPE void*
+
+static inline void aligned_free(void* ptr) {
+ free(ptr);
+}
+static inline int compat_closesocket(int fildes) {
+ return close(fildes);
+}
+
+#define DEV_NULL "/dev/null"
+
+#endif /* WIN32 */
+
+/* Supplies code to be run at startup time before invoking main().
+ * Use as:
+ *
+ * CEPH_CONSTRUCTOR(my_constructor) {
+ * ...some code...
+ * }
+ */
+#ifdef _MSC_VER
+#pragma section(".CRT$XCU",read)
+#define CEPH_CONSTRUCTOR(f) \
+ static void __cdecl f(void); \
+ __declspec(allocate(".CRT$XCU")) static void (__cdecl*f##_)(void) = f; \
+ static void __cdecl f(void)
+#else
+#define CEPH_CONSTRUCTOR(f) \
+ static void f(void) __attribute__((constructor)); \
+ static void f(void)
+#endif
+
+/* This should only be used with the socket API. */
+static inline int ceph_sock_errno() {
+#ifdef _WIN32
+ return wsae_to_errno(WSAGetLastError());
+#else
+ return errno;
+#endif
+}
+
+// Needed on Windows when handling binary files. Without it, line
+// endings will be replaced and certain characters can be treated as
+// EOF.
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+#endif /* !CEPH_COMPAT_H */
diff --git a/src/include/config-h.in.cmake b/src/include/config-h.in.cmake
new file mode 100644
index 000000000..cc9ad0ec7
--- /dev/null
+++ b/src/include/config-h.in.cmake
@@ -0,0 +1,393 @@
+/* config.h file expanded by Cmake for build */
+
+#ifndef CONFIG_H
+#define CONFIG_H
+
+/* Define to 1 if you have the `memset_s()` function. */
+#cmakedefine HAVE_MEMSET_S
+
+/* fallocate(2) is supported */
+#cmakedefine CEPH_HAVE_FALLOCATE
+
+/* Define to 1 if you have the `posix_fadvise' function. */
+#cmakedefine HAVE_POSIX_FADVISE 1
+
+/* Define to 1 if you have the `posix_fallocate' function. */
+#cmakedefine HAVE_POSIX_FALLOCATE 1
+
+/* Define to 1 if you have the `syncfs' function. */
+#cmakedefine HAVE_SYS_SYNCFS 1
+
+/* sync_file_range(2) is supported */
+#cmakedefine HAVE_SYNC_FILE_RANGE
+
+/* Define if you have mallinfo */
+#cmakedefine HAVE_MALLINFO
+
+/* Define to 1 if you have the `pwritev' function. */
+#cmakedefine HAVE_PWRITEV 1
+
+/* Define to 1 if you have the <sys/mount.h> header file. */
+#cmakedefine HAVE_SYS_MOUNT_H 1
+
+/* Define to 1 if you have the <sys/param.h> header file. */
+#cmakedefine HAVE_SYS_PARAM_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#cmakedefine HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <sys/vfs.h> header file. */
+#cmakedefine HAVE_SYS_VFS_H 1
+
+/* Define to 1 if you have the <execinfo.h> header file. */
+#cmakedefine HAVE_EXECINFO_H 1
+
+/* Define to 1 if the system has the type `__s16'. */
+#cmakedefine HAVE___S16 1
+
+/* Define to 1 if the system has the type `__s32'. */
+#cmakedefine HAVE___S32 1
+
+/* Define to 1 if the system has the type `__s64'. */
+#cmakedefine HAVE___S64 1
+
+/* Define to 1 if the system has the type `__s8'. */
+#cmakedefine HAVE___S8 1
+
+/* Define to 1 if the system has the type `__u16'. */
+#cmakedefine HAVE___U16 1
+
+/* Define to 1 if the system has the type `__u32'. */
+#cmakedefine HAVE___U32 1
+
+/* Define to 1 if the system has the type `__u64'. */
+#cmakedefine HAVE___U64 1
+
+/* Define to 1 if the system has the type `__u8'. */
+#cmakedefine HAVE___U8 1
+
+/* Define if the system has the type `in_addr_t' */
+#cmakedefine HAVE_IN_ADDR_T
+
+/* Define if you have suseconds_t */
+#cmakedefine HAVE_SUSECONDS_T
+
+/* Define if you have res_nquery */
+#cmakedefine HAVE_RES_NQUERY
+
+/* Defined if you have LZ4 */
+#cmakedefine HAVE_LZ4
+
+/* Defined if you have BROTLI */
+#cmakedefine HAVE_BROTLI
+
+/* Defined if you have libaio */
+#cmakedefine HAVE_LIBAIO
+
+/* Defined if you have libdml */
+#cmakedefine HAVE_LIBDML
+
+/* Defined if you have libzbd */
+#cmakedefine HAVE_LIBZBD
+
+/* Defined if you have liburing */
+#cmakedefine HAVE_LIBURING
+
+/* Defind if you have POSIX AIO */
+#cmakedefine HAVE_POSIXAIO
+
+/* Defined if OpenLDAP enabled */
+#cmakedefine HAVE_OPENLDAP
+
+/* Define if you have fuse */
+#cmakedefine HAVE_LIBFUSE
+
+/* Define version major */
+#define CEPH_FUSE_MAJOR_VERSION @FUSE_MAJOR_VERSION@
+
+/* Define version minor */
+#define CEPH_FUSE_MINOR_VERSION @FUSE_MINOR_VERSION@
+
+/* Define to 1 if you have libxfs */
+#cmakedefine HAVE_LIBXFS 1
+
+/* SPDK conditional compilation */
+#cmakedefine HAVE_SPDK
+
+/* DPDK conditional compilation */
+#cmakedefine HAVE_DPDK
+
+/* PMEM_DEVICE (OSD) conditional compilation */
+#cmakedefine HAVE_BLUESTORE_PMEM
+
+/* Define if you have tcmalloc */
+#cmakedefine HAVE_LIBTCMALLOC
+#cmakedefine LIBTCMALLOC_MISSING_ALIGNED_ALLOC
+
+/* AsyncMessenger RDMA conditional compilation */
+#cmakedefine HAVE_RDMA
+
+/* ibverbs experimental conditional compilation */
+#cmakedefine HAVE_IBV_EXP
+
+/* define if bluestore enabled */
+#cmakedefine WITH_BLUESTORE
+
+/* define if cephfs enabled */
+#cmakedefine WITH_CEPHFS
+
+/* define if systemed is enabled */
+#cmakedefine WITH_SYSTEMD
+
+/*define if GSSAPI/KRB5 enabled */
+#cmakedefine HAVE_GSSAPI
+
+/* define if rbd enabled */
+#cmakedefine WITH_RBD
+
+/* define if kernel rbd enabled */
+#cmakedefine WITH_KRBD
+
+/* define if key-value-store is enabled */
+#cmakedefine WITH_KVS
+
+/* define if radosgw enabled */
+#cmakedefine WITH_RADOSGW
+
+/* define if radosgw has openssl support */
+#cmakedefine WITH_CURL_OPENSSL
+
+/* define if HAVE_THREAD_SAFE_RES_QUERY */
+#cmakedefine HAVE_THREAD_SAFE_RES_QUERY
+
+/* define if HAVE_REENTRANT_STRSIGNAL */
+#cmakedefine HAVE_REENTRANT_STRSIGNAL
+
+/* Define if you want to use LTTng */
+#cmakedefine WITH_LTTNG
+
+/* Define if you want to use Jaeger */
+#cmakedefine HAVE_JAEGER
+
+/* Define if you want to use EVENTTRACE */
+#cmakedefine WITH_EVENTTRACE
+
+/* Define if you want to OSD function instrumentation */
+#cmakedefine WITH_OSD_INSTRUMENT_FUNCTIONS
+
+/* Define if you want to use Babeltrace */
+#cmakedefine WITH_BABELTRACE
+
+/* Define to 1 if you have the <babeltrace/babeltrace.h> header file. */
+#cmakedefine HAVE_BABELTRACE_BABELTRACE_H 1
+
+/* Define to 1 if you have the <babeltrace/ctf/events.h> header file. */
+#cmakedefine HAVE_BABELTRACE_CTF_EVENTS_H 1
+
+/* Define to 1 if you have the <babeltrace/ctf/iterator.h> header file. */
+#cmakedefine HAVE_BABELTRACE_CTF_ITERATOR_H 1
+
+/* Define to 1 if you have the <arpa/nameser_compat.h> header file. */
+#cmakedefine HAVE_ARPA_NAMESER_COMPAT_H 1
+
+/* FastCGI headers are in /usr/include/fastcgi */
+#cmakedefine FASTCGI_INCLUDE_DIR
+
+/* splice(2) is supported */
+#cmakedefine CEPH_HAVE_SPLICE
+
+/* Define if you want C_Gather debugging */
+#cmakedefine DEBUG_GATHER
+
+/* Define to 1 if you have the `getgrouplist' function. */
+#cmakedefine HAVE_GETGROUPLIST 1
+
+/* LTTng is disabled, so define this macro to be nothing. */
+#cmakedefine tracepoint
+
+/* Define to 1 if you have fdatasync. */
+#cmakedefine HAVE_FDATASYNC 1
+
+/* Define to 1 if you have the <valgrind/helgrind.h> header file. */
+#cmakedefine HAVE_VALGRIND_HELGRIND_H 1
+
+/* Define to 1 if you have the <sys/prctl.h> header file. */
+#cmakedefine HAVE_SYS_PRCTL_H 1
+
+/* Define to 1 if you have the <linux/types.h> header file. */
+#cmakedefine HAVE_LINUX_TYPES_H 1
+
+/* Define to 1 if you have the <linux/version.h> header file. */
+#cmakedefine HAVE_LINUX_VERSION_H 1
+
+/* Define to 1 if you have sched.h. */
+#cmakedefine HAVE_SCHED 1
+
+/* Define to 1 if you have sigdescr_np. */
+#cmakedefine HAVE_SIGDESCR_NP 1
+
+/* Support SSE (Streaming SIMD Extensions) instructions */
+#cmakedefine HAVE_SSE
+
+/* Support SSE2 (Streaming SIMD Extensions 2) instructions */
+#cmakedefine HAVE_SSE2
+
+/* Define to 1 if you have the `pipe2' function. */
+#cmakedefine HAVE_PIPE2 1
+
+/* Support NEON instructions */
+#cmakedefine HAVE_NEON
+
+/* Define if you have pthread_spin_init */
+#cmakedefine HAVE_PTHREAD_SPINLOCK
+
+/* name_to_handle_at exists */
+#cmakedefine HAVE_NAME_TO_HANDLE_AT
+
+/* we have a recent nasm and are x86_64 */
+#cmakedefine HAVE_NASM_X64
+
+/* nasm can also build the isa-l:avx512 */
+#cmakedefine HAVE_NASM_X64_AVX512
+
+/* Define if the erasure code isa-l plugin is compiled */
+#cmakedefine WITH_EC_ISA_PLUGIN
+
+/* Define to 1 if strerror_r returns char *. */
+#cmakedefine STRERROR_R_CHAR_P 1
+
+/* Defined if you have libzfs enabled */
+#cmakedefine HAVE_LIBZFS
+
+/* Define if the C compiler supports __func__ */
+#cmakedefine HAVE_FUNC
+
+/* Define if the C compiler supports __PRETTY_FUNCTION__ */
+#cmakedefine HAVE_PRETTY_FUNC
+
+/* Define if the C compiler supports __attribute__((__symver__ (".."))) */
+#cmakedefine HAVE_ATTR_SYMVER
+
+/* Define if the C compiler supports __asm__(".symver ..") */
+#cmakedefine HAVE_ASM_SYMVER
+
+/* Have eventfd extension. */
+#cmakedefine HAVE_EVENTFD
+
+/* Define if enabling coverage. */
+#cmakedefine ENABLE_COVERAGE
+
+/* Defined if you want pg ref debugging */
+#cmakedefine PG_DEBUG_REFS
+
+/* Support ARMv8 CRC instructions */
+#cmakedefine HAVE_ARMV8_CRC
+
+/* Support ARMv8 CRYPTO instructions */
+#cmakedefine HAVE_ARMV8_CRYPTO
+
+/* Support ARMv8 CRC and CRYPTO intrinsics */
+#cmakedefine HAVE_ARMV8_CRC_CRYPTO_INTRINSICS
+
+/* Define if you have struct stat.st_mtimespec.tv_nsec */
+#cmakedefine HAVE_STAT_ST_MTIMESPEC_TV_NSEC
+
+/* Define if you have struct stat.st_mtim.tv_nsec */
+#cmakedefine HAVE_STAT_ST_MTIM_TV_NSEC
+
+/* Define if compiler supports static_cast<> */
+#cmakedefine HAVE_STATIC_CAST
+
+/* Version number of package */
+#cmakedefine PROJECT_VERSION "@PROJECT_VERSION@"
+
+/* Defined if pthread_setname_np() is available */
+#cmakedefine HAVE_PTHREAD_SETNAME_NP 1
+
+/* Defined if pthread_rwlockattr_setkind_np() is available */
+#cmakedefine HAVE_PTHREAD_RWLOCKATTR_SETKIND_NP
+
+/* Defined if blkin enabled */
+#cmakedefine WITH_BLKIN
+
+/* Defined if pthread_set_name_np() is available */
+#cmakedefine HAVE_PTHREAD_SET_NAME_NP
+
+/* Defined if pthread_getname_np() is available */
+#cmakedefine HAVE_PTHREAD_GETNAME_NP 1
+
+/* Support POWER8 instructions */
+#cmakedefine HAVE_POWER8
+
+/* Define if endian type is big endian */
+#cmakedefine CEPH_BIG_ENDIAN
+
+/* Define if endian type is little endian */
+#cmakedefine CEPH_LITTLE_ENDIAN
+
+#cmakedefine MGR_PYTHON_EXECUTABLE "@MGR_PYTHON_EXECUTABLE@"
+
+/* Define to 1 if you have the `getprogname' function. */
+#cmakedefine HAVE_GETPROGNAME 1
+
+/* Defined if getentropy() is available */
+#cmakedefine HAVE_GETENTROPY
+
+/* Defined if libradosstriper is enabled: */
+#cmakedefine WITH_LIBRADOSSTRIPER
+
+/* Defined if OpenSSL is available for the rgw beast frontend */
+#cmakedefine WITH_RADOSGW_BEAST_OPENSSL
+
+/* Defined if rabbitmq-c is available for rgw amqp push endpoint */
+#cmakedefine WITH_RADOSGW_AMQP_ENDPOINT
+
+/* Defined if libedkafka is available for rgw kafka push endpoint */
+#cmakedefine WITH_RADOSGW_KAFKA_ENDPOINT
+
+/* Defined if lua packages can be installed by radosgw */
+#cmakedefine WITH_RADOSGW_LUA_PACKAGES
+
+/* Backend dbstore for Rados Gateway */
+#cmakedefine WITH_RADOSGW_DBSTORE
+
+/* Backend CORTX-Motr for Rados Gateway */
+#cmakedefine WITH_RADOSGW_MOTR
+
+/* Backend CORTX-DAOS for Rados Gateway */
+#cmakedefine WITH_RADOSGW_DAOS
+
+/* Defined if std::map::merge() is supported */
+#cmakedefine HAVE_STDLIB_MAP_SPLICING
+
+/* Defined if Intel QAT compress/decompress is supported */
+#cmakedefine HAVE_QATZIP
+
+/* Define if seastar is available. */
+#cmakedefine HAVE_SEASTAR
+
+/* Define if unit tests are built. */
+#cmakedefine UNIT_TESTS_BUILT
+
+/* Define if RBD QCOW migration format is enabled */
+#cmakedefine WITH_RBD_MIGRATION_FORMAT_QCOW_V1
+
+/* Define if libcephsqlite is enabled */
+#cmakedefine WITH_LIBCEPHSQLITE
+
+/* Define if RWL is enabled */
+#cmakedefine WITH_RBD_RWL
+
+/* Define if PWL-SSD is enabled */
+#cmakedefine WITH_RBD_SSD_CACHE
+
+/* Define if libcryptsetup can be used (linux only) */
+#cmakedefine HAVE_LIBCRYPTSETUP
+
+/* Shared library extension, such as .so, .dll or .dylib */
+#cmakedefine CMAKE_SHARED_LIBRARY_SUFFIX "@CMAKE_SHARED_LIBRARY_SUFFIX@"
+
+/* libexec directory path */
+#cmakedefine CMAKE_INSTALL_LIBEXECDIR "@CMAKE_INSTALL_LIBEXECDIR@"
+
+#endif /* CONFIG_H */
diff --git a/src/include/coredumpctl.h b/src/include/coredumpctl.h
new file mode 100644
index 000000000..60b91e999
--- /dev/null
+++ b/src/include/coredumpctl.h
@@ -0,0 +1,105 @@
+#pragma once
+
+#include "acconfig.h"
+
+#ifdef HAVE_SYS_PRCTL_H
+#include <iostream>
+#include <sys/prctl.h>
+#include "common/errno.h"
+
+class PrCtl {
+ int saved_state = -1;
+ static int get_dumpable() {
+ int r = prctl(PR_GET_DUMPABLE);
+ if (r == -1) {
+ r = errno;
+ std::cerr << "warning: unable to get dumpable flag: " << cpp_strerror(r)
+ << std::endl;
+ }
+ return r;
+ }
+ static int set_dumpable(bool new_state) {
+ int r = prctl(PR_SET_DUMPABLE, new_state);
+ if (r) {
+ r = -errno;
+ std::cerr << "warning: unable to " << (new_state ? "set" : "unset")
+ << " dumpable flag: " << cpp_strerror(r)
+ << std::endl;
+ }
+ return r;
+ }
+public:
+ PrCtl(int new_state = 0) {
+ int r = get_dumpable();
+ if (r == -1) {
+ return;
+ }
+ if (r != new_state) {
+ if (!set_dumpable(new_state)) {
+ saved_state = r;
+ }
+ }
+ }
+ ~PrCtl() {
+ if (saved_state < 0) {
+ return;
+ }
+ set_dumpable(saved_state);
+ }
+};
+
+#else
+#ifdef RLIMIT_CORE
+#include <sys/resource.h>
+#include <iostream>
+#include <sys/resource.h>
+#include "common/errno.h"
+
+class PrCtl {
+ rlimit saved_lim;
+ static int get_dumpable(rlimit* saved) {
+ int r = getrlimit(RLIMIT_CORE, saved);
+ if (r) {
+ r = errno;
+ std::cerr << "warning: unable to getrlimit(): " << cpp_strerror(r)
+ << std::endl;
+ }
+ return r;
+ }
+ static void set_dumpable(const rlimit& rlim) {
+ int r = setrlimit(RLIMIT_CORE, &rlim);
+ if (r) {
+ r = -errno;
+ std::cerr << "warning: unable to setrlimit(): " << cpp_strerror(r)
+ << std::endl;
+ }
+ }
+public:
+ PrCtl(int new_state = 0) {
+ int r = get_dumpable(&saved_lim);
+ if (r == -1) {
+ return;
+ }
+ rlimit new_lim;
+ if (new_state) {
+ new_lim.rlim_cur = saved_lim.rlim_max;
+ } else {
+ new_lim.rlim_cur = new_lim.rlim_max = 0;
+ }
+ if (new_lim.rlim_cur == saved_lim.rlim_cur) {
+ return;
+ }
+ set_dumpable(new_lim);
+ }
+ ~PrCtl() {
+ set_dumpable(saved_lim);
+ }
+};
+#else
+struct PrCtl {
+ // to silence the Wunused-variable warning
+ PrCtl() {}
+};
+
+#endif // RLIMIT_CORE
+#endif
diff --git a/src/include/counter.h b/src/include/counter.h
new file mode 100644
index 000000000..61ed7409c
--- /dev/null
+++ b/src/include/counter.h
@@ -0,0 +1,56 @@
+// -*- 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) 2017 Red Hat, Inc.
+ *
+ * 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_COUNTER_H
+#define CEPH_COUNTER_H
+
+#include <atomic>
+
+template <typename T>
+class Counter {
+public:
+ Counter() {
+ _count()++;
+ _increments()++;
+ }
+ Counter(const Counter &rhs) {
+ _count()++;
+ _increments()++;
+ }
+ Counter(Counter &&rhs) {}
+ ~Counter() {
+ _count()--;
+ }
+ static uint64_t count() {
+ return _count();
+ }
+ static uint64_t increments() {
+ return _increments();
+ }
+ static uint64_t decrements() {
+ return increments()-count();
+ }
+
+private:
+ static std::atomic<uint64_t> &_count() {
+ static std::atomic<uint64_t> c;
+ return c;
+ }
+ static std::atomic<uint64_t> &_increments() {
+ static std::atomic<uint64_t> i;
+ return i;
+ }
+};
+
+#endif
diff --git a/src/include/cpp-btree/btree.h b/src/include/cpp-btree/btree.h
new file mode 100644
index 000000000..2eddc2abe
--- /dev/null
+++ b/src/include/cpp-btree/btree.h
@@ -0,0 +1,2571 @@
+// Copyright 2018 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// A btree implementation of the STL set and map interfaces. A btree is smaller
+// and generally also faster than STL set/map (refer to the benchmarks below).
+// The red-black tree implementation of STL set/map has an overhead of 3
+// pointers (left, right and parent) plus the node color information for each
+// stored value. So a set<int32_t> consumes 40 bytes for each value stored in
+// 64-bit mode. This btree implementation stores multiple values on fixed
+// size nodes (usually 256 bytes) and doesn't store child pointers for leaf
+// nodes. The result is that a btree_set<int32_t> may use much less memory per
+// stored value. For the random insertion benchmark in btree_bench.cc, a
+// btree_set<int32_t> with node-size of 256 uses 5.1 bytes per stored value.
+//
+// The packing of multiple values on to each node of a btree has another effect
+// besides better space utilization: better cache locality due to fewer cache
+// lines being accessed. Better cache locality translates into faster
+// operations.
+//
+// CAVEATS
+//
+// Insertions and deletions on a btree can cause splitting, merging or
+// rebalancing of btree nodes. And even without these operations, insertions
+// and deletions on a btree will move values around within a node. In both
+// cases, the result is that insertions and deletions can invalidate iterators
+// pointing to values other than the one being inserted/deleted. Therefore, this
+// container does not provide pointer stability. This is notably different from
+// STL set/map which takes care to not invalidate iterators on insert/erase
+// except, of course, for iterators pointing to the value being erased. A
+// partial workaround when erasing is available: erase() returns an iterator
+// pointing to the item just after the one that was erased (or end() if none
+// exists).
+
+#pragma once
+
+#include <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+#include <cstring>
+#include <experimental/type_traits>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <new>
+#include <type_traits>
+#include <utility>
+
+namespace btree::internal {
+
+template <typename Compare, typename T>
+using btree_is_key_compare_to =
+ std::is_signed<std::invoke_result_t<Compare, T, T>>;
+
+template<typename T>
+using compare_to_t = decltype(std::declval<T&>().compare(std::declval<const T&>()));
+template<typename T>
+inline constexpr bool has_compare_to = std::experimental::is_detected_v<compare_to_t, T>;
+// A helper class to convert a boolean comparison into a three-way "compare-to"
+// comparison that returns a negative value to indicate less-than, zero to
+// indicate equality and a positive value to indicate greater-than. This helper
+// class is specialized for less<std::string>, greater<std::string>,
+// less<string_view>, and greater<string_view>.
+//
+// key_compare_to_adapter is provided so that btree users
+// automatically get the more efficient compare-to code when using common
+// google string types with common comparison functors.
+// These string-like specializations also turn on heterogeneous lookup by
+// default.
+template <typename Compare, typename=void>
+struct key_compare_to_adapter {
+ using type = Compare;
+};
+
+template <typename K>
+struct key_compare_to_adapter<std::less<K>, std::enable_if_t<has_compare_to<K>>>
+{
+ struct type {
+ inline int operator()(const K& lhs, const K& rhs) const noexcept {
+ return lhs.compare(rhs);
+ }
+ };
+};
+
+template <typename K>
+struct key_compare_to_adapter<std::less<K>, std::enable_if_t<std::is_signed_v<K>>>
+{
+ struct type {
+ inline K operator()(const K& lhs, const K& rhs) const noexcept {
+ return lhs - rhs;
+ }
+ };
+};
+
+template <typename K>
+struct key_compare_to_adapter<std::less<K>, std::enable_if_t<std::is_unsigned_v<K>>>
+{
+ struct type {
+ inline int operator()(const K& lhs, const K& rhs) const noexcept {
+ if (lhs < rhs) {
+ return -1;
+ } else if (lhs > rhs) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+ };
+};
+
+template <typename Key, typename Compare, typename Alloc,
+ int TargetNodeSize, int ValueSize,
+ bool Multi>
+struct common_params {
+ // If Compare is a common comparator for a std::string-like type, then we adapt it
+ // to use heterogeneous lookup and to be a key-compare-to comparator.
+ using key_compare = typename key_compare_to_adapter<Compare>::type;
+ // A type which indicates if we have a key-compare-to functor or a plain old
+ // key-compare functor.
+ using is_key_compare_to = btree_is_key_compare_to<key_compare, Key>;
+
+ using allocator_type = Alloc;
+ using key_type = Key;
+ using size_type = std::make_signed<size_t>::type;
+ using difference_type = ptrdiff_t;
+
+ // True if this is a multiset or multimap.
+ using is_multi_container = std::integral_constant<bool, Multi>;
+
+ constexpr static int kTargetNodeSize = TargetNodeSize;
+ constexpr static int kValueSize = ValueSize;
+ // Upper bound for the available space for values. This is largest for leaf
+ // nodes, which have overhead of at least a pointer + 3 bytes (for storing
+ // 3 field_types) + paddings. if alignof(key_type) is 1, the size of padding
+ // would be 0.
+ constexpr static int kNodeValueSpace =
+ TargetNodeSize - /*minimum overhead=*/(sizeof(void *) + 4);
+
+ // This is an integral type large enough to hold as many
+ // ValueSize-values as will fit a node of TargetNodeSize bytes.
+ using node_count_type =
+ std::conditional_t<(kNodeValueSpace / ValueSize >
+ (std::numeric_limits<uint8_t>::max)()),
+ uint16_t,
+ uint8_t>;
+};
+
+// The internal storage type
+//
+// It is convenient for the value_type of a btree_map<K, V> to be
+// pair<const K, V>; the "const K" prevents accidental modification of the key
+// when dealing with the reference returned from find() and similar methods.
+// However, this creates other problems; we want to be able to emplace(K, V)
+// efficiently with move operations, and similarly be able to move a
+// pair<K, V> in insert().
+//
+// The solution is this union, which aliases the const and non-const versions
+// of the pair. This also allows flat_hash_map<const K, V> to work, even though
+// that has the same efficiency issues with move in emplace() and insert() -
+// but people do it anyway.
+template <class K, class V>
+union map_slot_type {
+ map_slot_type() {}
+ ~map_slot_type() = delete;
+ map_slot_type& operator=(const map_slot_type& slot) {
+ mutable_value = slot.mutable_value;
+ return *this;
+ }
+ map_slot_type& operator=(map_slot_type&& slot) {
+ mutable_value = std::move(slot.mutable_value);
+ return *this;
+ }
+ using value_type = std::pair<const K, V>;
+ using mutable_value_type = std::pair<K, V>;
+
+ value_type value;
+ mutable_value_type mutable_value;
+ K key;
+};
+
+template <class K, class V>
+void swap(map_slot_type<K, V>& lhs, map_slot_type<K, V>& rhs) {
+ std::swap(lhs.mutable_value, rhs.mutable_value);
+}
+
+// A parameters structure for holding the type parameters for a btree_map.
+// Compare and Alloc should be nothrow copy-constructible.
+template <typename Key, typename Data, typename Compare, typename Alloc,
+ int TargetNodeSize, bool Multi>
+struct map_params : common_params<Key, Compare, Alloc, TargetNodeSize,
+ sizeof(Key) + sizeof(Data), Multi> {
+ using super_type = typename map_params::common_params;
+ using mapped_type = Data;
+ using value_type = std::pair<const Key, mapped_type>;
+ using mutable_value_type = std::pair<Key, mapped_type>;
+ using slot_type = map_slot_type<Key, mapped_type>;
+ using pointer = value_type*;
+ using const_pointer = const value_type *;
+ using reference = value_type &;
+ using const_reference = const value_type &;
+ using key_compare = typename super_type::key_compare;
+ using init_type = mutable_value_type;
+
+ static constexpr size_t kValueSize = sizeof(Key) + sizeof(mapped_type);
+
+ // Inherit from key_compare for empty base class optimization.
+ struct value_compare : private key_compare {
+ value_compare() = default;
+ explicit value_compare(const key_compare &cmp) : key_compare(cmp) {}
+
+ template <typename T, typename U>
+ auto operator()(const T &left, const U &right) const
+ -> decltype(std::declval<key_compare>()(left.first, right.first)) {
+ return key_compare::operator()(left.first, right.first);
+ }
+ };
+ using is_map_container = std::true_type;
+
+ static const Key &key(const value_type &value) { return value.first; }
+ static mapped_type &value(value_type *value) { return value->second; }
+ static const Key &key(const slot_type *slot) { return slot->key; }
+ static value_type& element(slot_type* slot) { return slot->value; }
+ static const value_type& element(const slot_type* slot) { return slot->value; }
+ template <class... Args>
+ static void construct(Alloc *alloc, slot_type *slot, Args &&... args) {
+ std::allocator_traits<Alloc>::construct(*alloc,
+ &slot->mutable_value,
+ std::forward<Args>(args)...);
+ }
+ // Construct this slot by moving from another slot.
+ static void construct(Alloc* alloc, slot_type* slot, slot_type* other) {
+ emplace(slot);
+ std::allocator_traits<Alloc>::construct(*alloc, &slot->value,
+ std::move(other->value));
+ }
+ static void move(Alloc *alloc, slot_type *src, slot_type *dest) {
+ dest->mutable_value = std::move(src->mutable_value);
+ }
+ static void destroy(Alloc *alloc, slot_type *slot) {
+ std::allocator_traits<Alloc>::destroy(*alloc, &slot->mutable_value);
+ }
+
+private:
+ static void emplace(slot_type* slot) {
+ // The construction of union doesn't do anything at runtime but it allows us
+ // to access its members without violating aliasing rules.
+ new (slot) slot_type;
+ }
+};
+
+// A parameters structure for holding the type parameters for a btree_set.
+template <typename Key, typename Compare, typename Alloc, int TargetNodeSize, bool Multi>
+struct set_params
+ : public common_params<Key, Compare, Alloc, TargetNodeSize,
+ sizeof(Key), Multi> {
+ using value_type = Key;
+ using mutable_value_type = value_type;
+ using slot_type = Key;
+ using pointer = value_type *;
+ using const_pointer = const value_type *;
+ using value_compare = typename set_params::common_params::key_compare;
+ using reference = value_type &;
+ using const_reference = const value_type &;
+ using is_map_container = std::false_type;
+ using init_type = mutable_value_type;
+
+ template <class... Args>
+ static void construct(Alloc *alloc, slot_type *slot, Args &&... args) {
+ std::allocator_traits<Alloc>::construct(*alloc,
+ slot,
+ std::forward<Args>(args)...);
+ }
+ static void construct(Alloc *alloc, slot_type *slot, slot_type *other) {
+ std::allocator_traits<Alloc>::construct(*alloc, slot, std::move(*other));
+ }
+ static void move(Alloc *alloc, slot_type *src, slot_type *dest) {
+ *dest = std::move(*src);
+ }
+ static void destroy(Alloc *alloc, slot_type *slot) {
+ std::allocator_traits<Alloc>::destroy(*alloc, slot);
+ }
+ static const Key &key(const value_type &x) { return x; }
+ static const Key &key(const slot_type *slot) { return *slot; }
+ static value_type &element(slot_type *slot) { return *slot; }
+ static const value_type &element(const slot_type *slot) { return *slot; }
+};
+
+// Helper functions to do a boolean comparison of two keys given a boolean
+// or three-way comparator.
+// SFINAE prevents implicit conversions to bool (such as from int).
+template <typename Result>
+constexpr bool compare_result_as_less_than(const Result r) {
+ if constexpr (std::is_signed_v<Result>) {
+ return r < 0;
+ } else {
+ return r;
+ }
+}
+// An adapter class that converts a lower-bound compare into an upper-bound
+// compare. Note: there is no need to make a version of this adapter specialized
+// for key-compare-to functors because the upper-bound (the first value greater
+// than the input) is never an exact match.
+template <typename Compare>
+struct upper_bound_adapter {
+ explicit upper_bound_adapter(const Compare &c) : comp(c) {}
+ template <typename K, typename LK>
+ bool operator()(const K &a, const LK &b) const {
+ // Returns true when a is not greater than b.
+ return !compare_result_as_less_than(comp(b, a));
+ }
+private:
+ const Compare& comp;
+};
+
+enum class MatchKind : uint8_t { kEq, kNe };
+
+template <typename V, bool IsCompareTo>
+struct SearchResult {
+ V value;
+ MatchKind match;
+
+ static constexpr bool has_match = true;
+ bool IsEq() const { return match == MatchKind::kEq; }
+};
+
+// When we don't use CompareTo, `match` is not present.
+// This ensures that callers can't use it accidentally when it provides no
+// useful information.
+template <typename V>
+struct SearchResult<V, false> {
+ V value;
+
+ static constexpr bool has_match = false;
+ static constexpr bool IsEq() { return false; }
+};
+
+// A node in the btree holding. The same node type is used for both internal
+// and leaf nodes in the btree, though the nodes are allocated in such a way
+// that the children array is only valid in internal nodes.
+template <typename Params>
+class btree_node {
+ using is_key_compare_to = typename Params::is_key_compare_to;
+ using is_multi_container = typename Params::is_multi_container;
+ using field_type = typename Params::node_count_type;
+ using allocator_type = typename Params::allocator_type;
+ using slot_type = typename Params::slot_type;
+
+ public:
+ using params_type = Params;
+ using key_type = typename Params::key_type;
+ using value_type = typename Params::value_type;
+ using mutable_value_type = typename Params::mutable_value_type;
+ using pointer = typename Params::pointer;
+ using const_pointer = typename Params::const_pointer;
+ using reference = typename Params::reference;
+ using const_reference = typename Params::const_reference;
+ using key_compare = typename Params::key_compare;
+ using size_type = typename Params::size_type;
+ using difference_type = typename Params::difference_type;
+
+ // Btree decides whether to use linear node search as follows:
+ // - If the key is arithmetic and the comparator is std::less or
+ // std::greater, choose linear.
+ // - Otherwise, choose binary.
+ // TODO(ezb): Might make sense to add condition(s) based on node-size.
+ using use_linear_search = std::integral_constant<
+ bool,
+ std::is_arithmetic_v<key_type> &&
+ (std::is_same_v<std::less<key_type>, key_compare> ||
+ std::is_same_v<std::greater<key_type>, key_compare>)>;
+
+ ~btree_node() = default;
+ btree_node(const btree_node&) = delete;
+ btree_node& operator=(const btree_node&) = delete;
+
+ protected:
+ btree_node() = default;
+
+ private:
+ constexpr static size_type SizeWithNValues(size_type n) {
+ return sizeof(base_fields) + n * sizeof(value_type);;
+ }
+ // A lower bound for the overhead of fields other than values in a leaf node.
+ constexpr static size_type MinimumOverhead() {
+ return SizeWithNValues(1) - sizeof(value_type);
+ }
+
+ // Compute how many values we can fit onto a leaf node taking into account
+ // padding.
+ constexpr static size_type NodeTargetValues(const int begin, const int end) {
+ return begin == end ? begin
+ : SizeWithNValues((begin + end) / 2 + 1) >
+ params_type::kTargetNodeSize
+ ? NodeTargetValues(begin, (begin + end) / 2)
+ : NodeTargetValues((begin + end) / 2 + 1, end);
+ }
+
+ constexpr static int kValueSize = params_type::kValueSize;
+ constexpr static int kTargetNodeSize = params_type::kTargetNodeSize;
+ constexpr static int kNodeTargetValues = NodeTargetValues(0, kTargetNodeSize);
+
+ // We need a minimum of 3 values per internal node in order to perform
+ // splitting (1 value for the two nodes involved in the split and 1 value
+ // propagated to the parent as the delimiter for the split).
+ constexpr static size_type kNodeValues = std::max(kNodeTargetValues, 3);
+
+ // The node is internal (i.e. is not a leaf node) if and only if `max_count`
+ // has this value.
+ constexpr static size_type kInternalNodeMaxCount = 0;
+
+ struct base_fields {
+ // A pointer to the node's parent.
+ btree_node *parent;
+ // The position of the node in the node's parent.
+ field_type position;
+ // The count of the number of values in the node.
+ field_type count;
+ // The maximum number of values the node can hold.
+ field_type max_count;
+ };
+
+ struct leaf_fields : public base_fields {
+ // The array of values. Only the first count of these values have been
+ // constructed and are valid.
+ slot_type values[kNodeValues];
+ };
+
+ struct internal_fields : public leaf_fields {
+ // The array of child pointers. The keys in children_[i] are all less than
+ // key(i). The keys in children_[i + 1] are all greater than key(i). There
+ // are always count + 1 children.
+ btree_node *children[kNodeValues + 1];
+ };
+
+ constexpr static size_type LeafSize(const int max_values = kNodeValues) {
+ return SizeWithNValues(max_values);
+ }
+ constexpr static size_type InternalSize() {
+ return sizeof(internal_fields);
+ }
+
+ template<auto MemPtr>
+ auto& GetField() {
+ return reinterpret_cast<internal_fields*>(this)->*MemPtr;
+ }
+
+ template<auto MemPtr>
+ auto& GetField() const {
+ return reinterpret_cast<const internal_fields*>(this)->*MemPtr;
+ }
+
+ void set_parent(btree_node *p) { GetField<&base_fields::parent>() = p; }
+ field_type &mutable_count() { return GetField<&base_fields::count>(); }
+ slot_type *slot(int i) { return &GetField<&leaf_fields::values>()[i]; }
+ const slot_type *slot(int i) const { return &GetField<&leaf_fields::values>()[i]; }
+ void set_position(field_type v) { GetField<&base_fields::position>() = v; }
+ void set_count(field_type v) { GetField<&base_fields::count>() = v; }
+ // This method is only called by the node init methods.
+ void set_max_count(field_type v) { GetField<&base_fields::max_count>() = v; }
+
+public:
+ constexpr static size_type Alignment() {
+ static_assert(alignof(leaf_fields) == alignof(internal_fields),
+ "Alignment of all nodes must be equal.");
+ return alignof(internal_fields);
+ }
+
+ // Getter/setter for whether this is a leaf node or not. This value doesn't
+ // change after the node is created.
+ bool leaf() const { return GetField<&base_fields::max_count>() != kInternalNodeMaxCount; }
+
+ // Getter for the position of this node in its parent.
+ field_type position() const { return GetField<&base_fields::position>(); }
+
+ // Getter for the number of values stored in this node.
+ field_type count() const { return GetField<&base_fields::count>(); }
+ field_type max_count() const {
+ // Internal nodes have max_count==kInternalNodeMaxCount.
+ // Leaf nodes have max_count in [1, kNodeValues].
+ const field_type max_count = GetField<&base_fields::max_count>();
+ return max_count == field_type{kInternalNodeMaxCount}
+ ? field_type{kNodeValues}
+ : max_count;
+ }
+
+ // Getter for the parent of this node.
+ btree_node* parent() const { return GetField<&base_fields::parent>(); }
+ // Getter for whether the node is the root of the tree. The parent of the
+ // root of the tree is the leftmost node in the tree which is guaranteed to
+ // be a leaf.
+ bool is_root() const { return parent()->leaf(); }
+ void make_root() {
+ assert(parent()->is_root());
+ set_parent(parent()->parent());
+ }
+
+ // Getters for the key/value at position i in the node.
+ const key_type& key(int i) const { return params_type::key(slot(i)); }
+ reference value(int i) { return params_type::element(slot(i)); }
+ const_reference value(int i) const { return params_type::element(slot(i)); }
+
+ // Getters/setter for the child at position i in the node.
+ btree_node* child(int i) const { return GetField<&internal_fields::children>()[i]; }
+ btree_node*& mutable_child(int i) { return GetField<&internal_fields::children>()[i]; }
+ void clear_child(int i) {
+#ifndef NDEBUG
+ memset(&mutable_child(i), 0, sizeof(btree_node*));
+#endif
+ }
+ void set_child(int i, btree_node *c) {
+ mutable_child(i) = c;
+ c->set_position(i);
+ }
+ void init_child(int i, btree_node *c) {
+ set_child(i, c);
+ c->set_parent(this);
+ }
+ // Returns the position of the first value whose key is not less than k.
+ template <typename K>
+ SearchResult<int, is_key_compare_to::value> lower_bound(
+ const K &k, const key_compare &comp) const {
+ return use_linear_search::value ? linear_search(k, comp)
+ : binary_search(k, comp);
+ }
+ // Returns the position of the first value whose key is greater than k.
+ template <typename K>
+ int upper_bound(const K &k, const key_compare &comp) const {
+ auto upper_compare = upper_bound_adapter<key_compare>(comp);
+ return use_linear_search::value ? linear_search(k, upper_compare).value
+ : binary_search(k, upper_compare).value;
+ }
+
+ template <typename K, typename Compare>
+ SearchResult<int, btree_is_key_compare_to<Compare, key_type>::value>
+ linear_search(const K &k, const Compare &comp) const {
+ return linear_search_impl(k, 0, count(), comp,
+ btree_is_key_compare_to<Compare, key_type>());
+ }
+
+ template <typename K, typename Compare>
+ SearchResult<int, btree_is_key_compare_to<Compare, key_type>::value>
+ binary_search(const K &k, const Compare &comp) const {
+ return binary_search_impl(k, 0, count(), comp,
+ btree_is_key_compare_to<Compare, key_type>());
+ }
+ // Returns the position of the first value whose key is not less than k using
+ // linear search performed using plain compare.
+ template <typename K, typename Compare>
+ SearchResult<int, false> linear_search_impl(
+ const K &k, int s, const int e, const Compare &comp,
+ std::false_type /* IsCompareTo */) const {
+ while (s < e) {
+ if (!comp(key(s), k)) {
+ break;
+ }
+ ++s;
+ }
+ return {s};
+ }
+
+ // Returns the position of the first value whose key is not less than k using
+ // linear search performed using compare-to.
+ template <typename K, typename Compare>
+ SearchResult<int, true> linear_search_impl(
+ const K &k, int s, const int e, const Compare &comp,
+ std::true_type /* IsCompareTo */) const {
+ while (s < e) {
+ const auto c = comp(key(s), k);
+ if (c == 0) {
+ return {s, MatchKind::kEq};
+ } else if (c > 0) {
+ break;
+ }
+ ++s;
+ }
+ return {s, MatchKind::kNe};
+ }
+
+ // Returns the position of the first value whose key is not less than k using
+ // binary search performed using plain compare.
+ template <typename K, typename Compare>
+ SearchResult<int, false> binary_search_impl(
+ const K &k, int s, int e, const Compare &comp,
+ std::false_type /* IsCompareTo */) const {
+ while (s != e) {
+ const int mid = (s + e) >> 1;
+ if (comp(key(mid), k)) {
+ s = mid + 1;
+ } else {
+ e = mid;
+ }
+ }
+ return {s};
+ }
+
+ // Returns the position of the first value whose key is not less than k using
+ // binary search performed using compare-to.
+ template <typename K, typename CompareTo>
+ SearchResult<int, true> binary_search_impl(
+ const K &k, int s, int e, const CompareTo &comp,
+ std::true_type /* IsCompareTo */) const {
+ if constexpr (is_multi_container::value) {
+ MatchKind exact_match = MatchKind::kNe;
+ while (s != e) {
+ const int mid = (s + e) >> 1;
+ const auto c = comp(key(mid), k);
+ if (c < 0) {
+ s = mid + 1;
+ } else {
+ e = mid;
+ if (c == 0) {
+ // Need to return the first value whose key is not less than k,
+ // which requires continuing the binary search if this is a
+ // multi-container.
+ exact_match = MatchKind::kEq;
+ }
+ }
+ }
+ return {s, exact_match};
+ } else { // Not a multi-container.
+ while (s != e) {
+ const int mid = (s + e) >> 1;
+ const auto c = comp(key(mid), k);
+ if (c < 0) {
+ s = mid + 1;
+ } else if (c > 0) {
+ e = mid;
+ } else {
+ return {mid, MatchKind::kEq};
+ }
+ }
+ return {s, MatchKind::kNe};
+ }
+ }
+
+ // Emplaces a value at position i, shifting all existing values and
+ // children at positions >= i to the right by 1.
+ template <typename... Args>
+ void emplace_value(size_type i, allocator_type *alloc, Args &&... args);
+
+ // Removes the value at position i, shifting all existing values and children
+ // at positions > i to the left by 1.
+ void remove_value(const int i, allocator_type *alloc);
+
+ // Removes the values at positions [i, i + to_erase), shifting all values
+ // after that range to the left by to_erase. Does not change children at all.
+ void remove_values_ignore_children(int i, int to_erase,
+ allocator_type *alloc);
+
+ // Rebalances a node with its right sibling.
+ void rebalance_right_to_left(const int to_move, btree_node *right,
+ allocator_type *alloc);
+ void rebalance_left_to_right(const int to_move, btree_node *right,
+ allocator_type *alloc);
+
+ // Splits a node, moving a portion of the node's values to its right sibling.
+ void split(const int insert_position, btree_node *dest, allocator_type *alloc);
+
+ // Merges a node with its right sibling, moving all of the values and the
+ // delimiting key in the parent node onto itself.
+ void merge(btree_node *sibling, allocator_type *alloc);
+
+ // Swap the contents of "this" and "src".
+ void swap(btree_node *src, allocator_type *alloc);
+
+ // Node allocation/deletion routines.
+ static btree_node *init_leaf(btree_node *n, btree_node *parent,
+ int max_count) {
+ n->set_parent(parent);
+ n->set_position(0);
+ n->set_count(0);
+ n->set_max_count(max_count);
+ return n;
+ }
+ static btree_node *init_internal(btree_node *n, btree_node *parent) {
+ init_leaf(n, parent, kNodeValues);
+ // Set `max_count` to a sentinel value to indicate that this node is
+ // internal.
+ n->set_max_count(kInternalNodeMaxCount);
+ return n;
+ }
+ void destroy(allocator_type *alloc) {
+ for (int i = 0; i < count(); ++i) {
+ value_destroy(i, alloc);
+ }
+ }
+
+ private:
+ template <typename... Args>
+ void value_init(const size_type i, allocator_type *alloc, Args &&... args) {
+ params_type::construct(alloc, slot(i), std::forward<Args>(args)...);
+ }
+ void value_destroy(const size_type i, allocator_type *alloc) {
+ params_type::destroy(alloc, slot(i));
+ }
+
+ // Move n values starting at value i in this node into the values starting at
+ // value j in node x.
+ void uninitialized_move_n(const size_type n, const size_type i,
+ const size_type j, btree_node *x,
+ allocator_type *alloc) {
+ for (slot_type *src = slot(i), *end = src + n, *dest = x->slot(j);
+ src != end; ++src, ++dest) {
+ params_type::construct(alloc, dest, src);
+ }
+ }
+
+ // Destroys a range of n values, starting at index i.
+ void value_destroy_n(const size_type i, const size_type n,
+ allocator_type *alloc) {
+ for (int j = 0; j < n; ++j) {
+ value_destroy(i + j, alloc);
+ }
+ }
+
+private:
+ template <typename P>
+ friend class btree;
+ template <typename N, typename R, typename P>
+ friend struct btree_iterator;
+};
+
+template <typename Node, typename Reference, typename Pointer>
+struct btree_iterator {
+ private:
+ using key_type = typename Node::key_type;
+ using size_type = typename Node::size_type;
+ using params_type = typename Node::params_type;
+
+ using node_type = Node;
+ using normal_node = typename std::remove_const<Node>::type;
+ using const_node = const Node;
+ using normal_pointer = typename params_type::pointer;
+ using normal_reference = typename params_type::reference;
+ using const_pointer = typename params_type::const_pointer;
+ using const_reference = typename params_type::const_reference;
+ using slot_type = typename params_type::slot_type;
+
+ using iterator =
+ btree_iterator<normal_node, normal_reference, normal_pointer>;
+ using const_iterator =
+ btree_iterator<const_node, const_reference, const_pointer>;
+
+ public:
+ // These aliases are public for std::iterator_traits.
+ using difference_type = typename Node::difference_type;
+ using value_type = typename params_type::value_type;
+ using pointer = Pointer;
+ using reference = Reference;
+ using iterator_category = std::bidirectional_iterator_tag;
+
+ btree_iterator() = default;
+ btree_iterator(Node *n, int p) : node(n), position(p) {}
+
+ // NOTE: this SFINAE allows for implicit conversions from iterator to
+ // const_iterator, but it specifically avoids defining copy constructors so
+ // that btree_iterator can be trivially copyable. This is for performance and
+ // binary size reasons.
+ template<typename N, typename R, typename P,
+ std::enable_if_t<
+ std::is_same_v<btree_iterator<N, R, P>, iterator> &&
+ std::is_same_v<btree_iterator, const_iterator>,
+ int> = 0>
+ btree_iterator(const btree_iterator<N, R, P> &x)
+ : node(x.node), position(x.position) {}
+
+ private:
+ // This SFINAE allows explicit conversions from const_iterator to
+ // iterator, but also avoids defining a copy constructor.
+ // NOTE: the const_cast is safe because this constructor is only called by
+ // non-const methods and the container owns the nodes.
+ template <typename N, typename R, typename P,
+ std::enable_if_t<
+ std::is_same_v<btree_iterator<N, R, P>, const_iterator> &&
+ std::is_same_v<btree_iterator, iterator>,
+ int> = 0>
+ explicit btree_iterator(const btree_iterator<N, R, P> &x)
+ : node(const_cast<node_type *>(x.node)), position(x.position) {}
+
+ // Increment/decrement the iterator.
+ void increment() {
+ if (node->leaf() && ++position < node->count()) {
+ return;
+ }
+ increment_slow();
+ }
+ void increment_slow();
+
+ void decrement() {
+ if (node->leaf() && --position >= 0) {
+ return;
+ }
+ decrement_slow();
+ }
+ void decrement_slow();
+
+ public:
+ bool operator==(const const_iterator &x) const {
+ return node == x.node && position == x.position;
+ }
+ bool operator!=(const const_iterator &x) const {
+ return node != x.node || position != x.position;
+ }
+ bool operator==(const iterator& x) const {
+ return node == x.node && position == x.position;
+ }
+ bool operator!=(const iterator& x) const {
+ return node != x.node || position != x.position;
+ }
+
+ // Accessors for the key/value the iterator is pointing at.
+ reference operator*() const {
+ return node->value(position);
+ }
+ pointer operator->() const {
+ return &node->value(position);
+ }
+
+ btree_iterator& operator++() {
+ increment();
+ return *this;
+ }
+ btree_iterator& operator--() {
+ decrement();
+ return *this;
+ }
+ btree_iterator operator++(int) {
+ btree_iterator tmp = *this;
+ ++*this;
+ return tmp;
+ }
+ btree_iterator operator--(int) {
+ btree_iterator tmp = *this;
+ --*this;
+ return tmp;
+ }
+
+ private:
+ template <typename Params>
+ friend class btree;
+ template <typename Tree>
+ friend class btree_container;
+ template <typename Tree>
+ friend class btree_set_container;
+ template <typename Tree>
+ friend class btree_map_container;
+ template <typename Tree>
+ friend class btree_multiset_container;
+ template <typename N, typename R, typename P>
+ friend struct btree_iterator;
+
+ const key_type &key() const { return node->key(position); }
+ slot_type *slot() { return node->slot(position); }
+
+ // The node in the tree the iterator is pointing at.
+ Node *node = nullptr;
+ // The position within the node of the tree the iterator is pointing at.
+ int position = -1;
+};
+
+template <size_t Alignment, class Alloc>
+class AlignedAlloc {
+ struct alignas(Alignment) M {};
+ using alloc_t =
+ typename std::allocator_traits<Alloc>::template rebind_alloc<M>;
+ using traits_t =
+ typename std::allocator_traits<Alloc>::template rebind_traits<M>;
+ static constexpr size_t num_aligned_objects(size_t size) {
+ return (size + sizeof(M) - 1) / sizeof(M);
+ }
+public:
+ static void* allocate(Alloc* alloc, size_t size) {
+ alloc_t aligned_alloc(*alloc);
+ void* p = traits_t::allocate(aligned_alloc,
+ num_aligned_objects(size));
+ assert(reinterpret_cast<uintptr_t>(p) % Alignment == 0 &&
+ "allocator does not respect alignment");
+ return p;
+ }
+ static void deallocate(Alloc* alloc, void* p, size_t size) {
+ alloc_t aligned_alloc(*alloc);
+ traits_t::deallocate(aligned_alloc, static_cast<M*>(p),
+ num_aligned_objects(size));
+ }
+};
+
+template <typename Params>
+class btree {
+ using node_type = btree_node<Params>;
+ using is_key_compare_to = typename Params::is_key_compare_to;
+
+ // We use a static empty node for the root/leftmost/rightmost of empty btrees
+ // in order to avoid branching in begin()/end().
+ struct alignas(node_type::Alignment()) EmptyNodeType : node_type {
+ using field_type = typename node_type::field_type;
+ node_type *parent;
+ field_type position = 0;
+ field_type count = 0;
+ // max_count must be != kInternalNodeMaxCount (so that this node is regarded
+ // as a leaf node). max_count() is never called when the tree is empty.
+ field_type max_count = node_type::kInternalNodeMaxCount + 1;
+
+ constexpr EmptyNodeType(node_type *p) : parent(p) {}
+ };
+
+ static node_type *EmptyNode() {
+ static constexpr EmptyNodeType empty_node(
+ const_cast<EmptyNodeType *>(&empty_node));
+ return const_cast<EmptyNodeType *>(&empty_node);
+ }
+
+ constexpr static int kNodeValues = node_type::kNodeValues;
+ constexpr static int kMinNodeValues = kNodeValues / 2;
+ constexpr static int kValueSize = node_type::kValueSize;
+
+ // A helper class to get the empty base class optimization for 0-size
+ // allocators. Base is allocator_type.
+ // (e.g. empty_base_handle<key_compare, allocator_type, node_type*>). If Base is
+ // 0-size, the compiler doesn't have to reserve any space for it and
+ // sizeof(empty_base_handle) will simply be sizeof(Data). Google [empty base
+ // class optimization] for more details.
+ template <typename Base1, typename Base2, typename Data>
+ struct empty_base_handle : public Base1, Base2 {
+ empty_base_handle(const Base1 &b1, const Base2 &b2, const Data &d)
+ : Base1(b1),
+ Base2(b2),
+ data(d) {}
+ Data data;
+ };
+
+ struct node_stats {
+ using size_type = typename Params::size_type;
+
+ node_stats(size_type l, size_type i)
+ : leaf_nodes(l),
+ internal_nodes(i) {
+ }
+
+ node_stats& operator+=(const node_stats &x) {
+ leaf_nodes += x.leaf_nodes;
+ internal_nodes += x.internal_nodes;
+ return *this;
+ }
+
+ size_type leaf_nodes;
+ size_type internal_nodes;
+ };
+
+ public:
+ using key_type = typename Params::key_type;
+ using value_type = typename Params::value_type;
+ using size_type = typename Params::size_type;
+ using difference_type = typename Params::difference_type;
+ using key_compare = typename Params::key_compare;
+ using value_compare = typename Params::value_compare;
+ using allocator_type = typename Params::allocator_type;
+ using reference = typename Params::reference;
+ using const_reference = typename Params::const_reference;
+ using pointer = typename Params::pointer;
+ using const_pointer = typename Params::const_pointer;
+ using iterator = btree_iterator<node_type, reference, pointer>;
+ using const_iterator = typename iterator::const_iterator;
+ using reverse_iterator = std::reverse_iterator<iterator>;
+ using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+
+ // Internal types made public for use by btree_container types.
+ using params_type = Params;
+
+ private:
+ // For use in copy_or_move_values_in_order.
+ const value_type &maybe_move_from_iterator(const_iterator x) { return *x; }
+ value_type &&maybe_move_from_iterator(iterator x) { return std::move(*x); }
+
+ // Copies or moves (depending on the template parameter) the values in
+ // x into this btree in their order in x. This btree must be empty before this
+ // method is called. This method is used in copy construction, copy
+ // assignment, and move assignment.
+ template <typename Btree>
+ void copy_or_move_values_in_order(Btree *x);
+
+ // Validates that various assumptions/requirements are true at compile time.
+ constexpr static bool static_assert_validation();
+
+ public:
+ btree(const key_compare &comp, const allocator_type &alloc);
+
+ btree(const btree &x);
+ btree(btree &&x) noexcept
+ : root_(std::move(x.root_)),
+ rightmost_(std::exchange(x.rightmost_, EmptyNode())),
+ size_(std::exchange(x.size_, 0)) {
+ x.mutable_root() = EmptyNode();
+ }
+
+ ~btree() {
+ // Put static_asserts in destructor to avoid triggering them before the type
+ // is complete.
+ static_assert(static_assert_validation(), "This call must be elided.");
+ clear();
+ }
+
+ // Assign the contents of x to *this.
+ btree &operator=(const btree &x);
+ btree &operator=(btree &&x) noexcept;
+
+ iterator begin() {
+ return iterator(leftmost(), 0);
+ }
+ const_iterator begin() const {
+ return const_iterator(leftmost(), 0);
+ }
+ iterator end() {
+ return iterator(rightmost_, rightmost_->count());
+ }
+ const_iterator end() const {
+ return const_iterator(rightmost_, rightmost_->count());
+ }
+ reverse_iterator rbegin() {
+ return reverse_iterator(end());
+ }
+ const_reverse_iterator rbegin() const {
+ return const_reverse_iterator(end());
+ }
+ reverse_iterator rend() {
+ return reverse_iterator(begin());
+ }
+ const_reverse_iterator rend() const {
+ return const_reverse_iterator(begin());
+ }
+
+ // Finds the first element whose key is not less than key.
+ template <typename K>
+ iterator lower_bound(const K &key) {
+ return internal_end(internal_lower_bound(key));
+ }
+ template <typename K>
+ const_iterator lower_bound(const K &key) const {
+ return internal_end(internal_lower_bound(key));
+ }
+
+ // Finds the first element whose key is greater than key.
+ template <typename K>
+ iterator upper_bound(const K &key) {
+ return internal_end(internal_upper_bound(key));
+ }
+ template <typename K>
+ const_iterator upper_bound(const K &key) const {
+ return internal_end(internal_upper_bound(key));
+ }
+
+ // Finds the range of values which compare equal to key. The first member of
+ // the returned pair is equal to lower_bound(key). The second member pair of
+ // the pair is equal to upper_bound(key).
+ template <typename K>
+ std::pair<iterator, iterator> equal_range(const K &key) {
+ return {lower_bound(key), upper_bound(key)};
+ }
+ template <typename K>
+ std::pair<const_iterator, const_iterator> equal_range(const K &key) const {
+ return {lower_bound(key), upper_bound(key)};
+ }
+
+ // Inserts a value into the btree only if it does not already exist. The
+ // boolean return value indicates whether insertion succeeded or failed.
+ // Requirement: if `key` already exists in the btree, does not consume `args`.
+ // Requirement: `key` is never referenced after consuming `args`.
+ template <typename... Args>
+ std::pair<iterator, bool> insert_unique(const key_type &key, Args &&... args);
+
+ // Inserts with hint. Checks to see if the value should be placed immediately
+ // before `position` in the tree. If so, then the insertion will take
+ // amortized constant time. If not, the insertion will take amortized
+ // logarithmic time as if a call to insert_unique() were made.
+ // Requirement: if `key` already exists in the btree, does not consume `args`.
+ // Requirement: `key` is never referenced after consuming `args`.
+ template <typename... Args>
+ std::pair<iterator, bool> insert_hint_unique(iterator position,
+ const key_type &key,
+ Args &&... args);
+
+ // Insert a range of values into the btree.
+ template <typename InputIterator>
+ void insert_iterator_unique(InputIterator b, InputIterator e);
+
+ // Inserts a value into the btree.
+ template <typename ValueType>
+ iterator insert_multi(const key_type &key, ValueType &&v);
+
+ // Inserts a value into the btree.
+ template <typename ValueType>
+ iterator insert_multi(ValueType &&v) {
+ return insert_multi(params_type::key(v), std::forward<ValueType>(v));
+ }
+
+ // Insert with hint. Check to see if the value should be placed immediately
+ // before position in the tree. If it does, then the insertion will take
+ // amortized constant time. If not, the insertion will take amortized
+ // logarithmic time as if a call to insert_multi(v) were made.
+ template <typename ValueType>
+ iterator insert_hint_multi(iterator position, ValueType &&v);
+
+ // Insert a range of values into the btree.
+ template <typename InputIterator>
+ void insert_iterator_multi(InputIterator b, InputIterator e);
+
+ // Erase the specified iterator from the btree. The iterator must be valid
+ // (i.e. not equal to end()). Return an iterator pointing to the node after
+ // the one that was erased (or end() if none exists).
+ // Requirement: does not read the value at `*iter`.
+ iterator erase(iterator iter);
+
+ // Erases range. Returns the number of keys erased and an iterator pointing
+ // to the element after the last erased element.
+ std::pair<size_type, iterator> erase(iterator begin, iterator end);
+
+ // Erases the specified key from the btree. Returns 1 if an element was
+ // erased and 0 otherwise.
+ template <typename K>
+ size_type erase_unique(const K &key);
+
+ // Erases all of the entries matching the specified key from the
+ // btree. Returns the number of elements erased.
+ template <typename K>
+ size_type erase_multi(const K &key);
+
+ // Finds the iterator corresponding to a key or returns end() if the key is
+ // not present.
+ template <typename K>
+ iterator find(const K &key) {
+ return internal_end(internal_find(key));
+ }
+ template <typename K>
+ const_iterator find(const K &key) const {
+ return internal_end(internal_find(key));
+ }
+
+ // Returns a count of the number of times the key appears in the btree.
+ template <typename K>
+ size_type count_unique(const K &key) const {
+ const iterator begin = internal_find(key);
+ if (begin.node == nullptr) {
+ // The key doesn't exist in the tree.
+ return 0;
+ }
+ return 1;
+ }
+ // Returns a count of the number of times the key appears in the btree.
+ template <typename K>
+ size_type count_multi(const K &key) const {
+ const auto range = equal_range(key);
+ return std::distance(range.first, range.second);
+ }
+
+ // Clear the btree, deleting all of the values it contains.
+ void clear();
+
+ // Swap the contents of *this and x.
+ void swap(btree &x);
+
+ const key_compare &key_comp() const noexcept {
+ return *static_cast<const key_compare*>(&root_);
+ }
+ template <typename K, typename LK>
+ bool compare_keys(const K &x, const LK &y) const {
+ return compare_result_as_less_than(key_comp()(x, y));
+ }
+
+ // Verifies the structure of the btree.
+ void verify() const;
+
+ // Size routines.
+ size_type size() const { return size_; }
+ size_type max_size() const { return std::numeric_limits<size_type>::max(); }
+ bool empty() const { return size_ == 0; }
+
+ // The height of the btree. An empty tree will have height 0.
+ size_type height() const {
+ size_type h = 0;
+ if (!empty()) {
+ // Count the length of the chain from the leftmost node up to the
+ // root. We actually count from the root back around to the level below
+ // the root, but the calculation is the same because of the circularity
+ // of that traversal.
+ const node_type *n = root();
+ do {
+ ++h;
+ n = n->parent();
+ } while (n != root());
+ }
+ return h;
+ }
+
+ // The number of internal, leaf and total nodes used by the btree.
+ size_type leaf_nodes() const {
+ return internal_stats(root()).leaf_nodes;
+ }
+ size_type internal_nodes() const {
+ return internal_stats(root()).internal_nodes;
+ }
+ size_type nodes() const {
+ node_stats stats = internal_stats(root());
+ return stats.leaf_nodes + stats.internal_nodes;
+ }
+
+ // The total number of bytes used by the btree.
+ size_type bytes_used() const {
+ node_stats stats = internal_stats(root());
+ if (stats.leaf_nodes == 1 && stats.internal_nodes == 0) {
+ return sizeof(*this) +
+ node_type::LeafSize(root()->max_count());
+ } else {
+ return sizeof(*this) +
+ stats.leaf_nodes * node_type::LeafSize() +
+ stats.internal_nodes * node_type::InternalSize();
+ }
+ }
+
+ // The average number of bytes used per value stored in the btree.
+ static double average_bytes_per_value() {
+ // Returns the number of bytes per value on a leaf node that is 75%
+ // full. Experimentally, this matches up nicely with the computed number of
+ // bytes per value in trees that had their values inserted in random order.
+ return node_type::LeafSize() / (kNodeValues * 0.75);
+ }
+
+ // The fullness of the btree. Computed as the number of elements in the btree
+ // divided by the maximum number of elements a tree with the current number
+ // of nodes could hold. A value of 1 indicates perfect space
+ // utilization. Smaller values indicate space wastage.
+ // Returns 0 for empty trees.
+ double fullness() const {
+ if (empty()) return 0.0;
+ return static_cast<double>(size()) / (nodes() * kNodeValues);
+ }
+ // The overhead of the btree structure in bytes per node. Computed as the
+ // total number of bytes used by the btree minus the number of bytes used for
+ // storing elements divided by the number of elements.
+ // Returns 0 for empty trees.
+ double overhead() const {
+ if (empty()) return 0.0;
+ return (bytes_used() - size() * sizeof(value_type)) /
+ static_cast<double>(size());
+ }
+
+ // The allocator used by the btree.
+ allocator_type get_allocator() const {
+ return allocator();
+ }
+
+ private:
+ // Internal accessor routines.
+ node_type *root() { return root_.data; }
+ const node_type *root() const { return root_.data; }
+ node_type *&mutable_root() { return root_.data; }
+ key_compare *mutable_key_comp() noexcept {
+ return static_cast<key_compare*>(&root_);
+ }
+
+ node_type* rightmost() {
+ return rightmost_;
+ }
+ const node_type* rightmost() const {
+ return rightmost_;
+ }
+ // The leftmost node is stored as the parent of the root node.
+ node_type* leftmost() { return root() ? root()->parent() : NULL; }
+ const node_type* leftmost() const { return root() ? root()->parent() : NULL; }
+
+ // The size of the tree is stored in the root node.
+ size_type* mutable_size() { return root()->mutable_size(); }
+
+ // Allocator routines.
+ allocator_type* mutable_allocator() noexcept {
+ return static_cast<allocator_type*>(&root_);
+ }
+ const allocator_type& allocator() const noexcept {
+ return *static_cast<const allocator_type*>(&root_);
+ }
+
+ node_type *allocate(const size_type size) {
+ using aligned_alloc_t =
+ AlignedAlloc<node_type::Alignment(), allocator_type>;
+ return static_cast<node_type*>(
+ aligned_alloc_t::allocate(mutable_allocator(), size));
+ }
+
+ // Node creation/deletion routines.
+ node_type* new_internal_node(node_type *parent) {
+ node_type *p = allocate(node_type::InternalSize());
+ return node_type::init_internal(p, parent);
+ }
+ node_type* new_leaf_node(node_type *parent) {
+ node_type *p = allocate(node_type::LeafSize());
+ return node_type::init_leaf(p, parent, kNodeValues);
+ }
+ node_type *new_leaf_root_node(const int max_count) {
+ node_type *p = allocate(node_type::LeafSize(max_count));
+ return node_type::init_leaf(p, p, max_count);
+ }
+
+ // Deletion helper routines.
+ void erase_same_node(iterator begin, iterator end);
+ iterator erase_from_leaf_node(iterator begin, size_type to_erase);
+ iterator rebalance_after_delete(iterator iter);
+
+ // Deallocates a node of a certain size in bytes using the allocator.
+ void deallocate(const size_type size, node_type *node) {
+ using aligned_alloc_t =
+ AlignedAlloc<node_type::Alignment(), allocator_type>;
+ aligned_alloc_t::deallocate(mutable_allocator(), node, size);
+ }
+
+ void delete_internal_node(node_type *node) {
+ node->destroy(mutable_allocator());
+ deallocate(node_type::InternalSize(), node);
+ }
+ void delete_leaf_node(node_type *node) {
+ node->destroy(mutable_allocator());
+ deallocate(node_type::LeafSize(node->max_count()), node);
+ }
+
+ // Rebalances or splits the node iter points to.
+ void rebalance_or_split(iterator *iter);
+
+ // Merges the values of left, right and the delimiting key on their parent
+ // onto left, removing the delimiting key and deleting right.
+ void merge_nodes(node_type *left, node_type *right);
+
+ // Tries to merge node with its left or right sibling, and failing that,
+ // rebalance with its left or right sibling. Returns true if a merge
+ // occurred, at which point it is no longer valid to access node. Returns
+ // false if no merging took place.
+ bool try_merge_or_rebalance(iterator *iter);
+
+ // Tries to shrink the height of the tree by 1.
+ void try_shrink();
+
+ iterator internal_end(iterator iter) {
+ return iter.node != nullptr ? iter : end();
+ }
+ const_iterator internal_end(const_iterator iter) const {
+ return iter.node != nullptr ? iter : end();
+ }
+
+ // Emplaces a value into the btree immediately before iter. Requires that
+ // key(v) <= iter.key() and (--iter).key() <= key(v).
+ template <typename... Args>
+ iterator internal_emplace(iterator iter, Args &&... args);
+
+ // Returns an iterator pointing to the first value >= the value "iter" is
+ // pointing at. Note that "iter" might be pointing to an invalid location as
+ // iter.position == iter.node->count(). This routine simply moves iter up in
+ // the tree to a valid location.
+ // Requires: iter.node is non-null.
+ template <typename IterType>
+ static IterType internal_last(IterType iter);
+
+ // Returns an iterator pointing to the leaf position at which key would
+ // reside in the tree. We provide 2 versions of internal_locate. The first
+ // version uses a less-than comparator and is incapable of distinguishing when
+ // there is an exact match. The second version is for the key-compare-to
+ // specialization and distinguishes exact matches. The key-compare-to
+ // specialization allows the caller to avoid a subsequent comparison to
+ // determine if an exact match was made, which is important for keys with
+ // expensive comparison, such as strings.
+ template <typename K>
+ SearchResult<iterator, is_key_compare_to::value> internal_locate(
+ const K &key) const;
+
+ template <typename K>
+ SearchResult<iterator, false> internal_locate_impl(
+ const K &key, std::false_type /* IsCompareTo */) const;
+
+ template <typename K>
+ SearchResult<iterator, true> internal_locate_impl(
+ const K &key, std::true_type /* IsCompareTo */) const;
+
+ // Internal routine which implements lower_bound().
+ template <typename K>
+ iterator internal_lower_bound(const K &key) const;
+
+ // Internal routine which implements upper_bound().
+ template <typename K>
+ iterator internal_upper_bound(const K &key) const;
+
+ // Internal routine which implements find().
+ template <typename K>
+ iterator internal_find(const K &key) const;
+
+ // Deletes a node and all of its children.
+ void internal_clear(node_type *node);
+
+ // Verifies the tree structure of node.
+ int internal_verify(const node_type *node,
+ const key_type *lo, const key_type *hi) const;
+
+ node_stats internal_stats(const node_type *node) const {
+ // The root can be a static empty node.
+ if (node == nullptr || (node == root() && empty())) {
+ return node_stats(0, 0);
+ }
+ if (node->leaf()) {
+ return node_stats(1, 0);
+ }
+ node_stats res(0, 1);
+ for (int i = 0; i <= node->count(); ++i) {
+ res += internal_stats(node->child(i));
+ }
+ return res;
+ }
+
+ private:
+ empty_base_handle<key_compare, allocator_type, node_type*> root_;
+
+ // A pointer to the rightmost node. Note that the leftmost node is stored as
+ // the root's parent.
+ node_type *rightmost_;
+
+ // Number of values.
+ size_type size_;
+};
+
+////
+// btree_node methods
+template <typename P>
+template <typename... Args>
+inline void btree_node<P>::emplace_value(const size_type i,
+ allocator_type *alloc,
+ Args &&... args) {
+ assert(i <= count());
+ // Shift old values to create space for new value and then construct it in
+ // place.
+ if (i < count()) {
+ value_init(count(), alloc, slot(count() - 1));
+ std::copy_backward(std::make_move_iterator(slot(i)),
+ std::make_move_iterator(slot(count() - 1)),
+ slot(count()));
+ value_destroy(i, alloc);
+ }
+ value_init(i, alloc, std::forward<Args>(args)...);
+ set_count(count() + 1);
+
+ if (!leaf() && count() > i + 1) {
+ for (int j = count(); j > i + 1; --j) {
+ set_child(j, child(j - 1));
+ }
+ clear_child(i + 1);
+ }
+}
+
+template <typename P>
+inline void btree_node<P>::remove_value(const int i, allocator_type *alloc) {
+ if (!leaf() && count() > i + 1) {
+ assert(child(i + 1)->count() == 0);
+ for (size_type j = i + 1; j < count(); ++j) {
+ set_child(j, child(j + 1));
+ }
+ clear_child(count());
+ }
+
+ remove_values_ignore_children(i, /*to_erase=*/1, alloc);
+}
+
+template <typename P>
+inline void btree_node<P>::remove_values_ignore_children(
+ const int i, const int to_erase, allocator_type *alloc) {
+ assert(to_erase >= 0);
+ std::copy(std::make_move_iterator(slot(i + to_erase)),
+ std::make_move_iterator(slot(count())),
+ slot(i));
+ value_destroy_n(count() - to_erase, to_erase, alloc);
+ set_count(count() - to_erase);
+}
+
+template <typename P>
+void btree_node<P>::rebalance_right_to_left(const int to_move,
+ btree_node *right,
+ allocator_type *alloc) {
+ assert(parent() == right->parent());
+ assert(position() + 1 == right->position());
+ assert(right->count() >= count());
+ assert(to_move >= 1);
+ assert(to_move <= right->count());
+
+ // 1) Move the delimiting value in the parent to the left node.
+ value_init(count(), alloc, parent()->slot(position()));
+
+ // 2) Move the (to_move - 1) values from the right node to the left node.
+ right->uninitialized_move_n(to_move - 1, 0, count() + 1, this, alloc);
+
+ // 3) Move the new delimiting value to the parent from the right node.
+ params_type::move(alloc, right->slot(to_move - 1),
+ parent()->slot(position()));
+
+ // 4) Shift the values in the right node to their correct position.
+ std::copy(std::make_move_iterator(right->slot(to_move)),
+ std::make_move_iterator(right->slot(right->count())),
+ right->slot(0));
+
+ // 5) Destroy the now-empty to_move entries in the right node.
+ right->value_destroy_n(right->count() - to_move, to_move, alloc);
+
+ if (!leaf()) {
+ // Move the child pointers from the right to the left node.
+ for (int i = 0; i < to_move; ++i) {
+ init_child(count() + i + 1, right->child(i));
+ }
+ for (int i = 0; i <= right->count() - to_move; ++i) {
+ assert(i + to_move <= right->max_count());
+ right->init_child(i, right->child(i + to_move));
+ right->clear_child(i + to_move);
+ }
+ }
+
+ // Fixup the counts on the left and right nodes.
+ set_count(count() + to_move);
+ right->set_count(right->count() - to_move);
+}
+
+template <typename P>
+void btree_node<P>::rebalance_left_to_right(const int to_move,
+ btree_node *right,
+ allocator_type *alloc) {
+ assert(parent() == right->parent());
+ assert(position() + 1 == right->position());
+ assert(count() >= right->count());
+ assert(to_move >= 1);
+ assert(to_move <= count());
+
+ // Values in the right node are shifted to the right to make room for the
+ // new to_move values. Then, the delimiting value in the parent and the
+ // other (to_move - 1) values in the left node are moved into the right node.
+ // Lastly, a new delimiting value is moved from the left node into the
+ // parent, and the remaining empty left node entries are destroyed.
+
+ if (right->count() >= to_move) {
+ // The original location of the right->count() values are sufficient to hold
+ // the new to_move entries from the parent and left node.
+
+ // 1) Shift existing values in the right node to their correct positions.
+ right->uninitialized_move_n(to_move, right->count() - to_move,
+ right->count(), right, alloc);
+ std::copy_backward(std::make_move_iterator(right->slot(0)),
+ std::make_move_iterator(right->slot(right->count() - to_move)),
+ right->slot(right->count()));
+
+ // 2) Move the delimiting value in the parent to the right node.
+ params_type::move(alloc, parent()->slot(position()),
+ right->slot(to_move - 1));
+
+ // 3) Move the (to_move - 1) values from the left node to the right node.
+ std::copy(std::make_move_iterator(slot(count() - (to_move - 1))),
+ std::make_move_iterator(slot(count())),
+ right->slot(0));
+ } else {
+ // The right node does not have enough initialized space to hold the new
+ // to_move entries, so part of them will move to uninitialized space.
+
+ // 1) Shift existing values in the right node to their correct positions.
+ right->uninitialized_move_n(right->count(), 0, to_move, right, alloc);
+
+ // 2) Move the delimiting value in the parent to the right node.
+ right->value_init(to_move - 1, alloc, parent()->slot(position()));
+
+ // 3) Move the (to_move - 1) values from the left node to the right node.
+ const size_type uninitialized_remaining = to_move - right->count() - 1;
+ uninitialized_move_n(uninitialized_remaining,
+ count() - uninitialized_remaining, right->count(),
+ right, alloc);
+ std::copy(std::make_move_iterator(slot(count() - (to_move - 1))),
+ std::make_move_iterator(slot(count() - uninitialized_remaining)),
+ right->slot(0));
+ }
+
+ // 4) Move the new delimiting value to the parent from the left node.
+ params_type::move(alloc, slot(count() - to_move), parent()->slot(position()));
+
+ // 5) Destroy the now-empty to_move entries in the left node.
+ value_destroy_n(count() - to_move, to_move, alloc);
+
+ if (!leaf()) {
+ // Move the child pointers from the left to the right node.
+ for (int i = right->count(); i >= 0; --i) {
+ right->init_child(i + to_move, right->child(i));
+ right->clear_child(i);
+ }
+ for (int i = 1; i <= to_move; ++i) {
+ right->init_child(i - 1, child(count() - to_move + i));
+ clear_child(count() - to_move + i);
+ }
+ }
+
+ // Fixup the counts on the left and right nodes.
+ set_count(count() - to_move);
+ right->set_count(right->count() + to_move);
+}
+
+template <typename P>
+void btree_node<P>::split(const int insert_position, btree_node *dest,
+ allocator_type *alloc) {
+ assert(dest->count() == 0);
+ assert(max_count() == kNodeValues);
+
+ // We bias the split based on the position being inserted. If we're
+ // inserting at the beginning of the left node then bias the split to put
+ // more values on the right node. If we're inserting at the end of the
+ // right node then bias the split to put more values on the left node.
+ if (insert_position == 0) {
+ dest->set_count(count() - 1);
+ } else if (insert_position == kNodeValues) {
+ dest->set_count(0);
+ } else {
+ dest->set_count(count() / 2);
+ }
+ set_count(count() - dest->count());
+ assert(count() >= 1);
+
+ // Move values from the left sibling to the right sibling.
+ uninitialized_move_n(dest->count(), count(), 0, dest, alloc);
+
+ // Destroy the now-empty entries in the left node.
+ value_destroy_n(count(), dest->count(), alloc);
+
+ // The split key is the largest value in the left sibling.
+ set_count(count() - 1);
+ parent()->emplace_value(position(), alloc, slot(count()));
+ value_destroy(count(), alloc);
+ parent()->init_child(position() + 1, dest);
+
+ if (!leaf()) {
+ for (int i = 0; i <= dest->count(); ++i) {
+ assert(child(count() + i + 1) != nullptr);
+ dest->init_child(i, child(count() + i + 1));
+ clear_child(count() + i + 1);
+ }
+ }
+}
+
+template <typename P>
+void btree_node<P>::merge(btree_node *src, allocator_type *alloc) {
+ assert(parent() == src->parent());
+ assert(position() + 1 == src->position());
+
+ // Move the delimiting value to the left node.
+ value_init(count(), alloc, parent()->slot(position()));
+
+ // Move the values from the right to the left node.
+ src->uninitialized_move_n(src->count(), 0, count() + 1, this, alloc);
+
+ // Destroy the now-empty entries in the right node.
+ src->value_destroy_n(0, src->count(), alloc);
+
+ if (!leaf()) {
+ // Move the child pointers from the right to the left node.
+ for (int i = 0; i <= src->count(); ++i) {
+ init_child(count() + i + 1, src->child(i));
+ src->clear_child(i);
+ }
+ }
+
+ // Fixup the counts on the src and dest nodes.
+ set_count(1 + count() + src->count());
+ src->set_count(0);
+
+ // Remove the value on the parent node.
+ parent()->remove_value(position(), alloc);
+}
+
+template <typename P>
+void btree_node<P>::swap(btree_node *x, allocator_type *alloc) {
+ using std::swap;
+ assert(leaf() == x->leaf());
+
+ // Determine which is the smaller/larger node.
+ btree_node *smaller = this, *larger = x;
+ if (smaller->count() > larger->count()) {
+ swap(smaller, larger);
+ }
+
+ // Swap the values.
+ std::swap_ranges(smaller->slot(0), smaller->slot(smaller->count()),
+ larger->slot(0));
+
+ // Move values that can't be swapped.
+ const size_type to_move = larger->count() - smaller->count();
+ larger->uninitialized_move_n(to_move, smaller->count(), smaller->count(),
+ smaller, alloc);
+ larger->value_destroy_n(smaller->count(), to_move, alloc);
+
+ if (!leaf()) {
+ // Swap the child pointers.
+ std::swap_ranges(&smaller->mutable_child(0),
+ &smaller->mutable_child(smaller->count() + 1),
+ &larger->mutable_child(0));
+ // Update swapped children's parent pointers.
+ int i = 0;
+ for (; i <= smaller->count(); ++i) {
+ smaller->child(i)->set_parent(smaller);
+ larger->child(i)->set_parent(larger);
+ }
+ // Move the child pointers that couldn't be swapped.
+ for (; i <= larger->count(); ++i) {
+ smaller->init_child(i, larger->child(i));
+ larger->clear_child(i);
+ }
+ }
+
+ // Swap the counts.
+ swap(mutable_count(), x->mutable_count());
+}
+
+////
+// btree_iterator methods
+template <typename N, typename R, typename P>
+void btree_iterator<N, R, P>::increment_slow() {
+ if (node->leaf()) {
+ assert(position >= node->count());
+ btree_iterator save(*this);
+ while (position == node->count() && !node->is_root()) {
+ assert(node->parent()->child(node->position()) == node);
+ position = node->position();
+ node = node->parent();
+ }
+ if (position == node->count()) {
+ *this = save;
+ }
+ } else {
+ assert(position < node->count());
+ node = node->child(position + 1);
+ while (!node->leaf()) {
+ node = node->child(0);
+ }
+ position = 0;
+ }
+}
+
+template <typename N, typename R, typename P>
+void btree_iterator<N, R, P>::decrement_slow() {
+ if (node->leaf()) {
+ assert(position <= -1);
+ btree_iterator save(*this);
+ while (position < 0 && !node->is_root()) {
+ assert(node->parent()->child(node->position()) == node);
+ position = node->position() - 1;
+ node = node->parent();
+ }
+ if (position < 0) {
+ *this = save;
+ }
+ } else {
+ assert(position >= 0);
+ node = node->child(position);
+ while (!node->leaf()) {
+ node = node->child(node->count());
+ }
+ position = node->count() - 1;
+ }
+}
+
+////
+// btree methods
+template <typename P>
+template <typename Btree>
+void btree<P>::copy_or_move_values_in_order(Btree *x) {
+ static_assert(std::is_same_v<btree, Btree>||
+ std::is_same_v<const btree, Btree>,
+ "Btree type must be same or const.");
+ assert(empty());
+
+ // We can avoid key comparisons because we know the order of the
+ // values is the same order we'll store them in.
+ auto iter = x->begin();
+ if (iter == x->end()) return;
+ insert_multi(maybe_move_from_iterator(iter));
+ ++iter;
+ for (; iter != x->end(); ++iter) {
+ // If the btree is not empty, we can just insert the new value at the end
+ // of the tree.
+ internal_emplace(end(), maybe_move_from_iterator(iter));
+ }
+}
+
+template <typename P>
+constexpr bool btree<P>::static_assert_validation() {
+ static_assert(std::is_nothrow_copy_constructible_v<key_compare>,
+ "Key comparison must be nothrow copy constructible");
+ static_assert(std::is_nothrow_copy_constructible_v<allocator_type>,
+ "Allocator must be nothrow copy constructible");
+ static_assert(std::is_trivially_copyable_v<iterator>,
+ "iterator not trivially copyable.");
+
+ // Note: We assert that kTargetValues, which is computed from
+ // Params::kTargetNodeSize, must fit the base_fields::field_type.
+ static_assert(
+ kNodeValues < (1 << (8 * sizeof(typename node_type::field_type))),
+ "target node size too large");
+
+ // Verify that key_compare returns an absl::{weak,strong}_ordering or bool.
+ using compare_result_type =
+ std::invoke_result_t<key_compare, key_type, key_type>;
+ static_assert(
+ std::is_same_v<compare_result_type, bool> ||
+ std::is_signed_v<compare_result_type>,
+ "key comparison function must return a signed value or "
+ "bool.");
+
+ // Test the assumption made in setting kNodeValueSpace.
+ static_assert(node_type::MinimumOverhead() >= sizeof(void *) + 4,
+ "node space assumption incorrect");
+
+ return true;
+}
+
+template <typename P>
+btree<P>::btree(const key_compare &comp, const allocator_type &alloc)
+ : root_(comp, alloc, EmptyNode()), rightmost_(EmptyNode()), size_(0) {}
+
+template <typename P>
+btree<P>::btree(const btree &x) : btree(x.key_comp(), x.allocator()) {
+ copy_or_move_values_in_order(&x);
+}
+
+template <typename P>
+template <typename... Args>
+auto btree<P>::insert_unique(const key_type &key, Args &&... args)
+ -> std::pair<iterator, bool> {
+ if (empty()) {
+ mutable_root() = rightmost_ = new_leaf_root_node(1);
+ }
+
+ auto res = internal_locate(key);
+ iterator &iter = res.value;
+
+ if constexpr (res.has_match) {
+ if (res.IsEq()) {
+ // The key already exists in the tree, do nothing.
+ return {iter, false};
+ }
+ } else {
+ iterator last = internal_last(iter);
+ if (last.node && !compare_keys(key, last.key())) {
+ // The key already exists in the tree, do nothing.
+ return {last, false};
+ }
+ }
+ return {internal_emplace(iter, std::forward<Args>(args)...), true};
+}
+
+template <typename P>
+template <typename... Args>
+inline auto btree<P>::insert_hint_unique(iterator position, const key_type &key,
+ Args &&... args)
+ -> std::pair<iterator, bool> {
+ if (!empty()) {
+ if (position == end() || compare_keys(key, position.key())) {
+ iterator prev = position;
+ if (position == begin() || compare_keys((--prev).key(), key)) {
+ // prev.key() < key < position.key()
+ return {internal_emplace(position, std::forward<Args>(args)...), true};
+ }
+ } else if (compare_keys(position.key(), key)) {
+ ++position;
+ if (position == end() || compare_keys(key, position.key())) {
+ // {original `position`}.key() < key < {current `position`}.key()
+ return {internal_emplace(position, std::forward<Args>(args)...), true};
+ }
+ } else {
+ // position.key() == key
+ return {position, false};
+ }
+ }
+ return insert_unique(key, std::forward<Args>(args)...);
+}
+
+template <typename P>
+template <typename InputIterator>
+void btree<P>::insert_iterator_unique(InputIterator b, InputIterator e) {
+ for (; b != e; ++b) {
+ insert_hint_unique(end(), params_type::key(*b), *b);
+ }
+}
+
+template <typename P>
+template <typename ValueType>
+auto btree<P>::insert_multi(const key_type &key, ValueType&& v) -> iterator {
+ if (empty()) {
+ mutable_root() = rightmost_ = new_leaf_root_node(1);
+ }
+
+ iterator iter = internal_upper_bound(key);
+ if (iter.node == nullptr) {
+ iter = end();
+ }
+ return internal_emplace(iter, std::forward<ValueType>(v));
+}
+
+template <typename P>
+template <typename ValueType>
+auto btree<P>::insert_hint_multi(iterator position, ValueType &&v) -> iterator {
+ if (!empty()) {
+ const key_type &key = params_type::key(v);
+ if (position == end() || !compare_keys(position.key(), key)) {
+ iterator prev = position;
+ if (position == begin() || !compare_keys(key, (--prev).key())) {
+ // prev.key() <= key <= position.key()
+ return internal_emplace(position, std::forward<ValueType>(v));
+ }
+ } else {
+ iterator next = position;
+ ++next;
+ if (next == end() || !compare_keys(next.key(), key)) {
+ // position.key() < key <= next.key()
+ return internal_emplace(next, std::forward<ValueType>(v));
+ }
+ }
+ }
+ return insert_multi(std::forward<ValueType>(v));
+}
+
+template <typename P>
+template <typename InputIterator>
+void btree<P>::insert_iterator_multi(InputIterator b, InputIterator e) {
+ for (; b != e; ++b) {
+ insert_hint_multi(end(), *b);
+ }
+}
+
+template <typename P>
+auto btree<P>::operator=(const btree &x) -> btree & {
+ if (this != &x) {
+ clear();
+
+ *mutable_key_comp() = x.key_comp();
+ if constexpr (std::allocator_traits<
+ allocator_type>::propagate_on_container_copy_assignment::value) {
+ *mutable_allocator() = x.allocator();
+ }
+
+ copy_or_move_values_in_order(&x);
+ }
+ return *this;
+}
+
+template <typename P>
+auto btree<P>::operator=(btree &&x) noexcept -> btree & {
+ if (this != &x) {
+ clear();
+
+ using std::swap;
+ if constexpr (std::allocator_traits<
+ allocator_type>::propagate_on_container_copy_assignment::value) {
+ // Note: `root_` also contains the allocator and the key comparator.
+ swap(root_, x.root_);
+ swap(rightmost_, x.rightmost_);
+ swap(size_, x.size_);
+ } else {
+ if (allocator() == x.allocator()) {
+ swap(mutable_root(), x.mutable_root());
+ swap(*mutable_key_comp(), *x.mutable_key_comp());
+ swap(rightmost_, x.rightmost_);
+ swap(size_, x.size_);
+ } else {
+ // We aren't allowed to propagate the allocator and the allocator is
+ // different so we can't take over its memory. We must move each element
+ // individually. We need both `x` and `this` to have `x`s key comparator
+ // while moving the values so we can't swap the key comparators.
+ *mutable_key_comp() = x.key_comp();
+ copy_or_move_values_in_order(&x);
+ }
+ }
+ }
+ return *this;
+}
+
+template <typename P>
+auto btree<P>::erase(iterator iter) -> iterator {
+ bool internal_delete = false;
+ if (!iter.node->leaf()) {
+ // Deletion of a value on an internal node. First, move the largest value
+ // from our left child here, then delete that position (in remove_value()
+ // below). We can get to the largest value from our left child by
+ // decrementing iter.
+ iterator internal_iter(iter);
+ --iter;
+ assert(iter.node->leaf());
+ params_type::move(mutable_allocator(), iter.node->slot(iter.position),
+ internal_iter.node->slot(internal_iter.position));
+ internal_delete = true;
+ }
+
+ // Delete the key from the leaf.
+ iter.node->remove_value(iter.position, mutable_allocator());
+ --size_;
+
+ // We want to return the next value after the one we just erased. If we
+ // erased from an internal node (internal_delete == true), then the next
+ // value is ++(++iter). If we erased from a leaf node (internal_delete ==
+ // false) then the next value is ++iter. Note that ++iter may point to an
+ // internal node and the value in the internal node may move to a leaf node
+ // (iter.node) when rebalancing is performed at the leaf level.
+
+ iterator res = rebalance_after_delete(iter);
+
+ // If we erased from an internal node, advance the iterator.
+ if (internal_delete) {
+ ++res;
+ }
+ return res;
+}
+
+template <typename P>
+auto btree<P>::rebalance_after_delete(iterator iter) -> iterator {
+ // Merge/rebalance as we walk back up the tree.
+ iterator res(iter);
+ bool first_iteration = true;
+ for (;;) {
+ if (iter.node == root()) {
+ try_shrink();
+ if (empty()) {
+ return end();
+ }
+ break;
+ }
+ if (iter.node->count() >= kMinNodeValues) {
+ break;
+ }
+ bool merged = try_merge_or_rebalance(&iter);
+ // On the first iteration, we should update `res` with `iter` because `res`
+ // may have been invalidated.
+ if (first_iteration) {
+ res = iter;
+ first_iteration = false;
+ }
+ if (!merged) {
+ break;
+ }
+ iter.position = iter.node->position();
+ iter.node = iter.node->parent();
+ }
+
+ // Adjust our return value. If we're pointing at the end of a node, advance
+ // the iterator.
+ if (res.position == res.node->count()) {
+ res.position = res.node->count() - 1;
+ ++res;
+ }
+
+ return res;
+}
+
+template <typename P>
+auto btree<P>::erase(iterator begin, iterator end)
+ -> std::pair<size_type, iterator> {
+ difference_type count = std::distance(begin, end);
+ assert(count >= 0);
+
+ if (count == 0) {
+ return {0, begin};
+ }
+
+ if (count == size_) {
+ clear();
+ return {count, this->end()};
+ }
+
+ if (begin.node == end.node) {
+ erase_same_node(begin, end);
+ size_ -= count;
+ return {count, rebalance_after_delete(begin)};
+ }
+
+ const size_type target_size = size_ - count;
+ while (size_ > target_size) {
+ if (begin.node->leaf()) {
+ const size_type remaining_to_erase = size_ - target_size;
+ const size_type remaining_in_node = begin.node->count() - begin.position;
+ begin = erase_from_leaf_node(
+ begin, std::min(remaining_to_erase, remaining_in_node));
+ } else {
+ begin = erase(begin);
+ }
+ }
+ return {count, begin};
+}
+
+template <typename P>
+void btree<P>::erase_same_node(iterator begin, iterator end) {
+ assert(begin.node == end.node);
+ assert(end.position > begin.position);
+
+ node_type *node = begin.node;
+ size_type to_erase = end.position - begin.position;
+ if (!node->leaf()) {
+ // Delete all children between begin and end.
+ for (size_type i = 0; i < to_erase; ++i) {
+ internal_clear(node->child(begin.position + i + 1));
+ }
+ // Rotate children after end into new positions.
+ for (size_type i = begin.position + to_erase + 1; i <= node->count(); ++i) {
+ node->set_child(i - to_erase, node->child(i));
+ node->clear_child(i);
+ }
+ }
+ node->remove_values_ignore_children(begin.position, to_erase,
+ mutable_allocator());
+
+ // Do not need to update rightmost_, because
+ // * either end == this->end(), and therefore node == rightmost_, and still
+ // exists
+ // * or end != this->end(), and therefore rightmost_ hasn't been erased, since
+ // it wasn't covered in [begin, end)
+}
+
+template <typename P>
+auto btree<P>::erase_from_leaf_node(iterator begin, size_type to_erase)
+ -> iterator {
+ node_type *node = begin.node;
+ assert(node->leaf());
+ assert(node->count() > begin.position);
+ assert(begin.position + to_erase <= node->count());
+
+ node->remove_values_ignore_children(begin.position, to_erase,
+ mutable_allocator());
+
+ size_ -= to_erase;
+
+ return rebalance_after_delete(begin);
+}
+
+template <typename P>
+template <typename K>
+auto btree<P>::erase_unique(const K &key) -> size_type {
+ const iterator iter = internal_find(key);
+ if (iter.node == nullptr) {
+ // The key doesn't exist in the tree, return nothing done.
+ return 0;
+ }
+ erase(iter);
+ return 1;
+}
+
+template <typename P>
+template <typename K>
+auto btree<P>::erase_multi(const K &key) -> size_type {
+ const iterator begin = internal_lower_bound(key);
+ if (begin.node == nullptr) {
+ // The key doesn't exist in the tree, return nothing done.
+ return 0;
+ }
+ // Delete all of the keys between begin and upper_bound(key).
+ const iterator end = internal_end(internal_upper_bound(key));
+ return erase(begin, end).first;
+}
+
+template <typename P>
+void btree<P>::clear() {
+ if (!empty()) {
+ internal_clear(root());
+ }
+ mutable_root() = EmptyNode();
+ rightmost_ = EmptyNode();
+ size_ = 0;
+}
+
+template <typename P>
+void btree<P>::swap(btree &x) {
+ using std::swap;
+ if (std::allocator_traits<
+ allocator_type>::propagate_on_container_swap::value) {
+ // Note: `root_` also contains the allocator and the key comparator.
+ swap(root_, x.root_);
+ } else {
+ // It's undefined behavior if the allocators are unequal here.
+ assert(allocator() == x.allocator());
+ swap(mutable_root(), x.mutable_root());
+ swap(*mutable_key_comp(), *x.mutable_key_comp());
+ }
+ swap(rightmost_, x.rightmost_);
+ swap(size_, x.size_);
+}
+
+template <typename P>
+void btree<P>::verify() const {
+ assert(root() != nullptr);
+ assert(leftmost() != nullptr);
+ assert(rightmost_ != nullptr);
+ assert(empty() || size() == internal_verify(root(), nullptr, nullptr));
+ assert(leftmost() == (++const_iterator(root(), -1)).node);
+ assert(rightmost_ == (--const_iterator(root(), root()->count())).node);
+ assert(leftmost()->leaf());
+ assert(rightmost_->leaf());
+}
+
+template <typename P>
+void btree<P>::rebalance_or_split(iterator *iter) {
+ node_type *&node = iter->node;
+ int &insert_position = iter->position;
+ assert(node->count() == node->max_count());
+ assert(kNodeValues == node->max_count());
+
+ // First try to make room on the node by rebalancing.
+ node_type *parent = node->parent();
+ if (node != root()) {
+ if (node->position() > 0) {
+ // Try rebalancing with our left sibling.
+ node_type *left = parent->child(node->position() - 1);
+ assert(left->max_count() == kNodeValues);
+ if (left->count() < kNodeValues) {
+ // We bias rebalancing based on the position being inserted. If we're
+ // inserting at the end of the right node then we bias rebalancing to
+ // fill up the left node.
+ int to_move = (kNodeValues - left->count()) /
+ (1 + (insert_position < kNodeValues));
+ to_move = std::max(1, to_move);
+
+ if (((insert_position - to_move) >= 0) ||
+ ((left->count() + to_move) < kNodeValues)) {
+ left->rebalance_right_to_left(to_move, node, mutable_allocator());
+
+ assert(node->max_count() - node->count() == to_move);
+ insert_position = insert_position - to_move;
+ if (insert_position < 0) {
+ insert_position = insert_position + left->count() + 1;
+ node = left;
+ }
+
+ assert(node->count() < node->max_count());
+ return;
+ }
+ }
+ }
+
+ if (node->position() < parent->count()) {
+ // Try rebalancing with our right sibling.
+ node_type *right = parent->child(node->position() + 1);
+ assert(right->max_count() == kNodeValues);
+ if (right->count() < kNodeValues) {
+ // We bias rebalancing based on the position being inserted. If we're
+ // inserting at the beginning of the left node then we bias rebalancing
+ // to fill up the right node.
+ int to_move =
+ (kNodeValues - right->count()) / (1 + (insert_position > 0));
+ to_move = (std::max)(1, to_move);
+
+ if ((insert_position <= (node->count() - to_move)) ||
+ ((right->count() + to_move) < kNodeValues)) {
+ node->rebalance_left_to_right(to_move, right, mutable_allocator());
+
+ if (insert_position > node->count()) {
+ insert_position = insert_position - node->count() - 1;
+ node = right;
+ }
+
+ assert(node->count() < node->max_count());
+ return;
+ }
+ }
+ }
+
+ // Rebalancing failed, make sure there is room on the parent node for a new
+ // value.
+ assert(parent->max_count() == kNodeValues);
+ if (parent->count() == kNodeValues) {
+ iterator parent_iter(node->parent(), node->position());
+ rebalance_or_split(&parent_iter);
+ }
+ } else {
+ // Rebalancing not possible because this is the root node.
+ // Create a new root node and set the current root node as the child of the
+ // new root.
+ parent = new_internal_node(parent);
+ parent->init_child(0, root());
+ mutable_root() = parent;
+ // If the former root was a leaf node, then it's now the rightmost node.
+ assert(!parent->child(0)->leaf() || parent->child(0) == rightmost_);
+ }
+
+ // Split the node.
+ node_type *split_node;
+ if (node->leaf()) {
+ split_node = new_leaf_node(parent);
+ node->split(insert_position, split_node, mutable_allocator());
+ if (rightmost_ == node) rightmost_ = split_node;
+ } else {
+ split_node = new_internal_node(parent);
+ node->split(insert_position, split_node, mutable_allocator());
+ }
+
+ if (insert_position > node->count()) {
+ insert_position = insert_position - node->count() - 1;
+ node = split_node;
+ }
+}
+
+template <typename P>
+void btree<P>::merge_nodes(node_type *left, node_type *right) {
+ left->merge(right, mutable_allocator());
+ if (right->leaf()) {
+ if (rightmost_ == right) rightmost_ = left;
+ delete_leaf_node(right);
+ } else {
+ delete_internal_node(right);
+ }
+}
+
+template <typename P>
+bool btree<P>::try_merge_or_rebalance(iterator *iter) {
+ node_type *parent = iter->node->parent();
+ if (iter->node->position() > 0) {
+ // Try merging with our left sibling.
+ node_type *left = parent->child(iter->node->position() - 1);
+ assert(left->max_count() == kNodeValues);
+ if ((1 + left->count() + iter->node->count()) <= kNodeValues) {
+ iter->position += 1 + left->count();
+ merge_nodes(left, iter->node);
+ iter->node = left;
+ return true;
+ }
+ }
+ if (iter->node->position() < parent->count()) {
+ // Try merging with our right sibling.
+ node_type *right = parent->child(iter->node->position() + 1);
+ assert(right->max_count() == kNodeValues);
+ if ((1 + iter->node->count() + right->count()) <= kNodeValues) {
+ merge_nodes(iter->node, right);
+ return true;
+ }
+ // Try rebalancing with our right sibling. We don't perform rebalancing if
+ // we deleted the first element from iter->node and the node is not
+ // empty. This is a small optimization for the common pattern of deleting
+ // from the front of the tree.
+ if ((right->count() > kMinNodeValues) &&
+ ((iter->node->count() == 0) ||
+ (iter->position > 0))) {
+ int to_move = (right->count() - iter->node->count()) / 2;
+ to_move = std::min(to_move, right->count() - 1);
+ iter->node->rebalance_right_to_left(to_move, right, mutable_allocator());
+ return false;
+ }
+ }
+ if (iter->node->position() > 0) {
+ // Try rebalancing with our left sibling. We don't perform rebalancing if
+ // we deleted the last element from iter->node and the node is not
+ // empty. This is a small optimization for the common pattern of deleting
+ // from the back of the tree.
+ node_type *left = parent->child(iter->node->position() - 1);
+ if ((left->count() > kMinNodeValues) &&
+ ((iter->node->count() == 0) ||
+ (iter->position < iter->node->count()))) {
+ int to_move = (left->count() - iter->node->count()) / 2;
+ to_move = std::min(to_move, left->count() - 1);
+ left->rebalance_left_to_right(to_move, iter->node, mutable_allocator());
+ iter->position += to_move;
+ return false;
+ }
+ }
+ return false;
+}
+
+template <typename P>
+void btree<P>::try_shrink() {
+ if (root()->count() > 0) {
+ return;
+ }
+ // Deleted the last item on the root node, shrink the height of the tree.
+ if (root()->leaf()) {
+ assert(size() == 0);
+ delete_leaf_node(root());
+ mutable_root() = EmptyNode();
+ rightmost_ = EmptyNode();
+ } else {
+ node_type *child = root()->child(0);
+ child->make_root();
+ delete_internal_node(root());
+ mutable_root() = child;
+ }
+}
+
+template <typename P>
+template <typename IterType>
+inline IterType btree<P>::internal_last(IterType iter) {
+ assert(iter.node != nullptr);
+ while (iter.position == iter.node->count()) {
+ iter.position = iter.node->position();
+ iter.node = iter.node->parent();
+ if (iter.node->leaf()) {
+ iter.node = nullptr;
+ break;
+ }
+ }
+ return iter;
+}
+
+template <typename P>
+template <typename... Args>
+inline auto btree<P>::internal_emplace(iterator iter, Args &&... args)
+ -> iterator {
+ if (!iter.node->leaf()) {
+ // We can't insert on an internal node. Instead, we'll insert after the
+ // previous value which is guaranteed to be on a leaf node.
+ --iter;
+ ++iter.position;
+ }
+ const int max_count = iter.node->max_count();
+ if (iter.node->count() == max_count) {
+ // Make room in the leaf for the new item.
+ if (max_count < kNodeValues) {
+ // Insertion into the root where the root is smaller than the full node
+ // size. Simply grow the size of the root node.
+ assert(iter.node == root());
+ iter.node =
+ new_leaf_root_node(std::min(kNodeValues, 2 * max_count));
+ iter.node->swap(root(), mutable_allocator());
+ delete_leaf_node(root());
+ mutable_root() = iter.node;
+ rightmost_ = iter.node;
+ } else {
+ rebalance_or_split(&iter);
+ }
+ }
+ iter.node->emplace_value(iter.position, mutable_allocator(),
+ std::forward<Args>(args)...);
+ ++size_;
+ return iter;
+}
+
+template <typename P>
+template <typename K>
+inline auto btree<P>::internal_locate(const K &key) const
+ -> SearchResult<iterator, is_key_compare_to::value> {
+ return internal_locate_impl(key, is_key_compare_to());
+}
+
+template <typename P>
+template <typename K>
+inline auto btree<P>::internal_locate_impl(
+ const K &key, std::false_type /* IsCompareTo */) const
+ -> SearchResult<iterator, false> {
+ iterator iter(const_cast<node_type *>(root()), 0);
+ for (;;) {
+ iter.position = iter.node->lower_bound(key, key_comp()).value;
+ // NOTE: we don't need to walk all the way down the tree if the keys are
+ // equal, but determining equality would require doing an extra comparison
+ // on each node on the way down, and we will need to go all the way to the
+ // leaf node in the expected case.
+ if (iter.node->leaf()) {
+ break;
+ }
+ iter.node = iter.node->child(iter.position);
+ }
+ return {iter};
+}
+
+template <typename P>
+template <typename K>
+inline auto btree<P>::internal_locate_impl(
+ const K &key, std::true_type /* IsCompareTo */) const
+ -> SearchResult<iterator, true> {
+ iterator iter(const_cast<node_type *>(root()), 0);
+ for (;;) {
+ SearchResult<int, true> res = iter.node->lower_bound(key, key_comp());
+ iter.position = res.value;
+ if (res.match == MatchKind::kEq) {
+ return {iter, MatchKind::kEq};
+ }
+ if (iter.node->leaf()) {
+ break;
+ }
+ iter.node = iter.node->child(iter.position);
+ }
+ return {iter, MatchKind::kNe};
+}
+
+template <typename P>
+template <typename K>
+auto btree<P>::internal_lower_bound(const K &key) const -> iterator {
+ iterator iter(const_cast<node_type *>(root()), 0);
+ for (;;) {
+ iter.position = iter.node->lower_bound(key, key_comp()).value;
+ if (iter.node->leaf()) {
+ break;
+ }
+ iter.node = iter.node->child(iter.position);
+ }
+ return internal_last(iter);
+}
+
+template <typename P>
+template <typename K>
+auto btree<P>::internal_upper_bound(const K &key) const -> iterator {
+ iterator iter(const_cast<node_type *>(root()), 0);
+ for (;;) {
+ iter.position = iter.node->upper_bound(key, key_comp());
+ if (iter.node->leaf()) {
+ break;
+ }
+ iter.node = iter.node->child(iter.position);
+ }
+ return internal_last(iter);
+}
+
+template <typename P>
+template <typename K>
+auto btree<P>::internal_find(const K &key) const -> iterator {
+ auto res = internal_locate(key);
+ if constexpr (res.has_match) {
+ if (res.IsEq()) {
+ return res.value;
+ }
+ } else {
+ const iterator iter = internal_last(res.value);
+ if (iter.node != nullptr && !compare_keys(key, iter.key())) {
+ return iter;
+ }
+ }
+ return {nullptr, 0};
+}
+
+template <typename P>
+void btree<P>::internal_clear(node_type *node) {
+ if (!node->leaf()) {
+ for (int i = 0; i <= node->count(); ++i) {
+ internal_clear(node->child(i));
+ }
+ delete_internal_node(node);
+ } else {
+ delete_leaf_node(node);
+ }
+}
+
+template <typename P>
+int btree<P>::internal_verify(
+ const node_type *node, const key_type *lo, const key_type *hi) const {
+ assert(node->count() > 0);
+ assert(node->count() <= node->max_count());
+ if (lo) {
+ assert(!compare_keys(node->key(0), *lo));
+ }
+ if (hi) {
+ assert(!compare_keys(*hi, node->key(node->count() - 1)));
+ }
+ for (int i = 1; i < node->count(); ++i) {
+ assert(!compare_keys(node->key(i), node->key(i - 1)));
+ }
+ int count = node->count();
+ if (!node->leaf()) {
+ for (int i = 0; i <= node->count(); ++i) {
+ assert(node->child(i) != nullptr);
+ assert(node->child(i)->parent() == node);
+ assert(node->child(i)->position() == i);
+ count += internal_verify(
+ node->child(i),
+ (i == 0) ? lo : &node->key(i - 1),
+ (i == node->count()) ? hi : &node->key(i));
+ }
+ }
+ return count;
+}
+
+} // namespace btree::internal
diff --git a/src/include/cpp-btree/btree_container.h b/src/include/cpp-btree/btree_container.h
new file mode 100644
index 000000000..e8d9efd38
--- /dev/null
+++ b/src/include/cpp-btree/btree_container.h
@@ -0,0 +1,526 @@
+// Copyright 2018 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include <algorithm>
+#include <initializer_list>
+#include <iterator>
+#include <type_traits>
+#include <utility>
+
+#include "btree.h"
+
+namespace btree::internal {
+
+// A common base class for btree_set, btree_map, btree_multiset, and
+// btree_multimap.
+template <typename Tree>
+class btree_container {
+ using params_type = typename Tree::params_type;
+
+ protected:
+ // Alias used for heterogeneous lookup functions.
+ // `key_arg<K>` evaluates to `K` when the functors are transparent and to
+ // `key_type` otherwise. It permits template argument deduction on `K` for the
+ // transparent case.
+ template <class Compare>
+ using is_transparent_t = typename Compare::is_transparent;
+ template <class K>
+ using key_arg =
+ std::conditional_t<
+ std::experimental::is_detected_v<is_transparent_t, typename Tree::key_compare>,
+ K,
+ typename Tree::key_type>;
+
+ public:
+ using key_type = typename Tree::key_type;
+ using value_type = typename Tree::value_type;
+ using size_type = typename Tree::size_type;
+ using difference_type = typename Tree::difference_type;
+ using key_compare = typename Tree::key_compare;
+ using value_compare = typename Tree::value_compare;
+ using allocator_type = typename Tree::allocator_type;
+ using reference = typename Tree::reference;
+ using const_reference = typename Tree::const_reference;
+ using pointer = typename Tree::pointer;
+ using const_pointer = typename Tree::const_pointer;
+ using iterator = typename Tree::iterator;
+ using const_iterator = typename Tree::const_iterator;
+ using reverse_iterator = typename Tree::reverse_iterator;
+ using const_reverse_iterator = typename Tree::const_reverse_iterator;
+
+ // Constructors/assignments.
+ btree_container() : tree_(key_compare(), allocator_type()) {}
+ explicit btree_container(const key_compare &comp,
+ const allocator_type &alloc = allocator_type())
+ : tree_(comp, alloc) {}
+ btree_container(const btree_container &x) = default;
+ btree_container(btree_container &&x) noexcept = default;
+ btree_container &operator=(const btree_container &x) = default;
+ btree_container &operator=(btree_container &&x) noexcept(
+ std::is_nothrow_move_assignable<Tree>::value) = default;
+
+ // Iterator routines.
+ iterator begin() { return tree_.begin(); }
+ const_iterator begin() const { return tree_.begin(); }
+ const_iterator cbegin() const { return tree_.begin(); }
+ iterator end() { return tree_.end(); }
+ const_iterator end() const { return tree_.end(); }
+ const_iterator cend() const { return tree_.end(); }
+ reverse_iterator rbegin() { return tree_.rbegin(); }
+ const_reverse_iterator rbegin() const { return tree_.rbegin(); }
+ const_reverse_iterator crbegin() const { return tree_.rbegin(); }
+ reverse_iterator rend() { return tree_.rend(); }
+ const_reverse_iterator rend() const { return tree_.rend(); }
+ const_reverse_iterator crend() const { return tree_.rend(); }
+
+ // Lookup routines.
+ template <typename K = key_type>
+ iterator find(const key_arg<K> &key) {
+ return tree_.find(key);
+ }
+ template <typename K = key_type>
+ const_iterator find(const key_arg<K> &key) const {
+ return tree_.find(key);
+ }
+ template <typename K = key_type>
+ bool contains(const key_arg<K> &key) const {
+ return find(key) != end();
+ }
+ template <typename K = key_type>
+ iterator lower_bound(const key_arg<K> &key) {
+ return tree_.lower_bound(key);
+ }
+ template <typename K = key_type>
+ const_iterator lower_bound(const key_arg<K> &key) const {
+ return tree_.lower_bound(key);
+ }
+ template <typename K = key_type>
+ iterator upper_bound(const key_arg<K> &key) {
+ return tree_.upper_bound(key);
+ }
+ template <typename K = key_type>
+ const_iterator upper_bound(const key_arg<K> &key) const {
+ return tree_.upper_bound(key);
+ }
+ template <typename K = key_type>
+ std::pair<iterator, iterator> equal_range(const key_arg<K> &key) {
+ return tree_.equal_range(key);
+ }
+ template <typename K = key_type>
+ std::pair<const_iterator, const_iterator> equal_range(
+ const key_arg<K> &key) const {
+ return tree_.equal_range(key);
+ }
+
+ // Deletion routines. Note that there is also a deletion routine that is
+ // specific to btree_set_container/btree_multiset_container.
+
+ // Erase the specified iterator from the btree. The iterator must be valid
+ // (i.e. not equal to end()). Return an iterator pointing to the node after
+ // the one that was erased (or end() if none exists).
+ iterator erase(const_iterator iter) { return tree_.erase(iterator(iter)); }
+ iterator erase(iterator iter) { return tree_.erase(iter); }
+ iterator erase(const_iterator first, const_iterator last) {
+ return tree_.erase(iterator(first), iterator(last)).second;
+ }
+
+ public:
+ // Utility routines.
+ void clear() { tree_.clear(); }
+ void swap(btree_container &x) { tree_.swap(x.tree_); }
+ void verify() const { tree_.verify(); }
+
+ // Size routines.
+ size_type size() const { return tree_.size(); }
+ size_type max_size() const { return tree_.max_size(); }
+ bool empty() const { return tree_.empty(); }
+
+ friend bool operator==(const btree_container &x, const btree_container &y) {
+ if (x.size() != y.size()) return false;
+ return std::equal(x.begin(), x.end(), y.begin());
+ }
+
+ friend bool operator!=(const btree_container &x, const btree_container &y) {
+ return !(x == y);
+ }
+
+ friend bool operator<(const btree_container &x, const btree_container &y) {
+ return std::lexicographical_compare(x.begin(), x.end(), y.begin(), y.end());
+ }
+
+ friend bool operator>(const btree_container &x, const btree_container &y) {
+ return y < x;
+ }
+
+ friend bool operator<=(const btree_container &x, const btree_container &y) {
+ return !(y < x);
+ }
+
+ friend bool operator>=(const btree_container &x, const btree_container &y) {
+ return !(x < y);
+ }
+
+ // The allocator used by the btree.
+ allocator_type get_allocator() const { return tree_.get_allocator(); }
+
+ // The key comparator used by the btree.
+ key_compare key_comp() const { return tree_.key_comp(); }
+ value_compare value_comp() const { return tree_.value_comp(); }
+
+ protected:
+ Tree tree_;
+};
+
+// A common base class for btree_set and btree_map.
+template <typename Tree>
+class btree_set_container : public btree_container<Tree> {
+ using super_type = btree_container<Tree>;
+ using params_type = typename Tree::params_type;
+ using init_type = typename params_type::init_type;
+ using is_key_compare_to = typename params_type::is_key_compare_to;
+ friend class BtreeNodePeer;
+
+ protected:
+ template <class K>
+ using key_arg = typename super_type::template key_arg<K>;
+
+ public:
+ using key_type = typename Tree::key_type;
+ using value_type = typename Tree::value_type;
+ using size_type = typename Tree::size_type;
+ using key_compare = typename Tree::key_compare;
+ using allocator_type = typename Tree::allocator_type;
+ using iterator = typename Tree::iterator;
+ using const_iterator = typename Tree::const_iterator;
+
+ // Inherit constructors.
+ using super_type::super_type;
+ btree_set_container() {}
+
+ // Range constructor.
+ template <class InputIterator>
+ btree_set_container(InputIterator b, InputIterator e,
+ const key_compare &comp = key_compare(),
+ const allocator_type &alloc = allocator_type())
+ : super_type(comp, alloc) {
+ insert(b, e);
+ }
+
+ // Initializer list constructor.
+ btree_set_container(std::initializer_list<init_type> init,
+ const key_compare &comp = key_compare(),
+ const allocator_type &alloc = allocator_type())
+ : btree_set_container(init.begin(), init.end(), comp, alloc) {}
+
+ // Lookup routines.
+ template <typename K = key_type>
+ size_type count(const key_arg<K> &key) const {
+ return this->tree_.count_unique(key);
+ }
+
+ // Insertion routines.
+ std::pair<iterator, bool> insert(const value_type &x) {
+ return this->tree_.insert_unique(params_type::key(x), x);
+ }
+ std::pair<iterator, bool> insert(value_type &&x) {
+ return this->tree_.insert_unique(params_type::key(x), std::move(x));
+ }
+ template <typename... Args>
+ std::pair<iterator, bool> emplace(Args &&... args) {
+ init_type v(std::forward<Args>(args)...);
+ return this->tree_.insert_unique(params_type::key(v), std::move(v));
+ }
+ iterator insert(const_iterator position, const value_type &x) {
+ return this->tree_
+ .insert_hint_unique(iterator(position), params_type::key(x), x)
+ .first;
+ }
+ iterator insert(const_iterator position, value_type &&x) {
+ return this->tree_
+ .insert_hint_unique(iterator(position), params_type::key(x),
+ std::move(x))
+ .first;
+ }
+ template <typename... Args>
+ iterator emplace_hint(const_iterator position, Args &&... args) {
+ init_type v(std::forward<Args>(args)...);
+ return this->tree_
+ .insert_hint_unique(iterator(position), params_type::key(v),
+ std::move(v))
+ .first;
+ }
+ template <typename InputIterator>
+ void insert(InputIterator b, InputIterator e) {
+ this->tree_.insert_iterator_unique(b, e);
+ }
+ void insert(std::initializer_list<init_type> init) {
+ this->tree_.insert_iterator_unique(init.begin(), init.end());
+ }
+ // Deletion routines.
+ template <typename K = key_type>
+ size_type erase(const key_arg<K> &key) {
+ return this->tree_.erase_unique(key);
+ }
+ using super_type::erase;
+
+ // Merge routines.
+ // Moves elements from `src` into `this`. If the element already exists in
+ // `this`, it is left unmodified in `src`.
+ template <
+ typename T,
+ typename std::enable_if_t<
+ std::conjunction_v<
+ std::is_same<value_type, typename T::value_type>,
+ std::is_same<allocator_type, typename T::allocator_type>,
+ std::is_same<typename params_type::is_map_container,
+ typename T::params_type::is_map_container>>,
+ int> = 0>
+ void merge(btree_container<T> &src) { // NOLINT
+ for (auto src_it = src.begin(); src_it != src.end();) {
+ if (insert(std::move(*src_it)).second) {
+ src_it = src.erase(src_it);
+ } else {
+ ++src_it;
+ }
+ }
+ }
+
+ template <
+ typename T,
+ typename std::enable_if_t<
+ std::conjunction_v<
+ std::is_same<value_type, typename T::value_type>,
+ std::is_same<allocator_type, typename T::allocator_type>,
+ std::is_same<typename params_type::is_map_container,
+ typename T::params_type::is_map_container>>,
+ int> = 0>
+ void merge(btree_container<T> &&src) {
+ merge(src);
+ }
+};
+
+// A common base class for btree_map and safe_btree_map.
+// Base class for btree_map.
+template <typename Tree>
+class btree_map_container : public btree_set_container<Tree> {
+ using super_type = btree_set_container<Tree>;
+ using params_type = typename Tree::params_type;
+
+ protected:
+ template <class K>
+ using key_arg = typename super_type::template key_arg<K>;
+
+ public:
+ using key_type = typename Tree::key_type;
+ using mapped_type = typename params_type::mapped_type;
+ using value_type = typename Tree::value_type;
+ using key_compare = typename Tree::key_compare;
+ using allocator_type = typename Tree::allocator_type;
+ using iterator = typename Tree::iterator;
+ using const_iterator = typename Tree::const_iterator;
+
+ // Inherit constructors.
+ using super_type::super_type;
+ btree_map_container() {}
+
+ // Insertion routines.
+ template <typename... Args>
+ std::pair<iterator, bool> try_emplace(const key_type &k, Args &&... args) {
+ return this->tree_.insert_unique(
+ k, std::piecewise_construct, std::forward_as_tuple(k),
+ std::forward_as_tuple(std::forward<Args>(args)...));
+ }
+ template <typename... Args>
+ std::pair<iterator, bool> try_emplace(key_type &&k, Args &&... args) {
+ // Note: `key_ref` exists to avoid a ClangTidy warning about moving from `k`
+ // and then using `k` unsequenced. This is safe because the move is into a
+ // forwarding reference and insert_unique guarantees that `key` is never
+ // referenced after consuming `args`.
+ const key_type& key_ref = k;
+ return this->tree_.insert_unique(
+ key_ref, std::piecewise_construct, std::forward_as_tuple(std::move(k)),
+ std::forward_as_tuple(std::forward<Args>(args)...));
+ }
+ template <typename... Args>
+ iterator try_emplace(const_iterator hint, const key_type &k,
+ Args &&... args) {
+ return this->tree_
+ .insert_hint_unique(iterator(hint), k, std::piecewise_construct,
+ std::forward_as_tuple(k),
+ std::forward_as_tuple(std::forward<Args>(args)...))
+ .first;
+ }
+ template <typename... Args>
+ iterator try_emplace(const_iterator hint, key_type &&k, Args &&... args) {
+ // Note: `key_ref` exists to avoid a ClangTidy warning about moving from `k`
+ // and then using `k` unsequenced. This is safe because the move is into a
+ // forwarding reference and insert_hint_unique guarantees that `key` is
+ // never referenced after consuming `args`.
+ const key_type& key_ref = k;
+ return this->tree_
+ .insert_hint_unique(iterator(hint), key_ref, std::piecewise_construct,
+ std::forward_as_tuple(std::move(k)),
+ std::forward_as_tuple(std::forward<Args>(args)...))
+ .first;
+ }
+ mapped_type &operator[](const key_type &k) {
+ return try_emplace(k).first->second;
+ }
+ mapped_type &operator[](key_type &&k) {
+ return try_emplace(std::move(k)).first->second;
+ }
+
+ template <typename K = key_type>
+ mapped_type &at(const key_arg<K> &key) {
+ auto it = this->find(key);
+ if (it == this->end())
+ throw std::out_of_range("btree_map::at");
+ return it->second;
+ }
+ template <typename K = key_type>
+ const mapped_type &at(const key_arg<K> &key) const {
+ auto it = this->find(key);
+ if (it == this->end())
+ throw std::out_of_range("btree_map::at");
+ return it->second;
+ }
+};
+
+// A common base class for btree_multiset and btree_multimap.
+template <typename Tree>
+class btree_multiset_container : public btree_container<Tree> {
+ using super_type = btree_container<Tree>;
+ using params_type = typename Tree::params_type;
+ using init_type = typename params_type::init_type;
+ using is_key_compare_to = typename params_type::is_key_compare_to;
+
+ template <class K>
+ using key_arg = typename super_type::template key_arg<K>;
+
+ public:
+ using key_type = typename Tree::key_type;
+ using value_type = typename Tree::value_type;
+ using size_type = typename Tree::size_type;
+ using key_compare = typename Tree::key_compare;
+ using allocator_type = typename Tree::allocator_type;
+ using iterator = typename Tree::iterator;
+ using const_iterator = typename Tree::const_iterator;
+
+ // Inherit constructors.
+ using super_type::super_type;
+ btree_multiset_container() {}
+
+ // Range constructor.
+ template <class InputIterator>
+ btree_multiset_container(InputIterator b, InputIterator e,
+ const key_compare &comp = key_compare(),
+ const allocator_type &alloc = allocator_type())
+ : super_type(comp, alloc) {
+ insert(b, e);
+ }
+
+ // Initializer list constructor.
+ btree_multiset_container(std::initializer_list<init_type> init,
+ const key_compare &comp = key_compare(),
+ const allocator_type &alloc = allocator_type())
+ : btree_multiset_container(init.begin(), init.end(), comp, alloc) {}
+
+ // Lookup routines.
+ template <typename K = key_type>
+ size_type count(const key_arg<K> &key) const {
+ return this->tree_.count_multi(key);
+ }
+
+ // Insertion routines.
+ iterator insert(const value_type &x) { return this->tree_.insert_multi(x); }
+ iterator insert(value_type &&x) {
+ return this->tree_.insert_multi(std::move(x));
+ }
+ iterator insert(const_iterator position, const value_type &x) {
+ return this->tree_.insert_hint_multi(iterator(position), x);
+ }
+ iterator insert(const_iterator position, value_type &&x) {
+ return this->tree_.insert_hint_multi(iterator(position), std::move(x));
+ }
+ template <typename InputIterator>
+ void insert(InputIterator b, InputIterator e) {
+ this->tree_.insert_iterator_multi(b, e);
+ }
+ void insert(std::initializer_list<init_type> init) {
+ this->tree_.insert_iterator_multi(init.begin(), init.end());
+ }
+ template <typename... Args>
+ iterator emplace(Args &&... args) {
+ return this->tree_.insert_multi(init_type(std::forward<Args>(args)...));
+ }
+ template <typename... Args>
+ iterator emplace_hint(const_iterator position, Args &&... args) {
+ return this->tree_.insert_hint_multi(
+ iterator(position), init_type(std::forward<Args>(args)...));
+ }
+
+ // Deletion routines.
+ template <typename K = key_type>
+ size_type erase(const key_arg<K> &key) {
+ return this->tree_.erase_multi(key);
+ }
+ using super_type::erase;
+
+ // Merge routines.
+ // Moves all elements from `src` into `this`.
+ template <
+ typename T,
+ typename std::enable_if_t<
+ std::conjunction_v<
+ std::is_same<value_type, typename T::value_type>,
+ std::is_same<allocator_type, typename T::allocator_type>,
+ std::is_same<typename params_type::is_map_container,
+ typename T::params_type::is_map_container>>,
+ int> = 0>
+ void merge(btree_container<T> &src) { // NOLINT
+ insert(std::make_move_iterator(src.begin()),
+ std::make_move_iterator(src.end()));
+ src.clear();
+ }
+
+ template <
+ typename T,
+ typename std::enable_if_t<
+ std::conjunction_v<
+ std::is_same<value_type, typename T::value_type>,
+ std::is_same<allocator_type, typename T::allocator_type>,
+ std::is_same<typename params_type::is_map_container,
+ typename T::params_type::is_map_container>>,
+ int> = 0>
+ void merge(btree_container<T> &&src) {
+ merge(src);
+ }
+};
+
+// A base class for btree_multimap.
+template <typename Tree>
+class btree_multimap_container : public btree_multiset_container<Tree> {
+ using super_type = btree_multiset_container<Tree>;
+ using params_type = typename Tree::params_type;
+
+ public:
+ using mapped_type = typename params_type::mapped_type;
+
+ // Inherit constructors.
+ using super_type::super_type;
+ btree_multimap_container() {}
+};
+} // namespace btree::internal
diff --git a/src/include/cpp-btree/btree_map.h b/src/include/cpp-btree/btree_map.h
new file mode 100644
index 000000000..749c2bbcd
--- /dev/null
+++ b/src/include/cpp-btree/btree_map.h
@@ -0,0 +1,159 @@
+// Copyright 2018 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// -----------------------------------------------------------------------------
+// File: btree_map.h
+// -----------------------------------------------------------------------------
+//
+// This header file defines B-tree maps: sorted associative containers mapping
+// keys to values.
+//
+// * `btree::btree_map<>`
+// * `btree::btree_multimap<>`
+//
+// These B-tree types are similar to the corresponding types in the STL
+// (`std::map` and `std::multimap`) and generally conform to the STL interfaces
+// of those types. However, because they are implemented using B-trees, they
+// are more efficient in most situations.
+//
+// Unlike `std::map` and `std::multimap`, which are commonly implemented using
+// red-black tree nodes, B-tree maps use more generic B-tree nodes able to hold
+// multiple values per node. Holding multiple values per node often makes
+// B-tree maps perform better than their `std::map` counterparts, because
+// multiple entries can be checked within the same cache hit.
+//
+// However, these types should not be considered drop-in replacements for
+// `std::map` and `std::multimap` as there are some API differences, which are
+// noted in this header file.
+//
+// Importantly, insertions and deletions may invalidate outstanding iterators,
+// pointers, and references to elements. Such invalidations are typically only
+// an issue if insertion and deletion operations are interleaved with the use of
+// more than one iterator, pointer, or reference simultaneously. For this
+// reason, `insert()` and `erase()` return a valid iterator at the current
+// position.
+
+#pragma once
+
+#include "btree.h"
+#include "btree_container.h"
+
+namespace btree {
+
+// btree::btree_map<>
+//
+// A `btree::btree_map<K, V>` is an ordered associative container of
+// unique keys and associated values designed to be a more efficient replacement
+// for `std::map` (in most cases).
+//
+// Keys are sorted using an (optional) comparison function, which defaults to
+// `std::less<K>`.
+//
+// A `btree::btree_map<K, V>` uses a default allocator of
+// `std::allocator<std::pair<const K, V>>` to allocate (and deallocate)
+// nodes, and construct and destruct values within those nodes. You may
+// instead specify a custom allocator `A` (which in turn requires specifying a
+// custom comparator `C`) as in `btree::btree_map<K, V, C, A>`.
+//
+template <typename Key, typename Value, typename Compare = std::less<Key>,
+ typename Alloc = std::allocator<std::pair<const Key, Value>>>
+class btree_map
+ : public internal::btree_map_container<
+ internal::btree<internal::map_params<
+ Key, Value, Compare, Alloc, /*TargetNodeSize=*/256,
+ /*Multi=*/false>>> {
+
+ using Base = typename btree_map::btree_map_container;
+
+ public:
+ // Default constructor.
+ btree_map() = default;
+ using Base::Base;
+};
+
+// btree::swap(btree::btree_map<>, btree::btree_map<>)
+//
+// Swaps the contents of two `btree::btree_map` containers.
+template <typename K, typename V, typename C, typename A>
+void swap(btree_map<K, V, C, A> &x, btree_map<K, V, C, A> &y) {
+ return x.swap(y);
+}
+
+// btree::erase_if(btree::btree_map<>, Pred)
+//
+// Erases all elements that satisfy the predicate pred from the container.
+template <typename K, typename V, typename C, typename A, typename Pred>
+void erase_if(btree_map<K, V, C, A> &map, Pred pred) {
+ for (auto it = map.begin(); it != map.end();) {
+ if (pred(*it)) {
+ it = map.erase(it);
+ } else {
+ ++it;
+ }
+ }
+}
+
+// btree::btree_multimap
+//
+// A `btree::btree_multimap<K, V>` is an ordered associative container of
+// keys and associated values designed to be a more efficient replacement for
+// `std::multimap` (in most cases). Unlike `btree::btree_map`, a B-tree multimap
+// allows multiple elements with equivalent keys.
+//
+// Keys are sorted using an (optional) comparison function, which defaults to
+// `std::less<K>`.
+//
+// A `btree::btree_multimap<K, V>` uses a default allocator of
+// `std::allocator<std::pair<const K, V>>` to allocate (and deallocate)
+// nodes, and construct and destruct values within those nodes. You may
+// instead specify a custom allocator `A` (which in turn requires specifying a
+// custom comparator `C`) as in `btree::btree_multimap<K, V, C, A>`.
+//
+template <typename Key, typename Value, typename Compare = std::less<Key>,
+ typename Alloc = std::allocator<std::pair<const Key, Value>>>
+class btree_multimap
+ : public internal::btree_multimap_container<
+ internal::btree<internal::map_params<
+ Key, Value, Compare, Alloc, /*TargetNodeSize=*/256,
+ /*Multi=*/true>>> {
+ using Base = typename btree_multimap::btree_multimap_container;
+
+ public:
+ btree_multimap() = default;
+ using Base::Base;
+};
+
+// btree::swap(btree::btree_multimap<>, btree::btree_multimap<>)
+//
+// Swaps the contents of two `btree::btree_multimap` containers.
+template <typename K, typename V, typename C, typename A>
+void swap(btree_multimap<K, V, C, A> &x, btree_multimap<K, V, C, A> &y) {
+ return x.swap(y);
+}
+
+// btree::erase_if(btree::btree_multimap<>, Pred)
+//
+// Erases all elements that satisfy the predicate pred from the container.
+template <typename K, typename V, typename C, typename A, typename Pred>
+void erase_if(btree_multimap<K, V, C, A> &map, Pred pred) {
+ for (auto it = map.begin(); it != map.end();) {
+ if (pred(*it)) {
+ it = map.erase(it);
+ } else {
+ ++it;
+ }
+ }
+}
+
+} // namespace btree
diff --git a/src/include/cpp-btree/btree_set.h b/src/include/cpp-btree/btree_set.h
new file mode 100644
index 000000000..57536ce2f
--- /dev/null
+++ b/src/include/cpp-btree/btree_set.h
@@ -0,0 +1,632 @@
+// Copyright 2018 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// -----------------------------------------------------------------------------
+// File: btree_set.h
+// -----------------------------------------------------------------------------
+//
+// This header file defines B-tree sets: sorted associative containers of
+// values.
+//
+// * `absl::btree_set<>`
+// * `absl::btree_multiset<>`
+//
+// These B-tree types are similar to the corresponding types in the STL
+// (`std::set` and `std::multiset`) and generally conform to the STL interfaces
+// of those types. However, because they are implemented using B-trees, they
+// are more efficient in most situations.
+//
+// Unlike `std::set` and `std::multiset`, which are commonly implemented using
+// red-black tree nodes, B-tree sets use more generic B-tree nodes able to hold
+// multiple values per node. Holding multiple values per node often makes
+// B-tree sets perform better than their `std::set` counterparts, because
+// multiple entries can be checked within the same cache hit.
+//
+// However, these types should not be considered drop-in replacements for
+// `std::set` and `std::multiset` as there are some API differences, which are
+// noted in this header file.
+//
+// Importantly, insertions and deletions may invalidate outstanding iterators,
+// pointers, and references to elements. Such invalidations are typically only
+// an issue if insertion and deletion operations are interleaved with the use of
+// more than one iterator, pointer, or reference simultaneously. For this
+// reason, `insert()` and `erase()` return a valid iterator at the current
+// position.
+
+#pragma once
+
+#include "btree.h"
+#include "btree_container.h"
+
+namespace btree {
+
+// btree::btree_set<>
+//
+// An `btree::btree_set<K>` is an ordered associative container of unique key
+// values designed to be a more efficient replacement for `std::set` (in most
+// cases).
+//
+// Keys are sorted using an (optional) comparison function, which defaults to
+// `std::less<K>`.
+//
+// An `btree::btree_set<K>` uses a default allocator of `std::allocator<K>` to
+// allocate (and deallocate) nodes, and construct and destruct values within
+// those nodes. You may instead specify a custom allocator `A` (which in turn
+// requires specifying a custom comparator `C`) as in
+// `btree::btree_set<K, C, A>`.
+//
+template <typename Key, typename Compare = std::less<Key>,
+ typename Alloc = std::allocator<Key>>
+class btree_set
+ : public internal::btree_set_container<
+ internal::btree<internal::set_params<
+ Key, Compare, Alloc, /*TargetNodeSize=*/256,
+ /*Multi=*/false>>> {
+ using Base = typename btree_set::btree_set_container;
+
+ public:
+ // Constructors and Assignment Operators
+ //
+ // A `btree_set` supports the same overload set as `std::set`
+ // for construction and assignment:
+ //
+ // * Default constructor
+ //
+ // btree::btree_set<std::string> set1;
+ //
+ // * Initializer List constructor
+ //
+ // btree::btree_set<std::string> set2 =
+ // {{"huey"}, {"dewey"}, {"louie"},};
+ //
+ // * Copy constructor
+ //
+ // btree::btree_set<std::string> set3(set2);
+ //
+ // * Copy assignment operator
+ //
+ // btree::btree_set<std::string> set4;
+ // set4 = set3;
+ //
+ // * Move constructor
+ //
+ // // Move is guaranteed efficient
+ // btree::btree_set<std::string> set5(std::move(set4));
+ //
+ // * Move assignment operator
+ //
+ // // May be efficient if allocators are compatible
+ // btree::btree_set<std::string> set6;
+ // set6 = std::move(set5);
+ //
+ // * Range constructor
+ //
+ // std::vector<std::string> v = {"a", "b"};
+ // btree::btree_set<std::string> set7(v.begin(), v.end());
+ btree_set() {}
+ using Base::Base;
+
+ // btree_set::begin()
+ //
+ // Returns an iterator to the beginning of the `btree_set`.
+ using Base::begin;
+
+ // btree_set::cbegin()
+ //
+ // Returns a const iterator to the beginning of the `btree_set`.
+ using Base::cbegin;
+
+ // btree_set::end()
+ //
+ // Returns an iterator to the end of the `btree_set`.
+ using Base::end;
+
+ // btree_set::cend()
+ //
+ // Returns a const iterator to the end of the `btree_set`.
+ using Base::cend;
+
+ // btree_set::empty()
+ //
+ // Returns whether or not the `btree_set` is empty.
+ using Base::empty;
+
+ // btree_set::max_size()
+ //
+ // Returns the largest theoretical possible number of elements within a
+ // `btree_set` under current memory constraints. This value can be thought
+ // of as the largest value of `std::distance(begin(), end())` for a
+ // `btree_set<Key>`.
+ using Base::max_size;
+
+ // btree_set::size()
+ //
+ // Returns the number of elements currently within the `btree_set`.
+ using Base::size;
+
+ // btree_set::clear()
+ //
+ // Removes all elements from the `btree_set`. Invalidates any references,
+ // pointers, or iterators referring to contained elements.
+ using Base::clear;
+
+ // btree_set::erase()
+ //
+ // Erases elements within the `btree_set`. Overloads are listed below.
+ //
+ // iterator erase(iterator position):
+ // iterator erase(const_iterator position):
+ //
+ // Erases the element at `position` of the `btree_set`, returning
+ // the iterator pointing to the element after the one that was erased
+ // (or end() if none exists).
+ //
+ // iterator erase(const_iterator first, const_iterator last):
+ //
+ // Erases the elements in the open interval [`first`, `last`), returning
+ // the iterator pointing to the element after the interval that was erased
+ // (or end() if none exists).
+ //
+ // template <typename K> size_type erase(const K& key):
+ //
+ // Erases the element with the matching key, if it exists, returning the
+ // number of elements erased.
+ using Base::erase;
+
+ // btree_set::insert()
+ //
+ // Inserts an element of the specified value into the `btree_set`,
+ // returning an iterator pointing to the newly inserted element, provided that
+ // an element with the given key does not already exist. If an insertion
+ // occurs, any references, pointers, or iterators are invalidated.
+ // Overloads are listed below.
+ //
+ // std::pair<iterator,bool> insert(const value_type& value):
+ //
+ // Inserts a value into the `btree_set`. Returns a pair consisting of an
+ // iterator to the inserted element (or to the element that prevented the
+ // insertion) and a bool denoting whether the insertion took place.
+ //
+ // std::pair<iterator,bool> insert(value_type&& value):
+ //
+ // Inserts a moveable value into the `btree_set`. Returns a pair
+ // consisting of an iterator to the inserted element (or to the element that
+ // prevented the insertion) and a bool denoting whether the insertion took
+ // place.
+ //
+ // iterator insert(const_iterator hint, const value_type& value):
+ // iterator insert(const_iterator hint, value_type&& value):
+ //
+ // Inserts a value, using the position of `hint` as a non-binding suggestion
+ // for where to begin the insertion search. Returns an iterator to the
+ // inserted element, or to the existing element that prevented the
+ // insertion.
+ //
+ // void insert(InputIterator first, InputIterator last):
+ //
+ // Inserts a range of values [`first`, `last`).
+ //
+ // void insert(std::initializer_list<init_type> ilist):
+ //
+ // Inserts the elements within the initializer list `ilist`.
+ using Base::insert;
+
+ // btree_set::emplace()
+ //
+ // Inserts an element of the specified value by constructing it in-place
+ // within the `btree_set`, provided that no element with the given key
+ // already exists.
+ //
+ // The element may be constructed even if there already is an element with the
+ // key in the container, in which case the newly constructed element will be
+ // destroyed immediately.
+ //
+ // If an insertion occurs, any references, pointers, or iterators are
+ // invalidated.
+ using Base::emplace;
+
+ // btree_set::emplace_hint()
+ //
+ // Inserts an element of the specified value by constructing it in-place
+ // within the `btree_set`, using the position of `hint` as a non-binding
+ // suggestion for where to begin the insertion search, and only inserts
+ // provided that no element with the given key already exists.
+ //
+ // The element may be constructed even if there already is an element with the
+ // key in the container, in which case the newly constructed element will be
+ // destroyed immediately.
+ //
+ // If an insertion occurs, any references, pointers, or iterators are
+ // invalidated.
+ using Base::emplace_hint;
+
+ // btree_set::merge()
+ //
+ // Extracts elements from a given `source` btree_set into this
+ // `btree_set`. If the destination `btree_set` already contains an
+ // element with an equivalent key, that element is not extracted.
+ using Base::merge;
+
+ // btree_set::swap(btree_set& other)
+ //
+ // Exchanges the contents of this `btree_set` with those of the `other`
+ // btree_set, avoiding invocation of any move, copy, or swap operations on
+ // individual elements.
+ //
+ // All iterators and references on the `btree_set` remain valid, excepting
+ // for the past-the-end iterator, which is invalidated.
+ using Base::swap;
+
+ // btree_set::contains()
+ //
+ // template <typename K> bool contains(const K& key) const:
+ //
+ // Determines whether an element comparing equal to the given `key` exists
+ // within the `btree_set`, returning `true` if so or `false` otherwise.
+ //
+ // Supports heterogeneous lookup, provided that the set is provided a
+ // compatible heterogeneous comparator.
+ using Base::contains;
+
+ // btree_set::count()
+ //
+ // template <typename K> size_type count(const K& key) const:
+ //
+ // Returns the number of elements comparing equal to the given `key` within
+ // the `btree_set`. Note that this function will return either `1` or `0`
+ // since duplicate elements are not allowed within a `btree_set`.
+ //
+ // Supports heterogeneous lookup, provided that the set is provided a
+ // compatible heterogeneous comparator.
+ using Base::count;
+
+ // btree_set::equal_range()
+ //
+ // Returns a closed range [first, last], defined by a `std::pair` of two
+ // iterators, containing all elements with the passed key in the
+ // `btree_set`.
+ using Base::equal_range;
+
+ // btree_set::find()
+ //
+ // template <typename K> iterator find(const K& key):
+ // template <typename K> const_iterator find(const K& key) const:
+ //
+ // Finds an element with the passed `key` within the `btree_set`.
+ //
+ // Supports heterogeneous lookup, provided that the set is provided a
+ // compatible heterogeneous comparator.
+ using Base::find;
+
+ // btree_set::get_allocator()
+ //
+ // Returns the allocator function associated with this `btree_set`.
+ using Base::get_allocator;
+
+ // btree_set::key_comp();
+ //
+ // Returns the key comparator associated with this `btree_set`.
+ using Base::key_comp;
+
+ // btree_set::value_comp();
+ //
+ // Returns the value comparator associated with this `btree_set`. The keys to
+ // sort the elements are the values themselves, therefore `value_comp` and its
+ // sibling member function `key_comp` are equivalent.
+ using Base::value_comp;
+};
+
+// btree::swap(btree::btree_set<>, btree::btree_set<>)
+//
+// Swaps the contents of two `btree::btree_set` containers.
+template <typename K, typename C, typename A>
+void swap(btree_set<K, C, A> &x, btree_set<K, C, A> &y) {
+ return x.swap(y);
+}
+
+// btree::erase_if(btree::btree_set<>, Pred)
+//
+// Erases all elements that satisfy the predicate pred from the container.
+template <typename K, typename C, typename A, typename Pred>
+void erase_if(btree_set<K, C, A> &set, Pred pred) {
+ for (auto it = set.begin(); it != set.end();) {
+ if (pred(*it)) {
+ it = set.erase(it);
+ } else {
+ ++it;
+ }
+ }
+}
+
+// btree::btree_multiset<>
+//
+// An `btree::btree_multiset<K>` is an ordered associative container of
+// keys and associated values designed to be a more efficient replacement
+// for `std::multiset` (in most cases). Unlike `btree::btree_set`, a B-tree
+// multiset allows equivalent elements.
+//
+// Keys are sorted using an (optional) comparison function, which defaults to
+// `std::less<K>`.
+//
+// An `btree::btree_multiset<K>` uses a default allocator of `std::allocator<K>`
+// to allocate (and deallocate) nodes, and construct and destruct values within
+// those nodes. You may instead specify a custom allocator `A` (which in turn
+// requires specifying a custom comparator `C`) as in
+// `btree::btree_multiset<K, C, A>`.
+//
+template <typename Key, typename Compare = std::less<Key>,
+ typename Alloc = std::allocator<Key>>
+class btree_multiset
+ : public internal::btree_multiset_container<
+ internal::btree<internal::set_params<
+ Key, Compare, Alloc, /*TargetNodeSize=*/256,
+ /*Multi=*/true>>> {
+ using Base = typename btree_multiset::btree_multiset_container;
+
+ public:
+ // Constructors and Assignment Operators
+ //
+ // A `btree_multiset` supports the same overload set as `std::set`
+ // for construction and assignment:
+ //
+ // * Default constructor
+ //
+ // btree::btree_multiset<std::string> set1;
+ //
+ // * Initializer List constructor
+ //
+ // btree::btree_multiset<std::string> set2 =
+ // {{"huey"}, {"dewey"}, {"louie"},};
+ //
+ // * Copy constructor
+ //
+ // btree::btree_multiset<std::string> set3(set2);
+ //
+ // * Copy assignment operator
+ //
+ // btree::btree_multiset<std::string> set4;
+ // set4 = set3;
+ //
+ // * Move constructor
+ //
+ // // Move is guaranteed efficient
+ // btree::btree_multiset<std::string> set5(std::move(set4));
+ //
+ // * Move assignment operator
+ //
+ // // May be efficient if allocators are compatible
+ // btree::btree_multiset<std::string> set6;
+ // set6 = std::move(set5);
+ //
+ // * Range constructor
+ //
+ // std::vector<std::string> v = {"a", "b"};
+ // btree::btree_multiset<std::string> set7(v.begin(), v.end());
+ btree_multiset() {}
+ using Base::Base;
+
+ // btree_multiset::begin()
+ //
+ // Returns an iterator to the beginning of the `btree_multiset`.
+ using Base::begin;
+
+ // btree_multiset::cbegin()
+ //
+ // Returns a const iterator to the beginning of the `btree_multiset`.
+ using Base::cbegin;
+
+ // btree_multiset::end()
+ //
+ // Returns an iterator to the end of the `btree_multiset`.
+ using Base::end;
+
+ // btree_multiset::cend()
+ //
+ // Returns a const iterator to the end of the `btree_multiset`.
+ using Base::cend;
+
+ // btree_multiset::empty()
+ //
+ // Returns whether or not the `btree_multiset` is empty.
+ using Base::empty;
+
+ // btree_multiset::max_size()
+ //
+ // Returns the largest theoretical possible number of elements within a
+ // `btree_multiset` under current memory constraints. This value can be
+ // thought of as the largest value of `std::distance(begin(), end())` for a
+ // `btree_multiset<Key>`.
+ using Base::max_size;
+
+ // btree_multiset::size()
+ //
+ // Returns the number of elements currently within the `btree_multiset`.
+ using Base::size;
+
+ // btree_multiset::clear()
+ //
+ // Removes all elements from the `btree_multiset`. Invalidates any references,
+ // pointers, or iterators referring to contained elements.
+ using Base::clear;
+
+ // btree_multiset::erase()
+ //
+ // Erases elements within the `btree_multiset`. Overloads are listed below.
+ //
+ // iterator erase(iterator position):
+ // iterator erase(const_iterator position):
+ //
+ // Erases the element at `position` of the `btree_multiset`, returning
+ // the iterator pointing to the element after the one that was erased
+ // (or end() if none exists).
+ //
+ // iterator erase(const_iterator first, const_iterator last):
+ //
+ // Erases the elements in the open interval [`first`, `last`), returning
+ // the iterator pointing to the element after the interval that was erased
+ // (or end() if none exists).
+ //
+ // template <typename K> size_type erase(const K& key):
+ //
+ // Erases the elements matching the key, if any exist, returning the
+ // number of elements erased.
+ using Base::erase;
+
+ // btree_multiset::insert()
+ //
+ // Inserts an element of the specified value into the `btree_multiset`,
+ // returning an iterator pointing to the newly inserted element.
+ // Any references, pointers, or iterators are invalidated. Overloads are
+ // listed below.
+ //
+ // iterator insert(const value_type& value):
+ //
+ // Inserts a value into the `btree_multiset`, returning an iterator to the
+ // inserted element.
+ //
+ // iterator insert(value_type&& value):
+ //
+ // Inserts a moveable value into the `btree_multiset`, returning an iterator
+ // to the inserted element.
+ //
+ // iterator insert(const_iterator hint, const value_type& value):
+ // iterator insert(const_iterator hint, value_type&& value):
+ //
+ // Inserts a value, using the position of `hint` as a non-binding suggestion
+ // for where to begin the insertion search. Returns an iterator to the
+ // inserted element.
+ //
+ // void insert(InputIterator first, InputIterator last):
+ //
+ // Inserts a range of values [`first`, `last`).
+ //
+ // void insert(std::initializer_list<init_type> ilist):
+ //
+ // Inserts the elements within the initializer list `ilist`.
+ using Base::insert;
+
+ // btree_multiset::emplace()
+ //
+ // Inserts an element of the specified value by constructing it in-place
+ // within the `btree_multiset`. Any references, pointers, or iterators are
+ // invalidated.
+ using Base::emplace;
+
+ // btree_multiset::emplace_hint()
+ //
+ // Inserts an element of the specified value by constructing it in-place
+ // within the `btree_multiset`, using the position of `hint` as a non-binding
+ // suggestion for where to begin the insertion search.
+ //
+ // Any references, pointers, or iterators are invalidated.
+ using Base::emplace_hint;
+
+ // btree_multiset::merge()
+ //
+ // Extracts elements from a given `source` btree_multiset into this
+ // `btree_multiset`. If the destination `btree_multiset` already contains an
+ // element with an equivalent key, that element is not extracted.
+ using Base::merge;
+
+ // btree_multiset::swap(btree_multiset& other)
+ //
+ // Exchanges the contents of this `btree_multiset` with those of the `other`
+ // btree_multiset, avoiding invocation of any move, copy, or swap operations
+ // on individual elements.
+ //
+ // All iterators and references on the `btree_multiset` remain valid,
+ // excepting for the past-the-end iterator, which is invalidated.
+ using Base::swap;
+
+ // btree_multiset::contains()
+ //
+ // template <typename K> bool contains(const K& key) const:
+ //
+ // Determines whether an element comparing equal to the given `key` exists
+ // within the `btree_multiset`, returning `true` if so or `false` otherwise.
+ //
+ // Supports heterogeneous lookup, provided that the set is provided a
+ // compatible heterogeneous comparator.
+ using Base::contains;
+
+ // btree_multiset::count()
+ //
+ // template <typename K> size_type count(const K& key) const:
+ //
+ // Returns the number of elements comparing equal to the given `key` within
+ // the `btree_multiset`.
+ //
+ // Supports heterogeneous lookup, provided that the set is provided a
+ // compatible heterogeneous comparator.
+ using Base::count;
+
+ // btree_multiset::equal_range()
+ //
+ // Returns a closed range [first, last], defined by a `std::pair` of two
+ // iterators, containing all elements with the passed key in the
+ // `btree_multiset`.
+ using Base::equal_range;
+
+ // btree_multiset::find()
+ //
+ // template <typename K> iterator find(const K& key):
+ // template <typename K> const_iterator find(const K& key) const:
+ //
+ // Finds an element with the passed `key` within the `btree_multiset`.
+ //
+ // Supports heterogeneous lookup, provided that the set is provided a
+ // compatible heterogeneous comparator.
+ using Base::find;
+
+ // btree_multiset::get_allocator()
+ //
+ // Returns the allocator function associated with this `btree_multiset`.
+ using Base::get_allocator;
+
+ // btree_multiset::key_comp();
+ //
+ // Returns the key comparator associated with this `btree_multiset`.
+ using Base::key_comp;
+
+ // btree_multiset::value_comp();
+ //
+ // Returns the value comparator associated with this `btree_multiset`. The
+ // keys to sort the elements are the values themselves, therefore `value_comp`
+ // and its sibling member function `key_comp` are equivalent.
+ using Base::value_comp;
+};
+
+// btree::swap(btree::btree_multiset<>, btree::btree_multiset<>)
+//
+// Swaps the contents of two `btree::btree_multiset` containers.
+template <typename K, typename C, typename A>
+void swap(btree_multiset<K, C, A> &x, btree_multiset<K, C, A> &y) {
+ return x.swap(y);
+}
+
+// btree::erase_if(btree::btree_multiset<>, Pred)
+//
+// Erases all elements that satisfy the predicate pred from the container.
+template <typename K, typename C, typename A, typename Pred>
+void erase_if(btree_multiset<K, C, A> &set, Pred pred) {
+ for (auto it = set.begin(); it != set.end();) {
+ if (pred(*it)) {
+ it = set.erase(it);
+ } else {
+ ++it;
+ }
+ }
+}
+
+} // namespace btree
diff --git a/src/include/cpp_lib_backport.h b/src/include/cpp_lib_backport.h
new file mode 100644
index 000000000..ea956c446
--- /dev/null
+++ b/src/include/cpp_lib_backport.h
@@ -0,0 +1,30 @@
+#pragma once
+
+#include <cstring>
+#include <type_traits>
+
+namespace std {
+
+#ifndef __cpp_lib_bit_cast
+#define __cpp_lib_bit_cast 201806L
+
+/// Create a value of type `To` from the bits of `from`.
+template<typename To, typename From>
+requires (sizeof(To) == sizeof(From)) &&
+ std::is_trivially_copyable_v<From> &&
+ std::is_trivially_copyable_v<To>
+[[nodiscard]] constexpr To
+bit_cast(const From& from) noexcept {
+#if __has_builtin(__builtin_bit_cast)
+ return __builtin_bit_cast(To, from);
+#else
+ static_assert(std::is_trivially_constructible_v<To>);
+ To to;
+ std::memcpy(&to, &from, sizeof(To));
+ return to;
+#endif
+}
+
+#endif // __cpp_lib_bit_cast
+
+} // namespace std
diff --git a/src/include/crc32c.h b/src/include/crc32c.h
new file mode 100644
index 000000000..dd4ede666
--- /dev/null
+++ b/src/include/crc32c.h
@@ -0,0 +1,57 @@
+#ifndef CEPH_CRC32C_H
+#define CEPH_CRC32C_H
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef uint32_t (*ceph_crc32c_func_t)(uint32_t crc, unsigned char const *data, unsigned length);
+
+/*
+ * this is a static global with the chosen crc32c implementation for
+ * the given architecture.
+ */
+extern ceph_crc32c_func_t ceph_crc32c_func;
+
+extern ceph_crc32c_func_t ceph_choose_crc32(void);
+
+/**
+ * calculate crc32c for data that is entirely 0 (ZERO)
+ *
+ * Note: works the same as ceph_crc32c_func for data == nullptr,
+ * but faster than the optimized assembly on certain architectures.
+ * This is faster than intel optimized assembly, but not as fast as
+ * ppc64le optimized assembly.
+ *
+ * @param crc initial value
+ * @param length length of buffer
+ */
+uint32_t ceph_crc32c_zeros(uint32_t crc, unsigned length);
+
+/**
+ * calculate crc32c
+ *
+ * Note: if the data pointer is NULL, we calculate a crc value as if
+ * it were zero-filled.
+ *
+ * @param crc initial value
+ * @param data pointer to data buffer
+ * @param length length of buffer
+ */
+static inline uint32_t ceph_crc32c(uint32_t crc, unsigned char const *data, unsigned length)
+{
+#ifndef HAVE_POWER8
+ if (!data && length > 16)
+ return ceph_crc32c_zeros(crc, length);
+#endif /* HAVE_POWER8 */
+
+ return ceph_crc32c_func(crc, data, length);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/include/demangle.h b/src/include/demangle.h
new file mode 100644
index 000000000..9e46d952f
--- /dev/null
+++ b/src/include/demangle.h
@@ -0,0 +1,48 @@
+// -*- 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) 2016 Allen Samuels <allen.samuels@sandisk.com>
+ *
+ * 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_INCLUDE_DEMANGLE
+#define CEPH_INCLUDE_DEMANGLE
+
+//// Stole this code from http://stackoverflow.com/questions/281818/unmangling-the-result-of-stdtype-infoname
+#ifdef __GNUG__
+#include <cstdlib>
+#include <memory>
+#include <cxxabi.h>
+
+static std::string ceph_demangle(const char* name)
+{
+ int status = -4; // some arbitrary value to eliminate the compiler warning
+
+ // enable c++11 by passing the flag -std=c++11 to g++
+ std::unique_ptr<char, void(*)(void*)> res {
+ abi::__cxa_demangle(name, NULL, NULL, &status),
+ std::free
+ };
+
+ return (status == 0) ? res.get() : name ;
+}
+
+#else
+
+// does nothing if not g++
+static std::string demangle(const char* name)
+{
+ return name;
+}
+
+#endif
+
+
+#endif
diff --git a/src/include/denc.h b/src/include/denc.h
new file mode 100644
index 000000000..d075dd518
--- /dev/null
+++ b/src/include/denc.h
@@ -0,0 +1,1895 @@
+// -*- 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) 2016 Allen Samuels <allen.samuels@sandisk.com>
+ *
+ * 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.
+ *
+ */
+
+// If you #include "include/encoding.h" you get the old-style *and*
+// the new-style definitions. (The old-style needs denc_traits<> in
+// order to disable the container helpers when new-style traits are
+// present.)
+
+// You can also just #include "include/denc.h" and get only the
+// new-style helpers. The eventual goal is to drop the legacy
+// definitions.
+
+#ifndef _ENC_DEC_H
+#define _ENC_DEC_H
+
+#include <array>
+#include <bit>
+#include <cstring>
+#include <concepts>
+#include <map>
+#include <optional>
+#include <set>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+#include <boost/container/flat_map.hpp>
+#include <boost/container/flat_set.hpp>
+#include <boost/container/small_vector.hpp>
+#include <boost/intrusive/set.hpp>
+#include <boost/optional.hpp>
+
+#include "include/cpp_lib_backport.h"
+#include "include/compat.h"
+#include "include/int_types.h"
+#include "include/scope_guard.h"
+
+#include "buffer.h"
+#include "byteorder.h"
+
+#include "common/convenience.h"
+#include "common/error_code.h"
+
+template<typename T, typename=void>
+struct denc_traits {
+ static constexpr bool supported = false;
+ static constexpr bool featured = false;
+ static constexpr bool bounded = false;
+ static constexpr bool need_contiguous = true;
+};
+
+template<typename T>
+inline constexpr bool denc_supported = denc_traits<T>::supported;
+
+
+// hack for debug only; FIXME
+//#include <iostream>
+//using std::cout;
+
+// Define this to compile in a dump of all encoded objects to disk to
+// populate ceph-object-corpus. Note that there is an almost
+// identical implementation in encoding.h, but you only need to define
+// ENCODE_DUMP_PATH here.
+//
+// See src/test/encoding/generate-corpus-objects.sh.
+//
+//#define ENCODE_DUMP_PATH /tmp/something
+
+#ifdef ENCODE_DUMP_PATH
+# include <cstdio>
+# include <sys/types.h>
+# include <sys/stat.h>
+# include <fcntl.h>
+
+# define ENCODE_STR(x) #x
+# define ENCODE_STRINGIFY(x) ENCODE_STR(x)
+
+template<typename T>
+class DencDumper {
+public:
+ DencDumper(const char* name,
+ const ceph::bufferlist::contiguous_appender& appender)
+ : name{name},
+ appender{appender},
+ bl_offset{appender.bl.length()},
+ space_offset{space_size()},
+ start{appender.get_pos()}
+ {}
+ ~DencDumper() {
+ if (do_sample()) {
+ dump();
+ }
+ }
+private:
+ static bool do_sample() {
+ // this hackery with bits below is just to get a semi-reasonable
+ // distribution across time. it is somewhat exponential but not
+ // quite.
+ i++;
+ int bits = 0;
+ for (unsigned t = i; t; bits++)
+ t &= t - 1;
+ return bits <= 2;
+ }
+ size_t space_size() const {
+ return appender.get_logical_offset() - appender.get_out_of_band_offset();
+ }
+ void dump() const {
+ char fn[PATH_MAX];
+ ::snprintf(fn, sizeof(fn),
+ ENCODE_STRINGIFY(ENCODE_DUMP_PATH) "/%s__%d.%x", name,
+ getpid(), i++);
+ int fd = ::open(fn, O_WRONLY|O_TRUNC|O_CREAT|O_CLOEXEC|O_BINARY, 0644);
+ if (fd < 0) {
+ return;
+ }
+ auto close_fd = make_scope_guard([fd] { ::close(fd); });
+ if (auto bl_delta = appender.bl.length() - bl_offset; bl_delta > 0) {
+ ceph::bufferlist dump_bl;
+ appender.bl.begin(bl_offset + space_offset).copy(bl_delta - space_offset, dump_bl);
+ const size_t space_len = space_size();
+ dump_bl.append(appender.get_pos() - space_len, space_len);
+ dump_bl.write_fd(fd);
+ } else {
+ size_t len = appender.get_pos() - start;
+ [[maybe_unused]] int r = ::write(fd, start, len);
+ }
+ }
+ const char* name;
+ const ceph::bufferlist::contiguous_appender& appender;
+ const size_t bl_offset;
+ const size_t space_offset;
+ const char* start;
+ static int i;
+};
+
+template<typename T> int DencDumper<T>::i = 0;
+
+# define DENC_DUMP_PRE(Type) \
+ DencDumper<Type> _denc_dumper{#Type, p};
+#else
+# define DENC_DUMP_PRE(Type)
+#endif
+
+
+/*
+
+ top level level functions look like so
+ ======================================
+
+ inline void denc(const T& o, size_t& p, uint64_t features=0);
+ inline void denc(const T& o, ceph::buffer::list::contiguous_appender& p,
+ uint64_t features=0);
+ inline void denc(T& o, ceph::buffer::ptr::const_iterator& p, uint64_t features=0);
+
+ or (for featured objects)
+
+ inline void denc(const T& o, size_t& p, uint64_t features);
+ inline void denc(const T& o, ceph::buffer::list::contiguous_appender& p,
+ uint64_t features);
+ inline void denc(T& o, ceph::buffer::ptr::const_iterator& p, uint64_t features);
+
+ - These are symmetrical, so that they can be used from the magic DENC
+ method of writing the bound_encode/encode/decode methods all in one go;
+ they differ only in the type of p.
+
+ - These are automatically fabricated via a template that calls into
+ the denc_traits<> methods (see below), provided denc_traits<T>::supported
+ is defined and true. They never need to be written explicitly.
+
+
+ static denc_traits<> definitions look like so
+ =============================================
+
+ template<>
+ struct denc_traits<T> {
+ static constexpr bool supported = true;
+ static constexpr bool bounded = false;
+ static constexpr bool featured = false;
+ static constexpr bool need_contiguous = true;
+ static void bound_encode(const T &o, size_t& p, uint64_t f=0);
+ static void encode(const T &o, ceph::buffer::list::contiguous_appender& p,
+ uint64_t f=0);
+ static void decode(T& o, ceph::buffer::ptr::const_iterator &p, uint64_t f=0);
+ };
+
+ or (for featured objects)
+
+ template<>
+ struct denc_traits<T> {
+ static constexpr bool supported = true;
+ static constexpr bool bounded = false;
+ static constexpr bool featured = true;
+ static constexpr bool need_contiguous = true;
+ static void bound_encode(const T &o, size_t& p, uint64_t f);
+ static void encode(const T &o, ceph::buffer::list::contiguous_appender& p,
+ uint64_t f);
+ static void decode(T& o, ceph::buffer::ptr::const_iterator &p, uint64_t f=0);
+ };
+
+ - denc_traits<T> is normally declared via the WRITE_CLASS_DENC(type) macro,
+ which is used in place of the old-style WRITE_CLASS_ENCODER(type) macro.
+ There are _FEATURED and _BOUNDED variants. The class traits simply call
+ into class methods of the same name (see below).
+
+ - denc_traits<T> can also be written explicitly for some type to indicate
+ how it should be encoded. This is the "source of truth" for how a type
+ is encoded.
+
+ - denc_traits<T> are declared for the base integer types, string, ceph::buffer::ptr,
+ and ceph::buffer::list base types.
+
+ - denc_traits<std::foo<T>>-like traits are declared for standard container
+ types.
+
+
+ class methods look like so
+ ==========================
+
+ void bound_encode(size_t& p) const;
+ void encode(ceph::buffer::list::contiguous_appender& p) const;
+ void decode(ceph::buffer::ptr::const_iterator &p);
+
+ or (for featured objects)
+
+ void bound_encode(size_t& p, uint64_t f) const;
+ void encode(ceph::buffer::list::contiguous_appender& p, uint64_t f) const;
+ void decode(ceph::buffer::ptr::const_iterator &p);
+
+ - These are normally invoked by the denc_traits<> methods that are
+ declared via WRITE_CLASS_DENC, although you can also invoke them explicitly
+ in your code.
+
+ - These methods are optimised for contiguous buffer, but denc() will try
+ rebuild a contigous one if the decoded ceph::buffer::list is segmented. If you are
+ concerned about the cost, you might want to define yet another method:
+
+ void decode(ceph::buffer::list::iterator &p);
+
+ - These can be defined either explicitly (as above), or can be "magically"
+ defined all in one go using the DENC macro and DENC_{START,FINISH} helpers
+ (which work like the legacy {ENCODE,DECODE}_{START,FINISH} macros):
+
+ class foo_t {
+ ...
+ DENC(foo_t, v, p) {
+ DENC_START(1, 1, p);
+ denc(v.foo, p);
+ denc(v.bar, p);
+ denc(v.baz, p);
+ DENC_FINISH(p);
+ }
+ ...
+ };
+ WRITE_CLASS_DENC(foo_t)
+
+ */
+
+// ---------------------------------------------------------------------
+// raw types
+namespace _denc {
+template<typename T, typename... U>
+concept is_any_of = (std::same_as<T, U> || ...);
+
+template<typename T, typename=void> struct underlying_type {
+ using type = T;
+};
+template<typename T>
+struct underlying_type<T, std::enable_if_t<std::is_enum_v<T>>> {
+ using type = std::underlying_type_t<T>;
+};
+template<typename T>
+using underlying_type_t = typename underlying_type<T>::type;
+}
+
+template<class It>
+concept is_const_iterator = requires(It& it, size_t n) {
+ { it.get_pos_add(n) } -> std::same_as<const char*>;
+};
+
+template<typename T, is_const_iterator It>
+const T& get_pos_add(It& i) {
+ return *reinterpret_cast<const T*>(i.get_pos_add(sizeof(T)));
+}
+
+template<typename T, class It>
+requires (!is_const_iterator<It>)
+T& get_pos_add(It& i) {
+ return *reinterpret_cast<T*>(i.get_pos_add(sizeof(T)));
+}
+
+template<typename T>
+requires _denc::is_any_of<_denc::underlying_type_t<T>,
+ ceph_le64, ceph_le32, ceph_le16, uint8_t
+#ifndef _CHAR_IS_SIGNED
+ , int8_t
+#endif
+ >
+struct denc_traits<T> {
+ static constexpr bool supported = true;
+ static constexpr bool featured = false;
+ static constexpr bool bounded = true;
+ static constexpr bool need_contiguous = false;
+ static void bound_encode(const T &o, size_t& p, uint64_t f=0) {
+ p += sizeof(T);
+ }
+ template<class It>
+ requires (!is_const_iterator<It>)
+ static void encode(const T &o, It& p, uint64_t f=0) {
+ get_pos_add<T>(p) = o;
+ }
+ template<is_const_iterator It>
+ static void decode(T& o, It& p, uint64_t f=0) {
+ o = get_pos_add<T>(p);
+ }
+ static void decode(T& o, ceph::buffer::list::const_iterator &p) {
+ p.copy(sizeof(T), reinterpret_cast<char*>(&o));
+ }
+};
+
+
+// -----------------------------------------------------------------------
+// integer types
+
+// itype == internal type
+// otype == external type, i.e., the type on the wire
+
+// NOTE: the overload resolution ensures that the legacy encode/decode methods
+// defined for int types is preferred to the ones defined using the specialized
+// template, and hence get selected. This machinery prevents these these from
+// getting glued into the legacy encode/decode methods; the overhead of setting
+// up a contiguous_appender etc is likely to be slower.
+namespace _denc {
+
+template<typename T> struct ExtType {
+ using type = void;
+};
+
+template<typename T>
+requires _denc::is_any_of<T,
+ int16_t, uint16_t>
+struct ExtType<T> {
+ using type = ceph_le16;
+};
+
+template<typename T>
+requires _denc::is_any_of<T,
+ int32_t, uint32_t>
+struct ExtType<T> {
+ using type = ceph_le32;
+};
+
+template<typename T>
+requires _denc::is_any_of<T,
+ int64_t, uint64_t>
+struct ExtType<T> {
+ using type = ceph_le64;
+};
+
+template<>
+struct ExtType<bool> {
+ using type = uint8_t;
+};
+template<typename T>
+using ExtType_t = typename ExtType<T>::type;
+} // namespace _denc
+
+template<typename T>
+requires (!std::is_void_v<_denc::ExtType_t<T>>)
+struct denc_traits<T>
+{
+ static constexpr bool supported = true;
+ static constexpr bool featured = false;
+ static constexpr bool bounded = true;
+ static constexpr bool need_contiguous = false;
+ using etype = _denc::ExtType_t<T>;
+ static void bound_encode(const T &o, size_t& p, uint64_t f=0) {
+ p += sizeof(etype);
+ }
+ template<class It>
+ requires (!is_const_iterator<It>)
+ static void encode(const T &o, It& p, uint64_t f=0) {
+ get_pos_add<etype>(p) = o;
+ }
+ template<is_const_iterator It>
+ static void decode(T& o, It &p, uint64_t f=0) {
+ o = get_pos_add<etype>(p);
+ }
+ static void decode(T& o, ceph::buffer::list::const_iterator &p) {
+ etype e;
+ p.copy(sizeof(etype), reinterpret_cast<char*>(&e));
+ o = e;
+ }
+};
+
+// varint
+//
+// high bit of each byte indicates another byte follows.
+template<typename T>
+inline void denc_varint(T v, size_t& p) {
+ p += sizeof(T) + 1;
+}
+
+template<typename T>
+inline void denc_varint(T v, ceph::buffer::list::contiguous_appender& p) {
+ uint8_t byte = v & 0x7f;
+ v >>= 7;
+ while (v) {
+ byte |= 0x80;
+ get_pos_add<__u8>(p) = byte;
+ byte = (v & 0x7f);
+ v >>= 7;
+ }
+ get_pos_add<__u8>(p) = byte;
+}
+
+template<typename T>
+inline void denc_varint(T& v, ceph::buffer::ptr::const_iterator& p) {
+ uint8_t byte = *(__u8*)p.get_pos_add(1);
+ v = byte & 0x7f;
+ int shift = 7;
+ while (byte & 0x80) {
+ byte = get_pos_add<__u8>(p);
+ v |= (T)(byte & 0x7f) << shift;
+ shift += 7;
+ }
+}
+
+
+// signed varint encoding
+//
+// low bit = 1 = negative, 0 = positive
+// high bit of every byte indicates whether another byte follows.
+inline void denc_signed_varint(int64_t v, size_t& p) {
+ p += sizeof(v) + 2;
+}
+template<class It>
+requires (!is_const_iterator<It>)
+void denc_signed_varint(int64_t v, It& p) {
+ if (v < 0) {
+ v = (-v << 1) | 1;
+ } else {
+ v <<= 1;
+ }
+ denc_varint(v, p);
+}
+
+template<typename T, is_const_iterator It>
+inline void denc_signed_varint(T& v, It& p)
+{
+ int64_t i = 0;
+ denc_varint(i, p);
+ if (i & 1) {
+ v = -(i >> 1);
+ } else {
+ v = i >> 1;
+ }
+}
+
+// varint + lowz encoding
+//
+// first(low) 2 bits = how many low zero bits (nibbles)
+// high bit of each byte = another byte follows
+// (so, 5 bits data in first byte, 7 bits data thereafter)
+inline void denc_varint_lowz(uint64_t v, size_t& p) {
+ p += sizeof(v) + 2;
+}
+inline void denc_varint_lowz(uint64_t v,
+ ceph::buffer::list::contiguous_appender& p) {
+ int lowznib = v ? (std::countr_zero(v) / 4) : 0;
+ if (lowznib > 3)
+ lowznib = 3;
+ v >>= lowznib * 4;
+ v <<= 2;
+ v |= lowznib;
+ denc_varint(v, p);
+}
+
+template<typename T>
+inline void denc_varint_lowz(T& v, ceph::buffer::ptr::const_iterator& p)
+{
+ uint64_t i = 0;
+ denc_varint(i, p);
+ int lowznib = (i & 3);
+ i >>= 2;
+ i <<= lowznib * 4;
+ v = i;
+}
+
+// signed varint + lowz encoding
+//
+// first low bit = 1 for negative, 0 for positive
+// next 2 bits = how many low zero bits (nibbles)
+// high bit of each byte = another byte follows
+// (so, 4 bits data in first byte, 7 bits data thereafter)
+inline void denc_signed_varint_lowz(int64_t v, size_t& p) {
+ p += sizeof(v) + 2;
+}
+template<class It>
+requires (!is_const_iterator<It>)
+inline void denc_signed_varint_lowz(int64_t v, It& p) {
+ bool negative = false;
+ if (v < 0) {
+ v = -v;
+ negative = true;
+ }
+ unsigned lowznib = v ? (std::countr_zero(std::bit_cast<uint64_t>(v)) / 4) : 0u;
+ if (lowznib > 3)
+ lowznib = 3;
+ v >>= lowznib * 4;
+ v <<= 3;
+ v |= lowznib << 1;
+ v |= (int)negative;
+ denc_varint(v, p);
+}
+
+template<typename T, is_const_iterator It>
+inline void denc_signed_varint_lowz(T& v, It& p)
+{
+ int64_t i = 0;
+ denc_varint(i, p);
+ int lowznib = (i & 6) >> 1;
+ if (i & 1) {
+ i >>= 3;
+ i <<= lowznib * 4;
+ v = -i;
+ } else {
+ i >>= 3;
+ i <<= lowznib * 4;
+ v = i;
+ }
+}
+
+
+// LBA
+//
+// first 1-3 bits = how many low zero bits
+// *0 = 12 (common 4 K alignment case)
+// *01 = 16
+// *011 = 20
+// *111 = byte
+// then 28-30 bits of data
+// then last bit = another byte follows
+// high bit of each subsequent byte = another byte follows
+inline void denc_lba(uint64_t v, size_t& p) {
+ p += sizeof(v) + 2;
+}
+
+template<class It>
+requires (!is_const_iterator<It>)
+inline void denc_lba(uint64_t v, It& p) {
+ int low_zero_nibbles = v ? std::countr_zero(v) / 4 : 0;
+ int pos;
+ uint32_t word;
+ int t = low_zero_nibbles - 3;
+ if (t < 0) {
+ pos = 3;
+ word = 0x7;
+ } else if (t < 3) {
+ v >>= (low_zero_nibbles * 4);
+ pos = t + 1;
+ word = (1 << t) - 1;
+ } else {
+ v >>= 20;
+ pos = 3;
+ word = 0x3;
+ }
+ word |= (v << pos) & 0x7fffffff;
+ v >>= 31 - pos;
+ if (!v) {
+ *(ceph_le32*)p.get_pos_add(sizeof(uint32_t)) = word;
+ return;
+ }
+ word |= 0x80000000;
+ *(ceph_le32*)p.get_pos_add(sizeof(uint32_t)) = word;
+ uint8_t byte = v & 0x7f;
+ v >>= 7;
+ while (v) {
+ byte |= 0x80;
+ *(__u8*)p.get_pos_add(1) = byte;
+ byte = (v & 0x7f);
+ v >>= 7;
+ }
+ *(__u8*)p.get_pos_add(1) = byte;
+}
+
+template<is_const_iterator It>
+inline void denc_lba(uint64_t& v, It& p) {
+ uint32_t word = *(ceph_le32*)p.get_pos_add(sizeof(uint32_t));
+ int shift = 0;
+ switch (word & 7) {
+ case 0:
+ case 2:
+ case 4:
+ case 6:
+ v = (uint64_t)(word & 0x7ffffffe) << (12 - 1);
+ shift = 12 + 30;
+ break;
+ case 1:
+ case 5:
+ v = (uint64_t)(word & 0x7ffffffc) << (16 - 2);
+ shift = 16 + 29;
+ break;
+ case 3:
+ v = (uint64_t)(word & 0x7ffffff8) << (20 - 3);
+ shift = 20 + 28;
+ break;
+ case 7:
+ v = (uint64_t)(word & 0x7ffffff8) >> 3;
+ shift = 28;
+ }
+ uint8_t byte = word >> 24;
+ while (byte & 0x80) {
+ byte = *(__u8*)p.get_pos_add(1);
+ v |= (uint64_t)(byte & 0x7f) << shift;
+ shift += 7;
+ }
+}
+
+
+// ---------------------------------------------------------------------
+// denc top-level methods that call into denc_traits<T> methods
+
+template<typename T, typename traits=denc_traits<T>>
+inline std::enable_if_t<traits::supported> denc(
+ const T& o,
+ size_t& p,
+ uint64_t f=0)
+{
+ if constexpr (traits::featured) {
+ traits::bound_encode(o, p, f);
+ } else {
+ traits::bound_encode(o, p);
+ }
+}
+
+template<typename T, class It, typename traits=denc_traits<T>>
+requires traits::supported && (!is_const_iterator<It>)
+inline void
+denc(const T& o,
+ It& p,
+ uint64_t features=0)
+{
+ if constexpr (traits::featured) {
+ traits::encode(o, p, features);
+ } else {
+ traits::encode(o, p);
+ }
+}
+
+template<typename T, is_const_iterator It, typename traits=denc_traits<T>>
+requires traits::supported
+inline void
+denc(T& o,
+ It& p,
+ uint64_t features=0)
+{
+ if constexpr (traits::featured) {
+ traits::decode(o, p, features);
+ } else {
+ traits::decode(o, p);
+ }
+}
+
+namespace _denc {
+template<typename T, typename = void>
+struct has_legacy_denc : std::false_type {};
+template<typename T>
+struct has_legacy_denc<T, decltype(std::declval<T&>()
+ .decode(std::declval<
+ ceph::buffer::list::const_iterator&>()))>
+ : std::true_type {
+ static void decode(T& v, ceph::buffer::list::const_iterator& p) {
+ v.decode(p);
+ }
+};
+template<typename T>
+struct has_legacy_denc<T,
+ std::enable_if_t<
+ !denc_traits<T>::need_contiguous>> : std::true_type {
+ static void decode(T& v, ceph::buffer::list::const_iterator& p) {
+ denc_traits<T>::decode(v, p);
+ }
+};
+}
+
+template<typename T,
+ typename traits=denc_traits<T>,
+ typename has_legacy_denc=_denc::has_legacy_denc<T>>
+inline std::enable_if_t<traits::supported &&
+ has_legacy_denc::value> denc(
+ T& o,
+ ceph::buffer::list::const_iterator& p)
+{
+ has_legacy_denc::decode(o, p);
+}
+
+// ---------------------------------------------------------------------
+// base types and containers
+
+//
+// std::string
+//
+template<typename A>
+struct denc_traits<std::basic_string<char,std::char_traits<char>,A>> {
+private:
+ using value_type = std::basic_string<char,std::char_traits<char>,A>;
+
+public:
+ static constexpr bool supported = true;
+ static constexpr bool featured = false;
+ static constexpr bool bounded = false;
+ static constexpr bool need_contiguous = false;
+
+ static void bound_encode(const value_type& s, size_t& p, uint64_t f=0) {
+ p += sizeof(uint32_t) + s.size();
+ }
+ template<class It>
+ static void encode(const value_type& s,
+ It& p,
+ uint64_t f=0) {
+ denc((uint32_t)s.size(), p);
+ memcpy(p.get_pos_add(s.size()), s.data(), s.size());
+ }
+ template<class It>
+ static void decode(value_type& s,
+ It& p,
+ uint64_t f=0) {
+ uint32_t len;
+ denc(len, p);
+ decode_nohead(len, s, p);
+ }
+ static void decode(value_type& s, ceph::buffer::list::const_iterator& p)
+ {
+ uint32_t len;
+ denc(len, p);
+ decode_nohead(len, s, p);
+ }
+ template<class It>
+ static void decode_nohead(size_t len, value_type& s, It& p) {
+ s.clear();
+ if (len) {
+ s.append(p.get_pos_add(len), len);
+ }
+ }
+ static void decode_nohead(size_t len, value_type& s,
+ ceph::buffer::list::const_iterator& p) {
+ if (len) {
+ if constexpr (std::is_same_v<value_type, std::string>) {
+ s.clear();
+ p.copy(len, s);
+ } else {
+ s.resize(len);
+ p.copy(len, s.data());
+ }
+ } else {
+ s.clear();
+ }
+ }
+ template<class It>
+ requires (!is_const_iterator<It>)
+ static void
+ encode_nohead(const value_type& s, It& p) {
+ auto len = s.length();
+ maybe_inline_memcpy(p.get_pos_add(len), s.data(), len, 16);
+ }
+};
+
+//
+// ceph::buffer::ptr
+//
+template<>
+struct denc_traits<ceph::buffer::ptr> {
+ static constexpr bool supported = true;
+ static constexpr bool featured = false;
+ static constexpr bool bounded = false;
+ static constexpr bool need_contiguous = false;
+ static void bound_encode(const ceph::buffer::ptr& v, size_t& p, uint64_t f=0) {
+ p += sizeof(uint32_t) + v.length();
+ }
+ template <class It>
+ requires (!is_const_iterator<It>)
+ static void
+ encode(const ceph::buffer::ptr& v, It& p, uint64_t f=0) {
+ denc((uint32_t)v.length(), p);
+ p.append(v);
+ }
+ template <is_const_iterator It>
+ static void
+ decode(ceph::buffer::ptr& v, It& p, uint64_t f=0) {
+ uint32_t len;
+ denc(len, p);
+ v = p.get_ptr(len);
+ }
+ static void decode(ceph::buffer::ptr& v, ceph::buffer::list::const_iterator& p) {
+ uint32_t len;
+ denc(len, p);
+ ceph::buffer::list s;
+ p.copy(len, s);
+ if (len) {
+ if (s.get_num_buffers() == 1)
+ v = s.front();
+ else
+ v = ceph::buffer::copy(s.c_str(), s.length());
+ }
+ }
+};
+
+//
+// ceph::buffer::list
+//
+template<>
+struct denc_traits<ceph::buffer::list> {
+ static constexpr bool supported = true;
+ static constexpr bool featured = false;
+ static constexpr bool bounded = false;
+ static constexpr bool need_contiguous = false;
+ static void bound_encode(const ceph::buffer::list& v, size_t& p, uint64_t f=0) {
+ p += sizeof(uint32_t) + v.length();
+ }
+ static void encode(const ceph::buffer::list& v, ceph::buffer::list::contiguous_appender& p,
+ uint64_t f=0) {
+ denc((uint32_t)v.length(), p);
+ p.append(v);
+ }
+ static void decode(ceph::buffer::list& v, ceph::buffer::ptr::const_iterator& p, uint64_t f=0) {
+ uint32_t len = 0;
+ denc(len, p);
+ v.clear();
+ v.push_back(p.get_ptr(len));
+ }
+ static void decode(ceph::buffer::list& v, ceph::buffer::list::const_iterator& p) {
+ uint32_t len;
+ denc(len, p);
+ v.clear();
+ p.copy(len, v);
+ }
+ static void encode_nohead(const ceph::buffer::list& v,
+ ceph::buffer::list::contiguous_appender& p) {
+ p.append(v);
+ }
+ static void decode_nohead(size_t len, ceph::buffer::list& v,
+ ceph::buffer::ptr::const_iterator& p) {
+ v.clear();
+ if (len) {
+ v.append(p.get_ptr(len));
+ }
+ }
+ static void decode_nohead(size_t len, ceph::buffer::list& v,
+ ceph::buffer::list::const_iterator& p) {
+ v.clear();
+ p.copy(len, v);
+ }
+};
+
+//
+// std::pair<A, B>
+//
+template<typename A, typename B>
+struct denc_traits<
+ std::pair<A, B>,
+ std::enable_if_t<denc_supported<std::remove_const_t<A>> && denc_supported<B>>> {
+ typedef denc_traits<A> a_traits;
+ typedef denc_traits<B> b_traits;
+
+ static constexpr bool supported = true;
+ static constexpr bool featured = a_traits::featured || b_traits::featured ;
+ static constexpr bool bounded = a_traits::bounded && b_traits::bounded;
+ static constexpr bool need_contiguous = (a_traits::need_contiguous ||
+ b_traits::need_contiguous);
+
+ static void bound_encode(const std::pair<A,B>& v, size_t& p, uint64_t f = 0) {
+ if constexpr (featured) {
+ denc(v.first, p, f);
+ denc(v.second, p, f);
+ } else {
+ denc(v.first, p);
+ denc(v.second, p);
+ }
+ }
+
+ static void encode(const std::pair<A,B>& v, ceph::buffer::list::contiguous_appender& p,
+ uint64_t f = 0) {
+ if constexpr (featured) {
+ denc(v.first, p, f);
+ denc(v.second, p, f);
+ } else {
+ denc(v.first, p);
+ denc(v.second, p);
+ }
+ }
+
+ static void decode(std::pair<A,B>& v, ceph::buffer::ptr::const_iterator& p, uint64_t f=0) {
+ denc(const_cast<std::remove_const_t<A>&>(v.first), p, f);
+ denc(v.second, p, f);
+ }
+ template<typename AA=A>
+ static std::enable_if_t<!!sizeof(AA) && !need_contiguous>
+ decode(std::pair<A,B>& v, ceph::buffer::list::const_iterator& p,
+ uint64_t f = 0) {
+ denc(const_cast<std::remove_const_t<AA>&>(v.first), p);
+ denc(v.second, p);
+ }
+};
+
+namespace _denc {
+ template<template<class...> class C, typename Details, typename ...Ts>
+ struct container_base {
+ private:
+ using container = C<Ts...>;
+ using T = typename Details::T;
+
+ public:
+ using traits = denc_traits<T>;
+
+ static constexpr bool supported = true;
+ static constexpr bool featured = traits::featured;
+ static constexpr bool bounded = false;
+ static constexpr bool need_contiguous = traits::need_contiguous;
+
+ template<typename U=T>
+ static void bound_encode(const container& s, size_t& p, uint64_t f = 0) {
+ p += sizeof(uint32_t);
+ if constexpr (traits::bounded) {
+#if _GLIBCXX_USE_CXX11_ABI
+ // intensionally not calling container's empty() method to not prohibit
+ // compiler from optimizing the check if it and the ::size() operate on
+ // different memory (observed when std::list::empty() works on pointers,
+ // not the size field).
+ if (const auto elem_num = s.size(); elem_num > 0) {
+#else
+ if (!s.empty()) {
+ const auto elem_num = s.size();
+#endif
+ // STL containers use weird element types like std::pair<const K, V>;
+ // cast to something we have denc_traits for.
+ size_t elem_size = 0;
+ if constexpr (traits::featured) {
+ denc(static_cast<const T&>(*s.begin()), elem_size, f);
+ } else {
+ denc(static_cast<const T&>(*s.begin()), elem_size);
+ }
+ p += elem_size * elem_num;
+ }
+ } else {
+ for (const T& e : s) {
+ if constexpr (traits::featured) {
+ denc(e, p, f);
+ } else {
+ denc(e, p);
+ }
+ }
+ }
+ }
+
+ template<typename U=T>
+ static void encode(const container& s,
+ ceph::buffer::list::contiguous_appender& p,
+ uint64_t f = 0) {
+ denc((uint32_t)s.size(), p);
+ if constexpr (traits::featured) {
+ encode_nohead(s, p, f);
+ } else {
+ encode_nohead(s, p);
+ }
+ }
+ static void decode(container& s, ceph::buffer::ptr::const_iterator& p,
+ uint64_t f = 0) {
+ uint32_t num;
+ denc(num, p);
+ decode_nohead(num, s, p, f);
+ }
+ template<typename U=T>
+ static std::enable_if_t<!!sizeof(U) && !need_contiguous>
+ decode(container& s, ceph::buffer::list::const_iterator& p) {
+ uint32_t num;
+ denc(num, p);
+ decode_nohead(num, s, p);
+ }
+
+ // nohead
+ static void encode_nohead(const container& s, ceph::buffer::list::contiguous_appender& p,
+ uint64_t f = 0) {
+ for (const T& e : s) {
+ if constexpr (traits::featured) {
+ denc(e, p, f);
+ } else {
+ denc(e, p);
+ }
+ }
+ }
+ static void decode_nohead(size_t num, container& s,
+ ceph::buffer::ptr::const_iterator& p,
+ uint64_t f=0) {
+ s.clear();
+ Details::reserve(s, num);
+ while (num--) {
+ T t;
+ denc(t, p, f);
+ Details::insert(s, std::move(t));
+ }
+ }
+ template<typename U=T>
+ static std::enable_if_t<!!sizeof(U) && !need_contiguous>
+ decode_nohead(size_t num, container& s,
+ ceph::buffer::list::const_iterator& p) {
+ s.clear();
+ Details::reserve(s, num);
+ while (num--) {
+ T t;
+ denc(t, p);
+ Details::insert(s, std::move(t));
+ }
+ }
+ };
+
+ template<typename T>
+ class container_has_reserve {
+ template<typename U, U> struct SFINAE_match;
+ template<typename U>
+ static std::true_type test(SFINAE_match<T(*)(typename T::size_type),
+ &U::reserve>*);
+
+ template<typename U>
+ static std::false_type test(...);
+
+ public:
+ static constexpr bool value = decltype(
+ test<denc_traits<T>>(0))::value;
+ };
+ template<typename T>
+ inline constexpr bool container_has_reserve_v =
+ container_has_reserve<T>::value;
+
+
+ template<typename Container>
+ struct container_details_base {
+ using T = typename Container::value_type;
+ static void reserve(Container& c, size_t s) {
+ if constexpr (container_has_reserve_v<Container>) {
+ c.reserve(s);
+ }
+ }
+ };
+
+ template<typename Container>
+ struct pushback_details : public container_details_base<Container> {
+ template<typename ...Args>
+ static void insert(Container& c, Args&& ...args) {
+ c.emplace_back(std::forward<Args>(args)...);
+ }
+ };
+}
+
+template<typename T, typename ...Ts>
+struct denc_traits<
+ std::list<T, Ts...>,
+ typename std::enable_if_t<denc_traits<T>::supported>>
+ : public _denc::container_base<std::list,
+ _denc::pushback_details<std::list<T, Ts...>>,
+ T, Ts...> {};
+
+template<typename T, typename ...Ts>
+struct denc_traits<
+ std::vector<T, Ts...>,
+ typename std::enable_if_t<denc_traits<T>::supported>>
+ : public _denc::container_base<std::vector,
+ _denc::pushback_details<std::vector<T, Ts...>>,
+ T, Ts...> {};
+
+template<typename T, std::size_t N, typename ...Ts>
+struct denc_traits<
+ boost::container::small_vector<T, N, Ts...>,
+ typename std::enable_if_t<denc_traits<T>::supported>> {
+private:
+ using container = boost::container::small_vector<T, N, Ts...>;
+public:
+ using traits = denc_traits<T>;
+
+ static constexpr bool supported = true;
+ static constexpr bool featured = traits::featured;
+ static constexpr bool bounded = false;
+ static constexpr bool need_contiguous = traits::need_contiguous;
+
+ template<typename U=T>
+ static void bound_encode(const container& s, size_t& p, uint64_t f = 0) {
+ p += sizeof(uint32_t);
+ if constexpr (traits::bounded) {
+ if (!s.empty()) {
+ const auto elem_num = s.size();
+ size_t elem_size = 0;
+ if constexpr (traits::featured) {
+ denc(*s.begin(), elem_size, f);
+ } else {
+ denc(*s.begin(), elem_size);
+ }
+ p += elem_size * elem_num;
+ }
+ } else {
+ for (const T& e : s) {
+ if constexpr (traits::featured) {
+ denc(e, p, f);
+ } else {
+ denc(e, p);
+ }
+ }
+ }
+ }
+
+ template<typename U=T>
+ static void encode(const container& s,
+ ceph::buffer::list::contiguous_appender& p,
+ uint64_t f = 0) {
+ denc((uint32_t)s.size(), p);
+ if constexpr (traits::featured) {
+ encode_nohead(s, p, f);
+ } else {
+ encode_nohead(s, p);
+ }
+ }
+ static void decode(container& s, ceph::buffer::ptr::const_iterator& p,
+ uint64_t f = 0) {
+ uint32_t num;
+ denc(num, p);
+ decode_nohead(num, s, p, f);
+ }
+ template<typename U=T>
+ static std::enable_if_t<!!sizeof(U) && !need_contiguous>
+ decode(container& s, ceph::buffer::list::const_iterator& p) {
+ uint32_t num;
+ denc(num, p);
+ decode_nohead(num, s, p);
+ }
+
+ // nohead
+ static void encode_nohead(const container& s, ceph::buffer::list::contiguous_appender& p,
+ uint64_t f = 0) {
+ for (const T& e : s) {
+ if constexpr (traits::featured) {
+ denc(e, p, f);
+ } else {
+ denc(e, p);
+ }
+ }
+ }
+ static void decode_nohead(size_t num, container& s,
+ ceph::buffer::ptr::const_iterator& p,
+ uint64_t f=0) {
+ s.clear();
+ s.reserve(num);
+ while (num--) {
+ T t;
+ denc(t, p, f);
+ s.push_back(std::move(t));
+ }
+ }
+ template<typename U=T>
+ static std::enable_if_t<!!sizeof(U) && !need_contiguous>
+ decode_nohead(size_t num, container& s,
+ ceph::buffer::list::const_iterator& p) {
+ s.clear();
+ s.reserve(num);
+ while (num--) {
+ T t;
+ denc(t, p);
+ s.push_back(std::move(t));
+ }
+ }
+};
+
+namespace _denc {
+ template<typename Container>
+ struct setlike_details : public container_details_base<Container> {
+ using T = typename Container::value_type;
+ template<typename ...Args>
+ static void insert(Container& c, Args&& ...args) {
+ c.emplace_hint(c.cend(), std::forward<Args>(args)...);
+ }
+ };
+}
+
+template<typename T, typename ...Ts>
+struct denc_traits<
+ std::set<T, Ts...>,
+ std::enable_if_t<denc_traits<T>::supported>>
+ : public _denc::container_base<std::set,
+ _denc::setlike_details<std::set<T, Ts...>>,
+ T, Ts...> {};
+
+template<typename T, typename ...Ts>
+struct denc_traits<
+ boost::container::flat_set<T, Ts...>,
+ std::enable_if_t<denc_traits<T>::supported>>
+ : public _denc::container_base<
+ boost::container::flat_set,
+ _denc::setlike_details<boost::container::flat_set<T, Ts...>>,
+ T, Ts...> {};
+
+namespace _denc {
+ template<typename Container>
+ struct maplike_details : public container_details_base<Container> {
+ using T = typename Container::value_type;
+ template<typename ...Args>
+ static void insert(Container& c, Args&& ...args) {
+ c.emplace_hint(c.cend(), std::forward<Args>(args)...);
+ }
+ };
+}
+
+template<typename A, typename B, typename ...Ts>
+struct denc_traits<
+ std::map<A, B, Ts...>,
+ std::enable_if_t<denc_traits<A>::supported &&
+ denc_traits<B>::supported>>
+ : public _denc::container_base<std::map,
+ _denc::maplike_details<std::map<A, B, Ts...>>,
+ A, B, Ts...> {};
+
+template<typename A, typename B, typename ...Ts>
+struct denc_traits<
+ boost::container::flat_map<A, B, Ts...>,
+ std::enable_if_t<denc_traits<A>::supported &&
+ denc_traits<B>::supported>>
+ : public _denc::container_base<
+ boost::container::flat_map,
+ _denc::maplike_details<boost::container::flat_map<
+ A, B, Ts...>>,
+ A, B, Ts...> {};
+
+template<typename T, size_t N>
+struct denc_traits<
+ std::array<T, N>,
+ std::enable_if_t<denc_traits<T>::supported>> {
+private:
+ using container = std::array<T, N>;
+public:
+ using traits = denc_traits<T>;
+
+ static constexpr bool supported = true;
+ static constexpr bool featured = traits::featured;
+ static constexpr bool bounded = traits::bounded;
+ static constexpr bool need_contiguous = traits::need_contiguous;
+
+ static void bound_encode(const container& s, size_t& p, uint64_t f = 0) {
+ if constexpr (traits::bounded) {
+ if constexpr (traits::featured) {
+ if (!s.empty()) {
+ size_t elem_size = 0;
+ denc(*s.begin(), elem_size, f);
+ p += elem_size * s.size();
+ }
+ } else {
+ size_t elem_size = 0;
+ denc(*s.begin(), elem_size);
+ p += elem_size * N;
+ }
+ } else {
+ for (const auto& e : s) {
+ if constexpr (traits::featured) {
+ denc(e, p, f);
+ } else {
+ denc(e, p);
+ }
+ }
+ }
+ }
+
+ static void encode(const container& s, ceph::buffer::list::contiguous_appender& p,
+ uint64_t f = 0) {
+ for (const auto& e : s) {
+ if constexpr (traits::featured) {
+ denc(e, p, f);
+ } else {
+ denc(e, p);
+ }
+ }
+ }
+ static void decode(container& s, ceph::buffer::ptr::const_iterator& p,
+ uint64_t f = 0) {
+ for (auto& e : s)
+ denc(e, p, f);
+ }
+ template<typename U=T>
+ static std::enable_if_t<!!sizeof(U) &&
+ !need_contiguous>
+ decode(container& s, ceph::buffer::list::const_iterator& p) {
+ for (auto& e : s) {
+ denc(e, p);
+ }
+ }
+};
+
+template<typename... Ts>
+struct denc_traits<
+ std::tuple<Ts...>,
+ std::enable_if_t<(denc_traits<Ts>::supported && ...)>> {
+
+private:
+ static_assert(sizeof...(Ts) > 0,
+ "Zero-length tuples are not supported.");
+ using container = std::tuple<Ts...>;
+
+public:
+
+ static constexpr bool supported = true;
+ static constexpr bool featured = (denc_traits<Ts>::featured || ...);
+ static constexpr bool bounded = (denc_traits<Ts>::bounded && ...);
+ static constexpr bool need_contiguous =
+ (denc_traits<Ts>::need_contiguous || ...);
+
+ template<typename U = container>
+ static std::enable_if_t<denc_traits<U>::featured>
+ bound_encode(const container& s, size_t& p, uint64_t f) {
+ ceph::for_each(s, [&p, f] (const auto& e) {
+ if constexpr (denc_traits<std::decay_t<decltype(e)>>::featured) {
+ denc(e, p, f);
+ } else {
+ denc(e, p);
+ }
+ });
+ }
+ template<typename U = container>
+ static std::enable_if_t<!denc_traits<U>::featured>
+ bound_encode(const container& s, size_t& p) {
+ ceph::for_each(s, [&p] (const auto& e) {
+ denc(e, p);
+ });
+ }
+
+ template<typename U = container>
+ static std::enable_if_t<denc_traits<U>::featured>
+ encode(const container& s, ceph::buffer::list::contiguous_appender& p,
+ uint64_t f) {
+ ceph::for_each(s, [&p, f] (const auto& e) {
+ if constexpr (denc_traits<std::decay_t<decltype(e)>>::featured) {
+ denc(e, p, f);
+ } else {
+ denc(e, p);
+ }
+ });
+ }
+ template<typename U = container>
+ static std::enable_if_t<!denc_traits<U>::featured>
+ encode(const container& s, ceph::buffer::list::contiguous_appender& p) {
+ ceph::for_each(s, [&p] (const auto& e) {
+ denc(e, p);
+ });
+ }
+
+ static void decode(container& s, ceph::buffer::ptr::const_iterator& p,
+ uint64_t f = 0) {
+ ceph::for_each(s, [&p] (auto& e) {
+ denc(e, p);
+ });
+ }
+
+ template<typename U = container>
+ static std::enable_if_t<!denc_traits<U>::need_contiguous>
+ decode(container& s, ceph::buffer::list::const_iterator& p, uint64_t f = 0) {
+ ceph::for_each(s, [&p] (auto& e) {
+ denc(e, p);
+ });
+ }
+};
+
+//
+// boost::optional<T>
+//
+template<typename T>
+struct denc_traits<
+ boost::optional<T>,
+ std::enable_if_t<denc_traits<T>::supported>> {
+ using traits = denc_traits<T>;
+
+ static constexpr bool supported = true;
+ static constexpr bool featured = traits::featured;
+ static constexpr bool bounded = false;
+ static constexpr bool need_contiguous = traits::need_contiguous;
+
+ static void bound_encode(const boost::optional<T>& v, size_t& p,
+ uint64_t f = 0) {
+ p += sizeof(bool);
+ if (v) {
+ if constexpr (featured) {
+ denc(*v, p, f);
+ } else {
+ denc(*v, p);
+ }
+ }
+ }
+
+ static void encode(const boost::optional<T>& v,
+ ceph::buffer::list::contiguous_appender& p,
+ uint64_t f = 0) {
+ denc((bool)v, p);
+ if (v) {
+ if constexpr (featured) {
+ denc(*v, p, f);
+ } else {
+ denc(*v, p);
+ }
+ }
+ }
+
+ static void decode(boost::optional<T>& v, ceph::buffer::ptr::const_iterator& p,
+ uint64_t f = 0) {
+ bool x;
+ denc(x, p, f);
+ if (x) {
+ v = T{};
+ denc(*v, p, f);
+ } else {
+ v = boost::none;
+ }
+ }
+
+ template<typename U = T>
+ static std::enable_if_t<!!sizeof(U) && !need_contiguous>
+ decode(boost::optional<T>& v, ceph::buffer::list::const_iterator& p) {
+ bool x;
+ denc(x, p);
+ if (x) {
+ v = T{};
+ denc(*v, p);
+ } else {
+ v = boost::none;
+ }
+ }
+
+ template<typename U = T>
+ static void encode_nohead(const boost::optional<T>& v,
+ ceph::buffer::list::contiguous_appender& p,
+ uint64_t f = 0) {
+ if (v) {
+ if constexpr (featured) {
+ denc(*v, p, f);
+ } else {
+ denc(*v, p);
+ }
+ }
+ }
+
+ static void decode_nohead(bool num, boost::optional<T>& v,
+ ceph::buffer::ptr::const_iterator& p, uint64_t f = 0) {
+ if (num) {
+ v = T();
+ denc(*v, p, f);
+ } else {
+ v = boost::none;
+ }
+ }
+};
+
+template<>
+struct denc_traits<boost::none_t> {
+ static constexpr bool supported = true;
+ static constexpr bool featured = false;
+ static constexpr bool bounded = true;
+ static constexpr bool need_contiguous = false;
+
+ static void bound_encode(const boost::none_t& v, size_t& p) {
+ p += sizeof(bool);
+ }
+
+ static void encode(const boost::none_t& v,
+ ceph::buffer::list::contiguous_appender& p) {
+ denc(false, p);
+ }
+};
+
+//
+// std::optional<T>
+//
+template<typename T>
+struct denc_traits<
+ std::optional<T>,
+ std::enable_if_t<denc_traits<T>::supported>> {
+ using traits = denc_traits<T>;
+
+ static constexpr bool supported = true;
+ static constexpr bool featured = traits::featured;
+ static constexpr bool bounded = false;
+ static constexpr bool need_contiguous = traits::need_contiguous;
+
+ static void bound_encode(const std::optional<T>& v, size_t& p,
+ uint64_t f = 0) {
+ p += sizeof(bool);
+ if (v) {
+ if constexpr (featured) {
+ denc(*v, p, f);
+ } else {
+ denc(*v, p);
+ }
+ }
+ }
+
+ static void encode(const std::optional<T>& v,
+ ceph::buffer::list::contiguous_appender& p,
+ uint64_t f = 0) {
+ denc((bool)v, p);
+ if (v) {
+ if constexpr (featured) {
+ denc(*v, p, f);
+ } else {
+ denc(*v, p);
+ }
+ }
+ }
+
+ static void decode(std::optional<T>& v, ceph::buffer::ptr::const_iterator& p,
+ uint64_t f = 0) {
+ bool x;
+ denc(x, p, f);
+ if (x) {
+ v = T{};
+ denc(*v, p, f);
+ } else {
+ v = std::nullopt;
+ }
+ }
+
+ template<typename U = T>
+ static std::enable_if_t<!!sizeof(U) && !need_contiguous>
+ decode(std::optional<T>& v, ceph::buffer::list::const_iterator& p) {
+ bool x;
+ denc(x, p);
+ if (x) {
+ v = T{};
+ denc(*v, p);
+ } else {
+ v = std::nullopt;
+ }
+ }
+
+ static void encode_nohead(const std::optional<T>& v,
+ ceph::buffer::list::contiguous_appender& p,
+ uint64_t f = 0) {
+ if (v) {
+ if constexpr (featured) {
+ denc(*v, p, f);
+ } else {
+ denc(*v, p);
+ }
+ }
+ }
+
+ static void decode_nohead(bool num, std::optional<T>& v,
+ ceph::buffer::ptr::const_iterator& p, uint64_t f = 0) {
+ if (num) {
+ v = T();
+ denc(*v, p, f);
+ } else {
+ v = std::nullopt;
+ }
+ }
+};
+
+template<>
+struct denc_traits<std::nullopt_t> {
+ static constexpr bool supported = true;
+ static constexpr bool featured = false;
+ static constexpr bool bounded = true;
+ static constexpr bool need_contiguous = false;
+
+ static void bound_encode(const std::nullopt_t& v, size_t& p) {
+ p += sizeof(bool);
+ }
+
+ static void encode(const std::nullopt_t& v,
+ ceph::buffer::list::contiguous_appender& p) {
+ denc(false, p);
+ }
+};
+
+// ----------------------------------------------------------------------
+// class helpers
+
+// Write denc_traits<> for a class that defines bound_encode/encode/decode
+// methods.
+
+#define WRITE_CLASS_DENC(T) _DECLARE_CLASS_DENC(T, false)
+#define WRITE_CLASS_DENC_BOUNDED(T) _DECLARE_CLASS_DENC(T, true)
+#define _DECLARE_CLASS_DENC(T, b) \
+ template<> struct denc_traits<T> { \
+ static constexpr bool supported = true; \
+ static constexpr bool featured = false; \
+ static constexpr bool bounded = b; \
+ static constexpr bool need_contiguous = !_denc::has_legacy_denc<T>::value;\
+ static void bound_encode(const T& v, size_t& p, uint64_t f=0) { \
+ v.bound_encode(p); \
+ } \
+ static void encode(const T& v, ::ceph::buffer::list::contiguous_appender& p, \
+ uint64_t f=0) { \
+ v.encode(p); \
+ } \
+ static void decode(T& v, ::ceph::buffer::ptr::const_iterator& p, uint64_t f=0) { \
+ v.decode(p); \
+ } \
+ };
+
+#define WRITE_CLASS_DENC_FEATURED(T) _DECLARE_CLASS_DENC_FEATURED(T, false)
+#define WRITE_CLASS_DENC_FEATURED_BOUNDED(T) _DECLARE_CLASS_DENC_FEATURED(T, true)
+#define _DECLARE_CLASS_DENC_FEATURED(T, b) \
+ template<> struct denc_traits<T> { \
+ static constexpr bool supported = true; \
+ static constexpr bool featured = true; \
+ static constexpr bool bounded = b; \
+ static constexpr bool need_contiguous = !_denc::has_legacy_denc<T>::value;\
+ static void bound_encode(const T& v, size_t& p, uint64_t f) { \
+ v.bound_encode(p, f); \
+ } \
+ static void encode(const T& v, ::ceph::buffer::list::contiguous_appender& p, \
+ uint64_t f) { \
+ v.encode(p, f); \
+ } \
+ static void decode(T& v, ::ceph::buffer::ptr::const_iterator& p, uint64_t f=0) { \
+ v.decode(p, f); \
+ } \
+ };
+
+// ----------------------------------------------------------------------
+// encoded_sizeof_wrapper
+
+namespace ceph {
+
+template <typename T, typename traits=denc_traits<T>>
+constexpr std::enable_if_t<traits::supported && traits::bounded, size_t>
+encoded_sizeof_bounded() {
+ size_t p = 0;
+ traits::bound_encode(T(), p);
+ return p;
+}
+
+template <typename T, typename traits=denc_traits<T>>
+std::enable_if_t<traits::supported, size_t>
+encoded_sizeof(const T &t) {
+ size_t p = 0;
+ traits::bound_encode(t, p);
+ return p;
+}
+
+} // namespace ceph
+
+
+// ----------------------------------------------------------------------
+// encode/decode wrappers
+
+// These glue the new-style denc world into old-style calls to encode
+// and decode by calling into denc_traits<> methods (when present).
+
+namespace ceph {
+template<typename T, typename traits=denc_traits<T>>
+inline std::enable_if_t<traits::supported && !traits::featured> encode(
+ const T& o,
+ ceph::buffer::list& bl,
+ uint64_t features_unused=0)
+{
+ size_t len = 0;
+ traits::bound_encode(o, len);
+ auto a = bl.get_contiguous_appender(len);
+ traits::encode(o, a);
+}
+
+template<typename T, typename traits=denc_traits<T>>
+inline std::enable_if_t<traits::supported && traits::featured> encode(
+ const T& o, ::ceph::buffer::list& bl,
+ uint64_t features)
+{
+ size_t len = 0;
+ traits::bound_encode(o, len, features);
+ auto a = bl.get_contiguous_appender(len);
+ traits::encode(o, a, features);
+}
+
+template<typename T,
+ typename traits=denc_traits<T>>
+inline std::enable_if_t<traits::supported && !traits::need_contiguous> decode(
+ T& o,
+ ::ceph::buffer::list::const_iterator& p)
+{
+ if (p.end())
+ throw ::ceph::buffer::end_of_buffer();
+ const auto& bl = p.get_bl();
+ const auto remaining = bl.length() - p.get_off();
+ // it is expensive to rebuild a contigous buffer and drop it, so avoid this.
+ if (!p.is_pointing_same_raw(bl.back()) && remaining > CEPH_PAGE_SIZE) {
+ traits::decode(o, p);
+ } else {
+ // ensure we get a contigous buffer... until the end of the
+ // ceph::buffer::list. we don't really know how much we'll need here,
+ // unfortunately. hopefully it is already contiguous and we're just
+ // bumping the raw ref and initializing the ptr tmp fields.
+ ceph::buffer::ptr tmp;
+ auto t = p;
+ t.copy_shallow(remaining, tmp);
+ auto cp = std::cbegin(tmp);
+ traits::decode(o, cp);
+ p += cp.get_offset();
+ }
+}
+
+template<typename T,
+ typename traits=denc_traits<T>>
+inline std::enable_if_t<traits::supported && traits::need_contiguous> decode(
+ T& o,
+ ceph::buffer::list::const_iterator& p)
+{
+ if (p.end())
+ throw ceph::buffer::end_of_buffer();
+ // ensure we get a contigous buffer... until the end of the
+ // ceph::buffer::list. we don't really know how much we'll need here,
+ // unfortunately. hopefully it is already contiguous and we're just
+ // bumping the raw ref and initializing the ptr tmp fields.
+ ceph::buffer::ptr tmp;
+ auto t = p;
+ t.copy_shallow(p.get_bl().length() - p.get_off(), tmp);
+ auto cp = std::cbegin(tmp);
+ traits::decode(o, cp);
+ p += cp.get_offset();
+}
+
+// nohead variants
+template<typename T, typename traits=denc_traits<T>>
+inline std::enable_if_t<traits::supported &&
+ !traits::featured> encode_nohead(
+ const T& o,
+ ceph::buffer::list& bl)
+{
+ size_t len = 0;
+ traits::bound_encode(o, len);
+ auto a = bl.get_contiguous_appender(len);
+ traits::encode_nohead(o, a);
+}
+
+template<typename T, typename traits=denc_traits<T>>
+inline std::enable_if_t<traits::supported && !traits::featured> decode_nohead(
+ size_t num,
+ T& o,
+ ceph::buffer::list::const_iterator& p)
+{
+ if (!num)
+ return;
+ if (p.end())
+ throw ceph::buffer::end_of_buffer();
+ if constexpr (traits::need_contiguous) {
+ ceph::buffer::ptr tmp;
+ auto t = p;
+ if constexpr (denc_traits<typename T::value_type>::bounded) {
+ size_t element_size = 0;
+ typename T::value_type v;
+ denc_traits<typename T::value_type>::bound_encode(v, element_size);
+ t.copy_shallow(num * element_size, tmp);
+ } else {
+ t.copy_shallow(p.get_bl().length() - p.get_off(), tmp);
+ }
+ auto cp = std::cbegin(tmp);
+ traits::decode_nohead(num, o, cp);
+ p += cp.get_offset();
+ } else {
+ traits::decode_nohead(num, o, p);
+ }
+}
+}
+
+
+// ----------------------------------------------------------------
+// DENC
+
+// These are some class methods we need to do the version and length
+// wrappers for DENC_{START,FINISH} for inter-version
+// interoperability.
+
+#define DENC_HELPERS \
+ /* bound_encode */ \
+ static void _denc_start(size_t& p, \
+ __u8 *struct_v, \
+ __u8 *struct_compat, \
+ char **, uint32_t *) { \
+ p += 2 + 4; \
+ } \
+ static void _denc_finish(size_t& p, \
+ __u8 *struct_v, \
+ __u8 *struct_compat, \
+ char **, uint32_t *) { } \
+ /* encode */ \
+ static void _denc_start(::ceph::buffer::list::contiguous_appender& p, \
+ __u8 *struct_v, \
+ __u8 *struct_compat, \
+ char **len_pos, \
+ uint32_t *start_oob_off) { \
+ denc(*struct_v, p); \
+ denc(*struct_compat, p); \
+ *len_pos = p.get_pos_add(4); \
+ *start_oob_off = p.get_out_of_band_offset(); \
+ } \
+ static void _denc_finish(::ceph::buffer::list::contiguous_appender& p, \
+ __u8 *struct_v, \
+ __u8 *struct_compat, \
+ char **len_pos, \
+ uint32_t *start_oob_off) { \
+ *(ceph_le32*)*len_pos = p.get_pos() - *len_pos - sizeof(uint32_t) + \
+ p.get_out_of_band_offset() - *start_oob_off; \
+ } \
+ /* decode */ \
+ static void _denc_start(::ceph::buffer::ptr::const_iterator& p, \
+ __u8 *struct_v, \
+ __u8 *struct_compat, \
+ char **start_pos, \
+ uint32_t *struct_len) { \
+ denc(*struct_v, p); \
+ denc(*struct_compat, p); \
+ denc(*struct_len, p); \
+ *start_pos = const_cast<char*>(p.get_pos()); \
+ } \
+ static void _denc_finish(::ceph::buffer::ptr::const_iterator& p, \
+ __u8 *struct_v, __u8 *struct_compat, \
+ char **start_pos, \
+ uint32_t *struct_len) { \
+ const char *pos = p.get_pos(); \
+ char *end = *start_pos + *struct_len; \
+ if (pos > end) { \
+ throw ::ceph::buffer::malformed_input(__PRETTY_FUNCTION__); \
+ } \
+ if (pos < end) { \
+ p += end - pos; \
+ } \
+ }
+
+// Helpers for versioning the encoding. These correspond to the
+// {ENCODE,DECODE}_{START,FINISH} macros.
+
+#define DENC_START(v, compat, p) \
+ __u8 struct_v = v; \
+ __u8 struct_compat = compat; \
+ char *_denc_pchar; \
+ uint32_t _denc_u32; \
+ _denc_start(p, &struct_v, &struct_compat, &_denc_pchar, &_denc_u32); \
+ do {
+
+#define DENC_FINISH(p) \
+ } while (false); \
+ _denc_finish(p, &struct_v, &struct_compat, &_denc_pchar, &_denc_u32);
+
+
+// ----------------------------------------------------------------------
+
+// Helpers for writing a unified bound_encode/encode/decode
+// implementation that won't screw up buffer size estimations.
+
+#define DENC(Type, v, p) \
+ DENC_HELPERS \
+ void bound_encode(size_t& p) const { \
+ _denc_friend(*this, p); \
+ } \
+ void encode(::ceph::buffer::list::contiguous_appender& p) const { \
+ DENC_DUMP_PRE(Type); \
+ _denc_friend(*this, p); \
+ } \
+ void decode(::ceph::buffer::ptr::const_iterator& p) { \
+ _denc_friend(*this, p); \
+ } \
+ template<typename T, typename P> \
+ friend std::enable_if_t<std::is_same_v<T, Type> || \
+ std::is_same_v<T, const Type>> \
+ _denc_friend(T& v, P& p)
+
+#define DENC_FEATURED(Type, v, p, f) \
+ DENC_HELPERS \
+ void bound_encode(size_t& p, uint64_t f) const { \
+ _denc_friend(*this, p, f); \
+ } \
+ void encode(::ceph::buffer::list::contiguous_appender& p, uint64_t f) const { \
+ DENC_DUMP_PRE(Type); \
+ _denc_friend(*this, p, f); \
+ } \
+ void decode(::ceph::buffer::ptr::const_iterator& p, uint64_t f=0) { \
+ _denc_friend(*this, p, f); \
+ } \
+ template<typename T, typename P> \
+ friend std::enable_if_t<std::is_same_v<T, Type> || \
+ std::is_same_v<T, const Type>> \
+ _denc_friend(T& v, P& p, uint64_t f)
+
+#endif
diff --git a/src/include/dlfcn_compat.h b/src/include/dlfcn_compat.h
new file mode 100644
index 000000000..95fd64e51
--- /dev/null
+++ b/src/include/dlfcn_compat.h
@@ -0,0 +1,48 @@
+// -*- 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) 2020 SUSE LINUX GmbH
+ *
+ * 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 DLFCN_COMPAT_H
+#define DLFCN_COMPAT_H
+
+#include "acconfig.h"
+
+#define SHARED_LIB_SUFFIX CMAKE_SHARED_LIBRARY_SUFFIX
+
+#ifdef _WIN32
+ #include <string>
+
+ using dl_errmsg_t = std::string;
+
+ // The load mode flags will be ignored on Windows. We keep the same
+ // values for debugging purposes though.
+ #define RTLD_LAZY 0x00001
+ #define RTLD_NOW 0x00002
+ #define RTLD_BINDING_MASK 0x3
+ #define RTLD_NOLOAD 0x00004
+ #define RTLD_DEEPBIND 0x00008
+ #define RTLD_GLOBAL 0x00100
+ #define RTLD_LOCAL 0
+ #define RTLD_NODELETE 0x01000
+
+ void* dlopen(const char *filename, int flags);
+ int dlclose(void* handle);
+ dl_errmsg_t dlerror();
+ void* dlsym(void* handle, const char* symbol);
+#else
+ #include <dlfcn.h>
+
+ using dl_errmsg_t = char*;
+#endif /* _WIN32 */
+
+#endif /* DLFCN_H */
diff --git a/src/include/elist.h b/src/include/elist.h
new file mode 100644
index 000000000..38be35dbf
--- /dev/null
+++ b/src/include/elist.h
@@ -0,0 +1,193 @@
+// -*- 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-2006 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_ELIST_H
+#define CEPH_ELIST_H
+
+/*
+ * elist: embedded list.
+ *
+ * requirements:
+ * - elist<T>::item be embedded in the parent class
+ * - items are _always_ added to the list via the same elist<T>::item at the same
+ * fixed offset in the class.
+ * - begin(), front(), back() methods take the member offset as an argument for traversal.
+ *
+ */
+
+#define member_offset(cls, member) ((size_t)(&((cls*)1)->member) - 1)
+
+template<typename T>
+class elist {
+public:
+ struct item {
+ item *_prev, *_next;
+
+ item(T i=0) : _prev(this), _next(this) {}
+ ~item() {
+ ceph_assert(!is_on_list());
+ }
+
+ item(const item& other) = delete;
+ const item& operator= (const item& right) = delete;
+
+
+ bool empty() const { return _prev == this; }
+ bool is_on_list() const { return !empty(); }
+
+ bool remove_myself() {
+ if (_next == this) {
+ ceph_assert(_prev == this);
+ return false;
+ }
+ _next->_prev = _prev;
+ _prev->_next = _next;
+ _prev = _next = this;
+ return true;
+ }
+
+ void insert_after(item *other) {
+ ceph_assert(other->empty());
+ other->_prev = this;
+ other->_next = _next;
+ _next->_prev = other;
+ _next = other;
+ }
+ void insert_before(item *other) {
+ ceph_assert(other->empty());
+ other->_next = this;
+ other->_prev = _prev;
+ _prev->_next = other;
+ _prev = other;
+ }
+
+ T get_item(size_t offset) {
+ ceph_assert(offset);
+ return (T)(((char *)this) - offset);
+ }
+ };
+
+private:
+ item _head;
+ size_t item_offset;
+
+public:
+ elist(const elist& other);
+ const elist& operator=(const elist& other);
+
+ elist(size_t o) : _head(NULL), item_offset(o) {}
+ ~elist() {
+ ceph_assert(_head.empty());
+ }
+
+ bool empty() const {
+ return _head.empty();
+ }
+
+ void clear() {
+ while (!_head.empty())
+ pop_front();
+ }
+
+ void push_front(item *i) {
+ if (!i->empty())
+ i->remove_myself();
+ _head.insert_after(i);
+ }
+ void push_back(item *i) {
+ if (!i->empty())
+ i->remove_myself();
+ _head.insert_before(i);
+ }
+
+ T front(size_t o=0) {
+ ceph_assert(!_head.empty());
+ return _head._next->get_item(o ? o : item_offset);
+ }
+ T back(size_t o=0) {
+ ceph_assert(!_head.empty());
+ return _head._prev->get_item(o ? o : item_offset);
+ }
+
+ void pop_front() {
+ ceph_assert(!empty());
+ _head._next->remove_myself();
+ }
+ void pop_back() {
+ ceph_assert(!empty());
+ _head._prev->remove_myself();
+ }
+
+ void clear_list() {
+ while (!empty())
+ pop_front();
+ }
+
+ enum mode_t {
+ MAGIC, CURRENT, CACHE_NEXT
+ };
+
+ class iterator {
+ private:
+ item *head;
+ item *cur, *next;
+ size_t item_offset;
+ mode_t mode;
+ public:
+ iterator(item *h, size_t o, mode_t m) :
+ head(h), cur(h->_next), next(cur->_next), item_offset(o),
+ mode(m) {
+ ceph_assert(item_offset > 0);
+ }
+ T operator*() {
+ return cur->get_item(item_offset);
+ }
+ iterator& operator++() {
+ ceph_assert(cur);
+ ceph_assert(cur != head);
+ if (mode == MAGIC) {
+ // if 'cur' appears to be valid, use that. otherwise,
+ // use cached 'next'.
+ // this is a bit magic, and probably a bad idea... :/
+ if (cur->empty())
+ cur = next;
+ else
+ cur = cur->_next;
+ } else if (mode == CURRENT)
+ cur = cur->_next;
+ else if (mode == CACHE_NEXT)
+ cur = next;
+ else
+ ceph_abort();
+ next = cur->_next;
+ return *this;
+ }
+ bool end() const {
+ return cur == head;
+ }
+ };
+
+ iterator begin(size_t o=0) {
+ return iterator(&_head, o ? o : item_offset, MAGIC);
+ }
+ iterator begin_use_current(size_t o=0) {
+ return iterator(&_head, o ? o : item_offset, CURRENT);
+ }
+ iterator begin_cache_next(size_t o=0) {
+ return iterator(&_head, o ? o : item_offset, CACHE_NEXT);
+ }
+};
+
+
+#endif
diff --git a/src/include/encoding.h b/src/include/encoding.h
new file mode 100644
index 000000000..40ba9d39c
--- /dev/null
+++ b/src/include/encoding.h
@@ -0,0 +1,1548 @@
+// -*- 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-2006 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_ENCODING_H
+#define CEPH_ENCODING_H
+
+#include <set>
+#include <map>
+#include <deque>
+#include <vector>
+#include <string>
+#include <string_view>
+#include <tuple>
+#include <optional>
+#include <boost/container/small_vector.hpp>
+#include <boost/optional/optional_io.hpp>
+#include <boost/tuple/tuple.hpp>
+
+#include "include/unordered_map.h"
+#include "include/unordered_set.h"
+#include "common/ceph_time.h"
+
+#include "include/int_types.h"
+
+#include "common/convenience.h"
+
+#include "byteorder.h"
+#include "buffer.h"
+
+// pull in the new-style encoding so that we get the denc_traits<> definition.
+#include "denc.h"
+
+#include "assert.h"
+
+using namespace ceph;
+
+namespace ceph {
+
+/*
+ * Notes on feature encoding:
+ *
+ * - The default encode() methods have a features argument with a default parameter
+ * (which goes to zero).
+ * - Normal classes will use WRITE_CLASS_ENCODER, with that features=0 default.
+ * - Classes that _require_ features will use WRITE_CLASS_ENCODER_FEATURES, which
+ * does not define the default. Any caller must explicitly pass it in.
+ * - STL container macros have two encode variants: one with a features arg, and one
+ * without.
+ *
+ * The result:
+ * - A feature encode() method will fail to compile if a value is not
+ * passed in.
+ * - The feature varianet of the STL templates will be used when the feature arg is
+ * provided. It will be passed through to any template arg types, but it will be
+ * ignored when not needed.
+ */
+
+// --------------------------------------
+// base types
+
+template<class T>
+inline void encode_raw(const T& t, bufferlist& bl)
+{
+ bl.append((char*)&t, sizeof(t));
+}
+template<class T>
+inline void decode_raw(T& t, bufferlist::const_iterator &p)
+{
+ p.copy(sizeof(t), (char*)&t);
+}
+
+#define WRITE_RAW_ENCODER(type) \
+ inline void encode(const type &v, ::ceph::bufferlist& bl, uint64_t features=0) { ::ceph::encode_raw(v, bl); } \
+ inline void decode(type &v, ::ceph::bufferlist::const_iterator& p) { ::ceph::decode_raw(v, p); }
+
+WRITE_RAW_ENCODER(__u8)
+#ifndef _CHAR_IS_SIGNED
+WRITE_RAW_ENCODER(__s8)
+#endif
+WRITE_RAW_ENCODER(char)
+WRITE_RAW_ENCODER(ceph_le64)
+WRITE_RAW_ENCODER(ceph_le32)
+WRITE_RAW_ENCODER(ceph_le16)
+
+inline void encode(const bool &v, bufferlist& bl) {
+ __u8 vv = v;
+ encode_raw(vv, bl);
+}
+inline void decode(bool &v, bufferlist::const_iterator& p) {
+ __u8 vv;
+ decode_raw(vv, p);
+ v = vv;
+}
+
+
+// -----------------------------------
+// int types
+
+#define WRITE_INTTYPE_ENCODER(type, etype) \
+ inline void encode(type v, ::ceph::bufferlist& bl, uint64_t features=0) { \
+ ceph_##etype e; \
+ e = v; \
+ ::ceph::encode_raw(e, bl); \
+ } \
+ inline void decode(type &v, ::ceph::bufferlist::const_iterator& p) { \
+ ceph_##etype e; \
+ ::ceph::decode_raw(e, p); \
+ v = e; \
+ }
+
+WRITE_INTTYPE_ENCODER(uint64_t, le64)
+WRITE_INTTYPE_ENCODER(int64_t, le64)
+WRITE_INTTYPE_ENCODER(uint32_t, le32)
+WRITE_INTTYPE_ENCODER(int32_t, le32)
+WRITE_INTTYPE_ENCODER(uint16_t, le16)
+WRITE_INTTYPE_ENCODER(int16_t, le16)
+
+// -----------------------------------
+// float types
+//
+// NOTE: The following code assumes all supported platforms use IEEE binary32
+// as float and IEEE binary64 as double floating-point format. The assumption
+// is verified by the assertions below.
+//
+// Under this assumption, we can use raw encoding of floating-point types
+// on little-endian machines, but we still need to perform a byte swap
+// on big-endian machines to ensure cross-architecture compatibility.
+// To achive that, we reinterpret the values as integers first, which are
+// byte-swapped via the ceph_le types as above. The extra conversions
+// are optimized away on little-endian machines by the compiler.
+#define WRITE_FLTTYPE_ENCODER(type, itype, etype) \
+ static_assert(sizeof(type) == sizeof(itype)); \
+ static_assert(std::numeric_limits<type>::is_iec559, \
+ "floating-point type not using IEEE754 format"); \
+ inline void encode(type v, ::ceph::bufferlist& bl, uint64_t features=0) { \
+ ceph_##etype e; \
+ e = *reinterpret_cast<itype *>(&v); \
+ ::ceph::encode_raw(e, bl); \
+ } \
+ inline void decode(type &v, ::ceph::bufferlist::const_iterator& p) { \
+ ceph_##etype e; \
+ ::ceph::decode_raw(e, p); \
+ *reinterpret_cast<itype *>(&v) = e; \
+ }
+
+WRITE_FLTTYPE_ENCODER(float, uint32_t, le32)
+WRITE_FLTTYPE_ENCODER(double, uint64_t, le64)
+
+// see denc.h for ENCODE_DUMP_PATH discussion and definition.
+#ifdef ENCODE_DUMP_PATH
+# define ENCODE_DUMP_PRE() \
+ unsigned pre_off = bl.length()
+# define ENCODE_DUMP_POST(cl) \
+ do { \
+ static int i = 0; \
+ i++; \
+ int bits = 0; \
+ for (unsigned t = i; t; bits++) \
+ t &= t - 1; \
+ if (bits > 2) \
+ break; \
+ char fn[PATH_MAX]; \
+ snprintf(fn, sizeof(fn), ENCODE_STRINGIFY(ENCODE_DUMP_PATH) "/%s__%d.%x", #cl, getpid(), i++); \
+ int fd = ::open(fn, O_WRONLY|O_TRUNC|O_CREAT|O_CLOEXEC|O_BINARY, 0644); \
+ if (fd >= 0) { \
+ ::ceph::bufferlist sub; \
+ sub.substr_of(bl, pre_off, bl.length() - pre_off); \
+ sub.write_fd(fd); \
+ ::close(fd); \
+ } \
+ } while (0)
+#else
+# define ENCODE_DUMP_PRE()
+# define ENCODE_DUMP_POST(cl)
+#endif
+
+
+#define WRITE_CLASS_ENCODER(cl) \
+ inline void encode(const cl& c, ::ceph::buffer::list &bl, uint64_t features=0) { \
+ ENCODE_DUMP_PRE(); c.encode(bl); ENCODE_DUMP_POST(cl); } \
+ inline void decode(cl &c, ::ceph::bufferlist::const_iterator &p) { c.decode(p); }
+
+#define WRITE_CLASS_MEMBER_ENCODER(cl) \
+ inline void encode(const cl &c, ::ceph::bufferlist &bl) const { \
+ ENCODE_DUMP_PRE(); c.encode(bl); ENCODE_DUMP_POST(cl); } \
+ inline void decode(cl &c, ::ceph::bufferlist::const_iterator &p) { c.decode(p); }
+
+#define WRITE_CLASS_ENCODER_FEATURES(cl) \
+ inline void encode(const cl &c, ::ceph::bufferlist &bl, uint64_t features) { \
+ ENCODE_DUMP_PRE(); c.encode(bl, features); ENCODE_DUMP_POST(cl); } \
+ inline void decode(cl &c, ::ceph::bufferlist::const_iterator &p) { c.decode(p); }
+
+#define WRITE_CLASS_ENCODER_OPTIONAL_FEATURES(cl) \
+ inline void encode(const cl &c, ::ceph::bufferlist &bl, uint64_t features = 0) { \
+ ENCODE_DUMP_PRE(); c.encode(bl, features); ENCODE_DUMP_POST(cl); } \
+ inline void decode(cl &c, ::ceph::bufferlist::const_iterator &p) { c.decode(p); }
+
+
+// string
+inline void encode(std::string_view s, bufferlist& bl, uint64_t features=0)
+{
+ __u32 len = s.length();
+ encode(len, bl);
+ if (len)
+ bl.append(s.data(), len);
+}
+inline void encode(const std::string& s, bufferlist& bl, uint64_t features=0)
+{
+ return encode(std::string_view(s), bl, features);
+}
+inline void decode(std::string& s, bufferlist::const_iterator& p)
+{
+ __u32 len;
+ decode(len, p);
+ s.clear();
+ p.copy(len, s);
+}
+
+inline void encode_nohead(std::string_view s, bufferlist& bl)
+{
+ bl.append(s.data(), s.length());
+}
+inline void encode_nohead(const std::string& s, bufferlist& bl)
+{
+ encode_nohead(std::string_view(s), bl);
+}
+inline void decode_nohead(int len, std::string& s, bufferlist::const_iterator& p)
+{
+ s.clear();
+ p.copy(len, s);
+}
+
+// const char* (encode only, string compatible)
+inline void encode(const char *s, bufferlist& bl)
+{
+ encode(std::string_view(s, strlen(s)), bl);
+}
+
+// opaque byte vectors
+inline void encode(std::vector<uint8_t>& v, bufferlist& bl)
+{
+ uint32_t len = v.size();
+ encode(len, bl);
+ if (len)
+ bl.append((char *)v.data(), len);
+}
+
+inline void decode(std::vector<uint8_t>& v, bufferlist::const_iterator& p)
+{
+ uint32_t len;
+
+ decode(len, p);
+ v.resize(len);
+ p.copy(len, (char *)v.data());
+}
+
+// -----------------------------
+// buffers
+
+// bufferptr (encapsulated)
+inline void encode(const buffer::ptr& bp, bufferlist& bl)
+{
+ __u32 len = bp.length();
+ encode(len, bl);
+ if (len)
+ bl.append(bp);
+}
+inline void decode(buffer::ptr& bp, bufferlist::const_iterator& p)
+{
+ __u32 len;
+ decode(len, p);
+
+ bufferlist s;
+ p.copy(len, s);
+
+ if (len) {
+ if (s.get_num_buffers() == 1)
+ bp = s.front();
+ else
+ bp = buffer::copy(s.c_str(), s.length());
+ }
+}
+
+// bufferlist (encapsulated)
+inline void encode(const bufferlist& s, bufferlist& bl)
+{
+ __u32 len = s.length();
+ encode(len, bl);
+ bl.append(s);
+}
+inline void encode_destructively(bufferlist& s, bufferlist& bl)
+{
+ __u32 len = s.length();
+ encode(len, bl);
+ bl.claim_append(s);
+}
+inline void decode(bufferlist& s, bufferlist::const_iterator& p)
+{
+ __u32 len;
+ decode(len, p);
+ s.clear();
+ p.copy(len, s);
+}
+
+inline void encode_nohead(const bufferlist& s, bufferlist& bl)
+{
+ bl.append(s);
+}
+inline void decode_nohead(int len, bufferlist& s, bufferlist::const_iterator& p)
+{
+ s.clear();
+ p.copy(len, s);
+}
+
+// Time, since the templates are defined in std::chrono
+
+template<typename Clock, typename Duration,
+ typename std::enable_if_t<converts_to_timespec_v<Clock>>* = nullptr>
+void encode(const std::chrono::time_point<Clock, Duration>& t,
+ ceph::bufferlist &bl) {
+ auto ts = Clock::to_timespec(t);
+ // A 32 bit count of seconds causes me vast unhappiness.
+ uint32_t s = ts.tv_sec;
+ uint32_t ns = ts.tv_nsec;
+ encode(s, bl);
+ encode(ns, bl);
+}
+
+template<typename Clock, typename Duration,
+ typename std::enable_if_t<converts_to_timespec_v<Clock>>* = nullptr>
+void decode(std::chrono::time_point<Clock, Duration>& t,
+ bufferlist::const_iterator& p) {
+ uint32_t s;
+ uint32_t ns;
+ decode(s, p);
+ decode(ns, p);
+ struct timespec ts = {
+ static_cast<time_t>(s),
+ static_cast<long int>(ns)};
+
+ t = Clock::from_timespec(ts);
+}
+
+template<typename Rep, typename Period,
+ typename std::enable_if_t<std::is_integral_v<Rep>>* = nullptr>
+void encode(const std::chrono::duration<Rep, Period>& d,
+ ceph::bufferlist &bl) {
+ using namespace std::chrono;
+ int32_t s = duration_cast<seconds>(d).count();
+ int32_t ns = (duration_cast<nanoseconds>(d) % seconds(1)).count();
+ encode(s, bl);
+ encode(ns, bl);
+}
+
+template<typename Rep, typename Period,
+ typename std::enable_if_t<std::is_integral_v<Rep>>* = nullptr>
+void decode(std::chrono::duration<Rep, Period>& d,
+ bufferlist::const_iterator& p) {
+ int32_t s;
+ int32_t ns;
+ decode(s, p);
+ decode(ns, p);
+ d = std::chrono::seconds(s) + std::chrono::nanoseconds(ns);
+}
+
+// -----------------------------
+// STL container types
+
+template<typename T>
+inline void encode(const boost::optional<T> &p, bufferlist &bl);
+template<typename T>
+inline void decode(boost::optional<T> &p, bufferlist::const_iterator &bp);
+template<typename T>
+inline void encode(const std::optional<T> &p, bufferlist &bl);
+template<typename T>
+inline void decode(std::optional<T> &p, bufferlist::const_iterator &bp);
+template<class A, class B, class C>
+inline void encode(const boost::tuple<A, B, C> &t, bufferlist& bl);
+template<class A, class B, class C>
+inline void decode(boost::tuple<A, B, C> &t, bufferlist::const_iterator &bp);
+template<class A, class B,
+ typename a_traits=denc_traits<A>, typename b_traits=denc_traits<B>>
+inline std::enable_if_t<!a_traits::supported || !b_traits::supported>
+encode(const std::pair<A,B> &p, bufferlist &bl, uint64_t features);
+template<class A, class B,
+ typename a_traits=denc_traits<A>, typename b_traits=denc_traits<B>>
+inline std::enable_if_t<!a_traits::supported ||
+ !b_traits::supported>
+encode(const std::pair<A,B> &p, bufferlist &bl);
+template<class A, class B,
+ typename a_traits=denc_traits<A>, typename b_traits=denc_traits<B>>
+inline std::enable_if_t<!a_traits::supported ||
+ !b_traits::supported>
+decode(std::pair<A,B> &pa, bufferlist::const_iterator &p);
+template<class T, class Alloc, typename traits=denc_traits<T>>
+inline std::enable_if_t<!traits::supported>
+encode(const std::list<T, Alloc>& ls, bufferlist& bl);
+template<class T, class Alloc, typename traits=denc_traits<T>>
+inline std::enable_if_t<!traits::supported>
+encode(const std::list<T,Alloc>& ls, bufferlist& bl, uint64_t features);
+template<class T, class Alloc, typename traits=denc_traits<T>>
+inline std::enable_if_t<!traits::supported>
+decode(std::list<T,Alloc>& ls, bufferlist::const_iterator& p);
+template<class T, class Alloc>
+inline void encode(const std::list<std::shared_ptr<T>, Alloc>& ls,
+ bufferlist& bl);
+template<class T, class Alloc>
+inline void encode(const std::list<std::shared_ptr<T>, Alloc>& ls,
+ bufferlist& bl, uint64_t features);
+template<class T, class Alloc>
+inline void decode(std::list<std::shared_ptr<T>, Alloc>& ls,
+ bufferlist::const_iterator& p);
+template<class T, class Comp, class Alloc, typename traits=denc_traits<T>>
+inline std::enable_if_t<!traits::supported>
+encode(const std::set<T,Comp,Alloc>& s, bufferlist& bl);
+template<class T, class Comp, class Alloc, typename traits=denc_traits<T>>
+inline std::enable_if_t<!traits::supported>
+decode(std::set<T,Comp,Alloc>& s, bufferlist::const_iterator& p);
+template<class T, class Comp, class Alloc, typename traits=denc_traits<T>>
+inline std::enable_if_t<!traits::supported>
+encode_nohead(const std::set<T,Comp,Alloc>& s, bufferlist& bl);
+template<class T, class Comp, class Alloc, typename traits=denc_traits<T>>
+inline std::enable_if_t<!traits::supported>
+decode_nohead(int len, std::set<T,Comp,Alloc>& s, bufferlist::iterator& p);
+template<class T, class Comp, class Alloc, typename traits=denc_traits<T>>
+inline std::enable_if_t<!traits::supported>
+encode(const boost::container::flat_set<T, Comp, Alloc>& s, bufferlist& bl);
+template<class T, class Comp, class Alloc, typename traits=denc_traits<T>>
+inline std::enable_if_t<!traits::supported>
+decode(boost::container::flat_set<T, Comp, Alloc>& s, bufferlist::const_iterator& p);
+template<class T, class Comp, class Alloc, typename traits=denc_traits<T>>
+inline std::enable_if_t<!traits::supported>
+encode_nohead(const boost::container::flat_set<T, Comp, Alloc>& s,
+ bufferlist& bl);
+template<class T, class Comp, class Alloc, typename traits=denc_traits<T>>
+inline std::enable_if_t<!traits::supported>
+decode_nohead(int len, boost::container::flat_set<T, Comp, Alloc>& s,
+ bufferlist::iterator& p);
+template<class T, class Comp, class Alloc>
+inline void encode(const std::multiset<T,Comp,Alloc>& s, bufferlist& bl);
+template<class T, class Comp, class Alloc>
+inline void decode(std::multiset<T,Comp,Alloc>& s, bufferlist::const_iterator& p);
+template<class T, class Alloc, typename traits=denc_traits<T>>
+inline std::enable_if_t<!traits::supported>
+encode(const std::vector<T,Alloc>& v, bufferlist& bl, uint64_t features);
+template<class T, class Alloc, typename traits=denc_traits<T>>
+inline std::enable_if_t<!traits::supported>
+encode(const std::vector<T,Alloc>& v, bufferlist& bl);
+template<class T, class Alloc, typename traits=denc_traits<T>>
+inline std::enable_if_t<!traits::supported>
+decode(std::vector<T,Alloc>& v, bufferlist::const_iterator& p);
+template<class T, class Alloc, typename traits=denc_traits<T>>
+inline std::enable_if_t<!traits::supported>
+encode_nohead(const std::vector<T,Alloc>& v, bufferlist& bl);
+template<class T, class Alloc, typename traits=denc_traits<T>>
+inline std::enable_if_t<!traits::supported>
+decode_nohead(int len, std::vector<T,Alloc>& v, bufferlist::const_iterator& p);
+template<class T,class Alloc>
+inline void encode(const std::vector<std::shared_ptr<T>,Alloc>& v,
+ bufferlist& bl,
+ uint64_t features);
+template<class T, class Alloc>
+inline void encode(const std::vector<std::shared_ptr<T>,Alloc>& v,
+ bufferlist& bl);
+template<class T, class Alloc>
+inline void decode(std::vector<std::shared_ptr<T>,Alloc>& v,
+ bufferlist::const_iterator& p);
+// small_vector
+template<class T, std::size_t N, class Alloc, typename traits=denc_traits<T>>
+inline std::enable_if_t<!traits::supported>
+encode(const boost::container::small_vector<T,N,Alloc>& v, bufferlist& bl, uint64_t features);
+template<class T, std::size_t N, class Alloc, typename traits=denc_traits<T>>
+inline std::enable_if_t<!traits::supported>
+encode(const boost::container::small_vector<T,N,Alloc>& v, bufferlist& bl);
+template<class T, std::size_t N, class Alloc, typename traits=denc_traits<T>>
+inline std::enable_if_t<!traits::supported>
+decode(boost::container::small_vector<T,N,Alloc>& v, bufferlist::const_iterator& p);
+template<class T, std::size_t N, class Alloc, typename traits=denc_traits<T>>
+inline std::enable_if_t<!traits::supported>
+encode_nohead(const boost::container::small_vector<T,N,Alloc>& v, bufferlist& bl);
+template<class T, std::size_t N, class Alloc, typename traits=denc_traits<T>>
+inline std::enable_if_t<!traits::supported>
+decode_nohead(int len, boost::container::small_vector<T,N,Alloc>& v, bufferlist::const_iterator& p);
+// std::map
+template<class T, class U, class Comp, class Alloc,
+ typename t_traits=denc_traits<T>, typename u_traits=denc_traits<U>>
+inline std::enable_if_t<!t_traits::supported ||
+ !u_traits::supported>
+encode(const std::map<T,U,Comp,Alloc>& m, bufferlist& bl);
+template<class T, class U, class Comp, class Alloc,
+ typename t_traits=denc_traits<T>, typename u_traits=denc_traits<U>>
+inline std::enable_if_t<!t_traits::supported || !u_traits::supported>
+encode(const std::map<T,U,Comp,Alloc>& m, bufferlist& bl, uint64_t features);
+template<class T, class U, class Comp, class Alloc,
+ typename t_traits=denc_traits<T>, typename u_traits=denc_traits<U>>
+inline std::enable_if_t<!t_traits::supported || !u_traits::supported>
+decode(std::map<T,U,Comp,Alloc>& m, bufferlist::const_iterator& p);
+template<class T, class U, class Comp, class Alloc>
+inline void decode_noclear(std::map<T,U,Comp,Alloc>& m, bufferlist::const_iterator& p);
+template<class T, class U, class Comp, class Alloc,
+ typename t_traits=denc_traits<T>, typename u_traits=denc_traits<U>>
+inline std::enable_if_t<!t_traits::supported || !u_traits::supported>
+encode_nohead(const std::map<T,U,Comp,Alloc>& m, bufferlist& bl);
+template<class T, class U, class Comp, class Alloc,
+ typename t_traits=denc_traits<T>, typename u_traits=denc_traits<U>>
+inline std::enable_if_t<!t_traits::supported || !u_traits::supported>
+encode_nohead(const std::map<T,U,Comp,Alloc>& m, bufferlist& bl, uint64_t features);
+template<class T, class U, class Comp, class Alloc,
+ typename t_traits=denc_traits<T>, typename u_traits=denc_traits<U>>
+inline std::enable_if_t<!t_traits::supported || !u_traits::supported>
+decode_nohead(int n, std::map<T,U,Comp,Alloc>& m, bufferlist::const_iterator& p);
+template<class T, class U, class Comp, class Alloc,
+ typename t_traits=denc_traits<T>, typename u_traits=denc_traits<U>>
+ inline std::enable_if_t<!t_traits::supported || !u_traits::supported>
+encode(const boost::container::flat_map<T,U,Comp,Alloc>& m, bufferlist& bl);
+template<class T, class U, class Comp, class Alloc,
+ typename t_traits=denc_traits<T>, typename u_traits=denc_traits<U>>
+inline std::enable_if_t<!t_traits::supported || !u_traits::supported>
+encode(const boost::container::flat_map<T,U,Comp,Alloc>& m, bufferlist& bl,
+ uint64_t features);
+template<class T, class U, class Comp, class Alloc,
+ typename t_traits=denc_traits<T>, typename u_traits=denc_traits<U>>
+inline std::enable_if_t<!t_traits::supported || !u_traits::supported>
+decode(boost::container::flat_map<T,U,Comp,Alloc>& m, bufferlist::const_iterator& p);
+template<class T, class U, class Comp, class Alloc>
+inline void decode_noclear(boost::container::flat_map<T,U,Comp,Alloc>& m,
+ bufferlist::const_iterator& p);
+template<class T, class U, class Comp, class Alloc,
+ typename t_traits=denc_traits<T>, typename u_traits=denc_traits<U>>
+inline std::enable_if_t<!t_traits::supported || !u_traits::supported>
+encode_nohead(const boost::container::flat_map<T,U,Comp,Alloc>& m,
+ bufferlist& bl);
+template<class T, class U, class Comp, class Alloc,
+ typename t_traits=denc_traits<T>, typename u_traits=denc_traits<U>>
+inline std::enable_if_t<!t_traits::supported || !u_traits::supported>
+encode_nohead(const boost::container::flat_map<T,U,Comp,Alloc>& m,
+ bufferlist& bl, uint64_t features);
+template<class T, class U, class Comp, class Alloc,
+ typename t_traits=denc_traits<T>, typename u_traits=denc_traits<U>>
+inline std::enable_if_t<!t_traits::supported || !u_traits::supported>
+decode_nohead(int n, boost::container::flat_map<T,U,Comp,Alloc>& m,
+ bufferlist::const_iterator& p);
+template<class T, class U, class Comp, class Alloc>
+inline void encode(const std::multimap<T,U,Comp,Alloc>& m, bufferlist& bl);
+template<class T, class U, class Comp, class Alloc>
+inline void decode(std::multimap<T,U,Comp,Alloc>& m, bufferlist::const_iterator& p);
+template<class T, class U, class Hash, class Pred, class Alloc>
+inline void encode(const unordered_map<T,U,Hash,Pred,Alloc>& m, bufferlist& bl,
+ uint64_t features);
+template<class T, class U, class Hash, class Pred, class Alloc>
+inline void encode(const unordered_map<T,U,Hash,Pred,Alloc>& m, bufferlist& bl);
+template<class T, class U, class Hash, class Pred, class Alloc>
+inline void decode(unordered_map<T,U,Hash,Pred,Alloc>& m, bufferlist::const_iterator& p);
+template<class T, class Hash, class Pred, class Alloc>
+inline void encode(const ceph::unordered_set<T,Hash,Pred,Alloc>& m, bufferlist& bl);
+template<class T, class Hash, class Pred, class Alloc>
+inline void decode(ceph::unordered_set<T,Hash,Pred,Alloc>& m, bufferlist::const_iterator& p);
+template<class T, class Alloc>
+inline void encode(const std::deque<T,Alloc>& ls, bufferlist& bl, uint64_t features);
+template<class T, class Alloc>
+inline void encode(const std::deque<T,Alloc>& ls, bufferlist& bl);
+template<class T, class Alloc>
+inline void decode(std::deque<T,Alloc>& ls, bufferlist::const_iterator& p);
+template<class T, size_t N, typename traits = denc_traits<T>>
+inline std::enable_if_t<!traits::supported>
+encode(const std::array<T, N>& v, bufferlist& bl, uint64_t features);
+template<class T, size_t N, typename traits = denc_traits<T>>
+inline std::enable_if_t<!traits::supported>
+encode(const std::array<T, N>& v, bufferlist& bl);
+template<class T, size_t N, typename traits = denc_traits<T>>
+inline std::enable_if_t<!traits::supported>
+decode(std::array<T, N>& v, bufferlist::const_iterator& p);
+
+// full bl decoder
+template<class T>
+inline void decode(T &o, const bufferlist& bl)
+{
+ auto p = bl.begin();
+ decode(o, p);
+ ceph_assert(p.end());
+}
+
+// boost optional
+template<typename T>
+inline void encode(const boost::optional<T> &p, bufferlist &bl)
+{
+ __u8 present = static_cast<bool>(p);
+ encode(present, bl);
+ if (p)
+ encode(p.get(), bl);
+}
+
+#pragma GCC diagnostic ignored "-Wpragmas"
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wuninitialized"
+template<typename T>
+inline void decode(boost::optional<T> &p, bufferlist::const_iterator &bp)
+{
+ __u8 present;
+ decode(present, bp);
+ if (present) {
+ p = T{};
+ decode(p.get(), bp);
+ } else {
+ p = boost::none;
+ }
+}
+#pragma GCC diagnostic pop
+#pragma GCC diagnostic warning "-Wpragmas"
+
+// std optional
+template<typename T>
+inline void encode(const std::optional<T> &p, bufferlist &bl)
+{
+ __u8 present = static_cast<bool>(p);
+ encode(present, bl);
+ if (p)
+ encode(*p, bl);
+}
+
+#pragma GCC diagnostic ignored "-Wpragmas"
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wuninitialized"
+template<typename T>
+inline void decode(std::optional<T> &p, bufferlist::const_iterator &bp)
+{
+ __u8 present;
+ decode(present, bp);
+ if (present) {
+ p = T{};
+ decode(*p, bp);
+ } else {
+ p = std::nullopt;
+ }
+}
+
+// std::tuple
+template<typename... Ts>
+inline void encode(const std::tuple<Ts...> &t, bufferlist& bl)
+{
+ ceph::for_each(t, [&bl](const auto& e) {
+ encode(e, bl);
+ });
+}
+template<typename... Ts>
+inline void decode(std::tuple<Ts...> &t, bufferlist::const_iterator &bp)
+{
+ ceph::for_each(t, [&bp](auto& e) {
+ decode(e, bp);
+ });
+}
+
+//triple boost::tuple
+template<class A, class B, class C>
+inline void encode(const boost::tuple<A, B, C> &t, bufferlist& bl)
+{
+ encode(boost::get<0>(t), bl);
+ encode(boost::get<1>(t), bl);
+ encode(boost::get<2>(t), bl);
+}
+template<class A, class B, class C>
+inline void decode(boost::tuple<A, B, C> &t, bufferlist::const_iterator &bp)
+{
+ decode(boost::get<0>(t), bp);
+ decode(boost::get<1>(t), bp);
+ decode(boost::get<2>(t), bp);
+}
+
+// std::pair<A,B>
+template<class A, class B,
+ typename a_traits, typename b_traits>
+inline std::enable_if_t<!a_traits::supported || !b_traits::supported>
+ encode(const std::pair<A,B> &p, bufferlist &bl, uint64_t features)
+{
+ encode(p.first, bl, features);
+ encode(p.second, bl, features);
+}
+template<class A, class B,
+ typename a_traits, typename b_traits>
+inline std::enable_if_t<!a_traits::supported ||
+ !b_traits::supported>
+ encode(const std::pair<A,B> &p, bufferlist &bl)
+{
+ encode(p.first, bl);
+ encode(p.second, bl);
+}
+template<class A, class B, typename a_traits, typename b_traits>
+inline std::enable_if_t<!a_traits::supported ||
+ !b_traits::supported>
+ decode(std::pair<A,B> &pa, bufferlist::const_iterator &p)
+{
+ decode(pa.first, p);
+ decode(pa.second, p);
+}
+
+// std::list<T>
+template<class T, class Alloc, typename traits>
+inline std::enable_if_t<!traits::supported>
+ encode(const std::list<T, Alloc>& ls, bufferlist& bl)
+{
+ __u32 n = (__u32)(ls.size()); // c++11 std::list::size() is O(1)
+ encode(n, bl);
+ for (auto p = ls.begin(); p != ls.end(); ++p)
+ encode(*p, bl);
+}
+template<class T, class Alloc, typename traits>
+inline std::enable_if_t<!traits::supported>
+ encode(const std::list<T,Alloc>& ls, bufferlist& bl, uint64_t features)
+{
+ using counter_encode_t = ceph_le32;
+ unsigned n = 0;
+ auto filler = bl.append_hole(sizeof(counter_encode_t));
+ for (const auto& item : ls) {
+ // we count on our own because of buggy std::list::size() implementation
+ // which doesn't follow the O(1) complexity constraint C++11 has brought.
+ ++n;
+ encode(item, bl, features);
+ }
+ counter_encode_t en;
+ en = n;
+ filler.copy_in(sizeof(en), reinterpret_cast<char*>(&en));
+}
+
+template<class T, class Alloc, typename traits>
+inline std::enable_if_t<!traits::supported>
+ decode(std::list<T,Alloc>& ls, bufferlist::const_iterator& p)
+{
+ __u32 n;
+ decode(n, p);
+ ls.clear();
+ while (n--) {
+ ls.emplace_back();
+ decode(ls.back(), p);
+ }
+}
+
+// std::list<std::shared_ptr<T>>
+template<class T, class Alloc>
+inline void encode(const std::list<std::shared_ptr<T>, Alloc>& ls,
+ bufferlist& bl)
+{
+ __u32 n = (__u32)(ls.size()); // c++11 std::list::size() is O(1)
+ encode(n, bl);
+ for (const auto& ref : ls) {
+ encode(*ref, bl);
+ }
+}
+template<class T, class Alloc>
+inline void encode(const std::list<std::shared_ptr<T>, Alloc>& ls,
+ bufferlist& bl, uint64_t features)
+{
+ __u32 n = (__u32)(ls.size()); // c++11 std::list::size() is O(1)
+ encode(n, bl);
+ for (const auto& ref : ls) {
+ encode(*ref, bl, features);
+ }
+}
+template<class T, class Alloc>
+inline void decode(std::list<std::shared_ptr<T>, Alloc>& ls,
+ bufferlist::const_iterator& p)
+{
+ __u32 n;
+ decode(n, p);
+ ls.clear();
+ while (n--) {
+ auto ref = std::make_shared<T>();
+ decode(*ref, p);
+ ls.emplace_back(std::move(ref));
+ }
+}
+
+// std::set<T>
+template<class T, class Comp, class Alloc, typename traits>
+inline std::enable_if_t<!traits::supported>
+ encode(const std::set<T,Comp,Alloc>& s, bufferlist& bl)
+{
+ __u32 n = (__u32)(s.size());
+ encode(n, bl);
+ for (auto p = s.begin(); p != s.end(); ++p)
+ encode(*p, bl);
+}
+template<class T, class Comp, class Alloc, typename traits>
+inline std::enable_if_t<!traits::supported>
+ decode(std::set<T,Comp,Alloc>& s, bufferlist::const_iterator& p)
+{
+ __u32 n;
+ decode(n, p);
+ s.clear();
+ while (n--) {
+ T v;
+ decode(v, p);
+ s.insert(v);
+ }
+}
+
+template<class T, class Comp, class Alloc, typename traits>
+inline typename std::enable_if<!traits::supported>::type
+ encode_nohead(const std::set<T,Comp,Alloc>& s, bufferlist& bl)
+{
+ for (auto p = s.begin(); p != s.end(); ++p)
+ encode(*p, bl);
+}
+template<class T, class Comp, class Alloc, typename traits>
+inline std::enable_if_t<!traits::supported>
+ decode_nohead(int len, std::set<T,Comp,Alloc>& s, bufferlist::const_iterator& p)
+{
+ for (int i=0; i<len; i++) {
+ T v;
+ decode(v, p);
+ s.insert(v);
+ }
+}
+
+// boost::container::flat_set<T>
+template<class T, class Comp, class Alloc, typename traits>
+inline std::enable_if_t<!traits::supported>
+encode(const boost::container::flat_set<T, Comp, Alloc>& s, bufferlist& bl)
+{
+ __u32 n = (__u32)(s.size());
+ encode(n, bl);
+ for (const auto& e : s)
+ encode(e, bl);
+}
+template<class T, class Comp, class Alloc, typename traits>
+inline std::enable_if_t<!traits::supported>
+decode(boost::container::flat_set<T, Comp, Alloc>& s, bufferlist::const_iterator& p)
+{
+ __u32 n;
+ decode(n, p);
+ s.clear();
+ s.reserve(n);
+ while (n--) {
+ T v;
+ decode(v, p);
+ s.insert(v);
+ }
+}
+
+template<class T, class Comp, class Alloc, typename traits>
+inline std::enable_if_t<!traits::supported>
+encode_nohead(const boost::container::flat_set<T, Comp, Alloc>& s,
+ bufferlist& bl)
+{
+ for (const auto& e : s)
+ encode(e, bl);
+}
+template<class T, class Comp, class Alloc, typename traits>
+inline std::enable_if_t<!traits::supported>
+decode_nohead(int len, boost::container::flat_set<T, Comp, Alloc>& s,
+ bufferlist::iterator& p)
+{
+ s.reserve(len);
+ for (int i=0; i<len; i++) {
+ T v;
+ decode(v, p);
+ s.insert(v);
+ }
+}
+
+// multiset
+template<class T, class Comp, class Alloc>
+inline void encode(const std::multiset<T,Comp,Alloc>& s, bufferlist& bl)
+{
+ __u32 n = (__u32)(s.size());
+ encode(n, bl);
+ for (auto p = s.begin(); p != s.end(); ++p)
+ encode(*p, bl);
+}
+template<class T, class Comp, class Alloc>
+inline void decode(std::multiset<T,Comp,Alloc>& s, bufferlist::const_iterator& p)
+{
+ __u32 n;
+ decode(n, p);
+ s.clear();
+ while (n--) {
+ T v;
+ decode(v, p);
+ s.insert(v);
+ }
+}
+
+template<class T, class Alloc, typename traits>
+inline std::enable_if_t<!traits::supported>
+ encode(const std::vector<T,Alloc>& v, bufferlist& bl, uint64_t features)
+{
+ __u32 n = (__u32)(v.size());
+ encode(n, bl);
+ for (auto p = v.begin(); p != v.end(); ++p)
+ encode(*p, bl, features);
+}
+template<class T, class Alloc, typename traits>
+inline std::enable_if_t<!traits::supported>
+ encode(const std::vector<T,Alloc>& v, bufferlist& bl)
+{
+ __u32 n = (__u32)(v.size());
+ encode(n, bl);
+ for (auto p = v.begin(); p != v.end(); ++p)
+ encode(*p, bl);
+}
+template<class T, class Alloc, typename traits>
+inline std::enable_if_t<!traits::supported>
+ decode(std::vector<T,Alloc>& v, bufferlist::const_iterator& p)
+{
+ __u32 n;
+ decode(n, p);
+ v.resize(n);
+ for (__u32 i=0; i<n; i++)
+ decode(v[i], p);
+}
+
+template<class T, class Alloc, typename traits>
+inline std::enable_if_t<!traits::supported>
+ encode_nohead(const std::vector<T,Alloc>& v, bufferlist& bl)
+{
+ for (auto p = v.begin(); p != v.end(); ++p)
+ encode(*p, bl);
+}
+template<class T, class Alloc, typename traits>
+inline std::enable_if_t<!traits::supported>
+ decode_nohead(int len, std::vector<T,Alloc>& v, bufferlist::const_iterator& p)
+{
+ v.resize(len);
+ for (__u32 i=0; i<v.size(); i++)
+ decode(v[i], p);
+}
+
+// small vector
+template<class T, std::size_t N, class Alloc, typename traits>
+inline std::enable_if_t<!traits::supported>
+ encode(const boost::container::small_vector<T,N,Alloc>& v, bufferlist& bl, uint64_t features)
+{
+ __u32 n = (__u32)(v.size());
+ encode(n, bl);
+ for (const auto& i : v)
+ encode(i, bl, features);
+}
+template<class T, std::size_t N, class Alloc, typename traits>
+inline std::enable_if_t<!traits::supported>
+ encode(const boost::container::small_vector<T,N,Alloc>& v, bufferlist& bl)
+{
+ __u32 n = (__u32)(v.size());
+ encode(n, bl);
+ for (const auto& i : v)
+ encode(i, bl);
+}
+template<class T, std::size_t N, class Alloc, typename traits>
+inline std::enable_if_t<!traits::supported>
+ decode(boost::container::small_vector<T,N,Alloc>& v, bufferlist::const_iterator& p)
+{
+ __u32 n;
+ decode(n, p);
+ v.resize(n);
+ for (auto& i : v)
+ decode(i, p);
+}
+
+template<class T, std::size_t N, class Alloc, typename traits>
+inline std::enable_if_t<!traits::supported>
+ encode_nohead(const boost::container::small_vector<T,N,Alloc>& v, bufferlist& bl)
+{
+ for (const auto& i : v)
+ encode(i, bl);
+}
+template<class T, std::size_t N, class Alloc, typename traits>
+inline std::enable_if_t<!traits::supported>
+ decode_nohead(int len, boost::container::small_vector<T,N,Alloc>& v, bufferlist::const_iterator& p)
+{
+ v.resize(len);
+ for (auto& i : v)
+ decode(i, p);
+}
+
+
+// vector (shared_ptr)
+template<class T,class Alloc>
+inline void encode(const std::vector<std::shared_ptr<T>,Alloc>& v,
+ bufferlist& bl,
+ uint64_t features)
+{
+ __u32 n = (__u32)(v.size());
+ encode(n, bl);
+ for (const auto& ref : v) {
+ if (ref)
+ encode(*ref, bl, features);
+ else
+ encode(T(), bl, features);
+ }
+}
+template<class T, class Alloc>
+inline void encode(const std::vector<std::shared_ptr<T>,Alloc>& v,
+ bufferlist& bl)
+{
+ __u32 n = (__u32)(v.size());
+ encode(n, bl);
+ for (const auto& ref : v) {
+ if (ref)
+ encode(*ref, bl);
+ else
+ encode(T(), bl);
+ }
+}
+template<class T, class Alloc>
+inline void decode(std::vector<std::shared_ptr<T>,Alloc>& v,
+ bufferlist::const_iterator& p)
+{
+ __u32 n;
+ decode(n, p);
+ v.clear();
+ v.reserve(n);
+ while (n--) {
+ auto ref = std::make_shared<T>();
+ decode(*ref, p);
+ v.emplace_back(std::move(ref));
+ }
+}
+
+// map
+template<class T, class U, class Comp, class Alloc,
+ typename t_traits, typename u_traits>
+inline std::enable_if_t<!t_traits::supported ||
+ !u_traits::supported>
+ encode(const std::map<T,U,Comp,Alloc>& m, bufferlist& bl)
+{
+ __u32 n = (__u32)(m.size());
+ encode(n, bl);
+ for (auto p = m.begin(); p != m.end(); ++p) {
+ encode(p->first, bl);
+ encode(p->second, bl);
+ }
+}
+template<class T, class U, class Comp, class Alloc,
+ typename t_traits, typename u_traits>
+inline std::enable_if_t<!t_traits::supported || !u_traits::supported>
+ encode(const std::map<T,U,Comp,Alloc>& m, bufferlist& bl, uint64_t features)
+{
+ __u32 n = (__u32)(m.size());
+ encode(n, bl);
+ for (auto p = m.begin(); p != m.end(); ++p) {
+ encode(p->first, bl, features);
+ encode(p->second, bl, features);
+ }
+}
+template<class T, class U, class Comp, class Alloc,
+ typename t_traits, typename u_traits>
+inline std::enable_if_t<!t_traits::supported || !u_traits::supported>
+ decode(std::map<T,U,Comp,Alloc>& m, bufferlist::const_iterator& p)
+{
+ __u32 n;
+ decode(n, p);
+ m.clear();
+ while (n--) {
+ T k;
+ decode(k, p);
+ decode(m[k], p);
+ }
+}
+template<class T, class U, class Comp, class Alloc>
+inline void decode_noclear(std::map<T,U,Comp,Alloc>& m, bufferlist::const_iterator& p)
+{
+ __u32 n;
+ decode(n, p);
+ while (n--) {
+ T k;
+ decode(k, p);
+ decode(m[k], p);
+ }
+}
+template<class T, class U, class Comp, class Alloc,
+ typename t_traits, typename u_traits>
+inline std::enable_if_t<!t_traits::supported || !u_traits::supported>
+ encode_nohead(const std::map<T,U,Comp,Alloc>& m, bufferlist& bl)
+{
+ for (auto p = m.begin(); p != m.end(); ++p) {
+ encode(p->first, bl);
+ encode(p->second, bl);
+ }
+}
+template<class T, class U, class Comp, class Alloc,
+ typename t_traits, typename u_traits>
+inline std::enable_if_t<!t_traits::supported || !u_traits::supported>
+ encode_nohead(const std::map<T,U,Comp,Alloc>& m, bufferlist& bl, uint64_t features)
+{
+ for (auto p = m.begin(); p != m.end(); ++p) {
+ encode(p->first, bl, features);
+ encode(p->second, bl, features);
+ }
+}
+template<class T, class U, class Comp, class Alloc,
+ typename t_traits, typename u_traits>
+inline std::enable_if_t<!t_traits::supported || !u_traits::supported>
+ decode_nohead(int n, std::map<T,U,Comp,Alloc>& m, bufferlist::const_iterator& p)
+{
+ m.clear();
+ while (n--) {
+ T k;
+ decode(k, p);
+ decode(m[k], p);
+ }
+}
+
+// boost::container::flat-map
+template<class T, class U, class Comp, class Alloc,
+ typename t_traits, typename u_traits>
+ inline std::enable_if_t<!t_traits::supported || !u_traits::supported>
+ encode(const boost::container::flat_map<T,U,Comp,Alloc>& m, bufferlist& bl)
+{
+ __u32 n = (__u32)(m.size());
+ encode(n, bl);
+ for (typename boost::container::flat_map<T,U,Comp>::const_iterator p
+ = m.begin(); p != m.end(); ++p) {
+ encode(p->first, bl);
+ encode(p->second, bl);
+ }
+}
+template<class T, class U, class Comp, class Alloc,
+ typename t_traits, typename u_traits>
+ inline std::enable_if_t<!t_traits::supported || !u_traits::supported>
+ encode(const boost::container::flat_map<T,U,Comp,Alloc>& m, bufferlist& bl,
+ uint64_t features)
+{
+ __u32 n = (__u32)(m.size());
+ encode(n, bl);
+ for (auto p = m.begin(); p != m.end(); ++p) {
+ encode(p->first, bl, features);
+ encode(p->second, bl, features);
+ }
+}
+template<class T, class U, class Comp, class Alloc,
+ typename t_traits, typename u_traits>
+ inline std::enable_if_t<!t_traits::supported || !u_traits::supported>
+ decode(boost::container::flat_map<T,U,Comp,Alloc>& m, bufferlist::const_iterator& p)
+{
+ __u32 n;
+ decode(n, p);
+ m.clear();
+ m.reserve(n);
+ while (n--) {
+ T k;
+ decode(k, p);
+ decode(m[k], p);
+ }
+}
+template<class T, class U, class Comp, class Alloc>
+inline void decode_noclear(boost::container::flat_map<T,U,Comp,Alloc>& m,
+ bufferlist::const_iterator& p)
+{
+ __u32 n;
+ decode(n, p);
+ m.reserve(m.size() + n);
+ while (n--) {
+ T k;
+ decode(k, p);
+ decode(m[k], p);
+ }
+}
+template<class T, class U, class Comp, class Alloc,
+ typename t_traits, typename u_traits>
+ inline std::enable_if_t<!t_traits::supported || !u_traits::supported>
+ encode_nohead(const boost::container::flat_map<T,U,Comp,Alloc>& m,
+ bufferlist& bl)
+{
+ for (auto p = m.begin(); p != m.end(); ++p) {
+ encode(p->first, bl);
+ encode(p->second, bl);
+ }
+}
+template<class T, class U, class Comp, class Alloc,
+ typename t_traits, typename u_traits>
+ inline std::enable_if_t<!t_traits::supported || !u_traits::supported>
+ encode_nohead(const boost::container::flat_map<T,U,Comp,Alloc>& m,
+ bufferlist& bl, uint64_t features)
+{
+ for (auto p = m.begin(); p != m.end(); ++p) {
+ encode(p->first, bl, features);
+ encode(p->second, bl, features);
+ }
+}
+template<class T, class U, class Comp, class Alloc,
+ typename t_traits, typename u_traits>
+inline std::enable_if_t<!t_traits::supported || !u_traits::supported>
+ decode_nohead(int n, boost::container::flat_map<T,U,Comp,Alloc>& m,
+ bufferlist::const_iterator& p)
+{
+ m.clear();
+ while (n--) {
+ T k;
+ decode(k, p);
+ decode(m[k], p);
+ }
+}
+
+// multimap
+template<class T, class U, class Comp, class Alloc>
+inline void encode(const std::multimap<T,U,Comp,Alloc>& m, bufferlist& bl)
+{
+ __u32 n = (__u32)(m.size());
+ encode(n, bl);
+ for (auto p = m.begin(); p != m.end(); ++p) {
+ encode(p->first, bl);
+ encode(p->second, bl);
+ }
+}
+template<class T, class U, class Comp, class Alloc>
+inline void decode(std::multimap<T,U,Comp,Alloc>& m, bufferlist::const_iterator& p)
+{
+ __u32 n;
+ decode(n, p);
+ m.clear();
+ while (n--) {
+ typename std::pair<T,U> tu = std::pair<T,U>();
+ decode(tu.first, p);
+ typename std::multimap<T,U,Comp,Alloc>::iterator it = m.insert(tu);
+ decode(it->second, p);
+ }
+}
+
+// ceph::unordered_map
+template<class T, class U, class Hash, class Pred, class Alloc>
+inline void encode(const unordered_map<T,U,Hash,Pred,Alloc>& m, bufferlist& bl,
+ uint64_t features)
+{
+ __u32 n = (__u32)(m.size());
+ encode(n, bl);
+ for (auto p = m.begin(); p != m.end(); ++p) {
+ encode(p->first, bl, features);
+ encode(p->second, bl, features);
+ }
+}
+template<class T, class U, class Hash, class Pred, class Alloc>
+inline void encode(const unordered_map<T,U,Hash,Pred,Alloc>& m, bufferlist& bl)
+{
+ __u32 n = (__u32)(m.size());
+ encode(n, bl);
+ for (auto p = m.begin(); p != m.end(); ++p) {
+ encode(p->first, bl);
+ encode(p->second, bl);
+ }
+}
+template<class T, class U, class Hash, class Pred, class Alloc>
+inline void decode(unordered_map<T,U,Hash,Pred,Alloc>& m, bufferlist::const_iterator& p)
+{
+ __u32 n;
+ decode(n, p);
+ m.clear();
+ while (n--) {
+ T k;
+ decode(k, p);
+ decode(m[k], p);
+ }
+}
+
+// ceph::unordered_set
+template<class T, class Hash, class Pred, class Alloc>
+inline void encode(const ceph::unordered_set<T,Hash,Pred,Alloc>& m, bufferlist& bl)
+{
+ __u32 n = (__u32)(m.size());
+ encode(n, bl);
+ for (auto p = m.begin(); p != m.end(); ++p)
+ encode(*p, bl);
+}
+template<class T, class Hash, class Pred, class Alloc>
+inline void decode(ceph::unordered_set<T,Hash,Pred,Alloc>& m, bufferlist::const_iterator& p)
+{
+ __u32 n;
+ decode(n, p);
+ m.clear();
+ while (n--) {
+ T k;
+ decode(k, p);
+ m.insert(k);
+ }
+}
+
+// deque
+template<class T, class Alloc>
+inline void encode(const std::deque<T,Alloc>& ls, bufferlist& bl, uint64_t features)
+{
+ __u32 n = ls.size();
+ encode(n, bl);
+ for (auto p = ls.begin(); p != ls.end(); ++p)
+ encode(*p, bl, features);
+}
+template<class T, class Alloc>
+inline void encode(const std::deque<T,Alloc>& ls, bufferlist& bl)
+{
+ __u32 n = ls.size();
+ encode(n, bl);
+ for (auto p = ls.begin(); p != ls.end(); ++p)
+ encode(*p, bl);
+}
+template<class T, class Alloc>
+inline void decode(std::deque<T,Alloc>& ls, bufferlist::const_iterator& p)
+{
+ __u32 n;
+ decode(n, p);
+ ls.clear();
+ while (n--) {
+ ls.emplace_back();
+ decode(ls.back(), p);
+ }
+}
+
+// std::array<T, N>
+template<class T, size_t N, typename traits>
+inline std::enable_if_t<!traits::supported>
+encode(const std::array<T, N>& v, bufferlist& bl, uint64_t features)
+{
+ for (const auto& e : v)
+ encode(e, bl, features);
+}
+template<class T, size_t N, typename traits>
+inline std::enable_if_t<!traits::supported>
+encode(const std::array<T, N>& v, bufferlist& bl)
+{
+ for (const auto& e : v)
+ encode(e, bl);
+}
+template<class T, size_t N, typename traits>
+inline std::enable_if_t<!traits::supported>
+decode(std::array<T, N>& v, bufferlist::const_iterator& p)
+{
+ for (auto& e : v)
+ decode(e, p);
+}
+}
+
+/*
+ * guards
+ */
+
+/**
+ * start encoding block
+ *
+ * @param v current (code) version of the encoding
+ * @param compat oldest code version that can decode it
+ * @param bl bufferlist to encode to
+ *
+ */
+#define ENCODE_START(v, compat, bl) \
+ __u8 struct_v = v; \
+ __u8 struct_compat = compat; \
+ ceph_le32 struct_len; \
+ auto filler = (bl).append_hole(sizeof(struct_v) + \
+ sizeof(struct_compat) + sizeof(struct_len)); \
+ const auto starting_bl_len = (bl).length(); \
+ using ::ceph::encode; \
+ do {
+
+/**
+ * finish encoding block
+ *
+ * @param bl bufferlist we were encoding to
+ * @param new_struct_compat struct-compat value to use
+ */
+#define ENCODE_FINISH_NEW_COMPAT(bl, new_struct_compat) \
+ } while (false); \
+ if (new_struct_compat) { \
+ struct_compat = new_struct_compat; \
+ } \
+ struct_len = (bl).length() - starting_bl_len; \
+ filler.copy_in(sizeof(struct_v), (char *)&struct_v); \
+ filler.copy_in(sizeof(struct_compat), \
+ (char *)&struct_compat); \
+ filler.copy_in(sizeof(struct_len), (char *)&struct_len);
+
+#define ENCODE_FINISH(bl) ENCODE_FINISH_NEW_COMPAT(bl, 0)
+
+#define DECODE_ERR_OLDVERSION(func, v, compatv) \
+ (std::string(func) + " no longer understand old encoding version " #v " < " + std::to_string(compatv))
+
+#define DECODE_ERR_PAST(func) \
+ (std::string(func) + " decode past end of struct encoding")
+
+/**
+ * check for very old encoding
+ *
+ * If the encoded data is older than oldestv, raise an exception.
+ *
+ * @param oldestv oldest version of the code we can successfully decode.
+ */
+#define DECODE_OLDEST(oldestv) \
+ if (struct_v < oldestv) \
+ throw ::ceph::buffer::malformed_input(DECODE_ERR_OLDVERSION(__PRETTY_FUNCTION__, v, oldestv));
+
+/**
+ * start a decoding block
+ *
+ * @param v current version of the encoding that the code supports/encodes
+ * @param bl bufferlist::iterator for the encoded data
+ */
+#define DECODE_START(v, bl) \
+ __u8 struct_v, struct_compat; \
+ using ::ceph::decode; \
+ decode(struct_v, bl); \
+ decode(struct_compat, bl); \
+ if (v < struct_compat) \
+ throw ::ceph::buffer::malformed_input(DECODE_ERR_OLDVERSION(__PRETTY_FUNCTION__, v, struct_compat)); \
+ __u32 struct_len; \
+ decode(struct_len, bl); \
+ if (struct_len > bl.get_remaining()) \
+ throw ::ceph::buffer::malformed_input(DECODE_ERR_PAST(__PRETTY_FUNCTION__)); \
+ unsigned struct_end = bl.get_off() + struct_len; \
+ do {
+
+/* BEWARE: any change to this macro MUST be also reflected in the duplicative
+ * DECODE_START_LEGACY_COMPAT_LEN! */
+#define __DECODE_START_LEGACY_COMPAT_LEN(v, compatv, lenv, skip_v, bl) \
+ using ::ceph::decode; \
+ __u8 struct_v; \
+ decode(struct_v, bl); \
+ if (struct_v >= compatv) { \
+ __u8 struct_compat; \
+ decode(struct_compat, bl); \
+ if (v < struct_compat) \
+ throw ::ceph::buffer::malformed_input(DECODE_ERR_OLDVERSION(__PRETTY_FUNCTION__, v, struct_compat)); \
+ } else if (skip_v) { \
+ if (bl.get_remaining() < skip_v) \
+ throw ::ceph::buffer::malformed_input(DECODE_ERR_PAST(__PRETTY_FUNCTION__)); \
+ bl += skip_v; \
+ } \
+ unsigned struct_end = 0; \
+ if (struct_v >= lenv) { \
+ __u32 struct_len; \
+ decode(struct_len, bl); \
+ if (struct_len > bl.get_remaining()) \
+ throw ::ceph::buffer::malformed_input(DECODE_ERR_PAST(__PRETTY_FUNCTION__)); \
+ struct_end = bl.get_off() + struct_len; \
+ } \
+ do {
+
+/**
+ * start a decoding block with legacy support for older encoding schemes
+ *
+ * The old encoding schemes has a __u8 struct_v only, or lacked either
+ * the compat version or length. Skip those fields conditionally.
+ *
+ * Most of the time, v, compatv, and lenv will all match the version
+ * where the structure was switched over to the new macros.
+ *
+ * @param v current version of the encoding that the code supports/encodes
+ * @param compatv oldest version that includes a __u8 compat version field
+ * @param lenv oldest version that includes a __u32 length wrapper
+ * @param bl bufferlist::iterator containing the encoded data
+ */
+
+/* BEWARE: this is duplication of __DECODE_START_LEGACY_COMPAT_LEN which
+ * MUST be changed altogether. For the rationale behind code duplication,
+ * please `git blame` and refer to the commit message. */
+#define DECODE_START_LEGACY_COMPAT_LEN(v, compatv, lenv, bl) \
+ using ::ceph::decode; \
+ __u8 struct_v; \
+ decode(struct_v, bl); \
+ if (struct_v >= compatv) { \
+ __u8 struct_compat; \
+ decode(struct_compat, bl); \
+ if (v < struct_compat) \
+ throw ::ceph::buffer::malformed_input(DECODE_ERR_OLDVERSION( \
+ __PRETTY_FUNCTION__, v, struct_compat)); \
+ } \
+ unsigned struct_end = 0; \
+ if (struct_v >= lenv) { \
+ __u32 struct_len; \
+ decode(struct_len, bl); \
+ if (struct_len > bl.get_remaining()) \
+ throw ::ceph::buffer::malformed_input(DECODE_ERR_PAST(__PRETTY_FUNCTION__)); \
+ struct_end = bl.get_off() + struct_len; \
+ } \
+ do {
+
+/**
+ * start a decoding block with legacy support for older encoding schemes
+ *
+ * This version of the macro assumes the legacy encoding had a 32 bit
+ * version
+ *
+ * The old encoding schemes has a __u8 struct_v only, or lacked either
+ * the compat version or length. Skip those fields conditionally.
+ *
+ * Most of the time, v, compatv, and lenv will all match the version
+ * where the structure was switched over to the new macros.
+ *
+ * @param v current version of the encoding that the code supports/encodes
+ * @param compatv oldest version that includes a __u8 compat version field
+ * @param lenv oldest version that includes a __u32 length wrapper
+ * @param bl bufferlist::iterator containing the encoded data
+ */
+#define DECODE_START_LEGACY_COMPAT_LEN_32(v, compatv, lenv, bl) \
+ __DECODE_START_LEGACY_COMPAT_LEN(v, compatv, lenv, 3u, bl)
+
+#define DECODE_START_LEGACY_COMPAT_LEN_16(v, compatv, lenv, bl) \
+ __DECODE_START_LEGACY_COMPAT_LEN(v, compatv, lenv, 1u, bl)
+
+/**
+ * finish decode block
+ *
+ * @param bl bufferlist::iterator we were decoding from
+ */
+#define DECODE_FINISH(bl) \
+ } while (false); \
+ if (struct_end) { \
+ if (bl.get_off() > struct_end) \
+ throw ::ceph::buffer::malformed_input(DECODE_ERR_PAST(__PRETTY_FUNCTION__)); \
+ if (bl.get_off() < struct_end) \
+ bl += struct_end - bl.get_off(); \
+ }
+
+namespace ceph {
+
+/*
+ * Encoders/decoders to read from current offset in a file handle and
+ * encode/decode the data according to argument types.
+ */
+inline ssize_t decode_file(int fd, std::string &str)
+{
+ bufferlist bl;
+ __u32 len = 0;
+ bl.read_fd(fd, sizeof(len));
+ decode(len, bl);
+ bl.read_fd(fd, len);
+ decode(str, bl);
+ return bl.length();
+}
+
+inline ssize_t decode_file(int fd, bufferptr &bp)
+{
+ bufferlist bl;
+ __u32 len = 0;
+ bl.read_fd(fd, sizeof(len));
+ decode(len, bl);
+ bl.read_fd(fd, len);
+ auto bli = std::cbegin(bl);
+
+ decode(bp, bli);
+ return bl.length();
+}
+}
+
+#endif
diff --git a/src/include/err.h b/src/include/err.h
new file mode 100644
index 000000000..c188e9753
--- /dev/null
+++ b/src/include/err.h
@@ -0,0 +1,31 @@
+#ifndef CEPH_ERR_H
+#define CEPH_ERR_H
+
+/*
+ * adapted from linux 2.6.24 include/linux/err.h
+ */
+#define MAX_ERRNO 4095
+#define IS_ERR_VALUE(x) ((x) >= (uintptr_t)-MAX_ERRNO)
+
+#include <errno.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+/* this generates a warning in c++; caller can do the cast manually
+static inline void *ERR_PTR(long error)
+{
+ return (void *) error;
+}
+*/
+
+static inline intptr_t PTR_ERR(const void *ptr)
+{
+ return (intptr_t) ptr;
+}
+
+static inline bool IS_ERR(const void *ptr)
+{
+ return IS_ERR_VALUE((uintptr_t)ptr);
+}
+
+#endif
diff --git a/src/include/error.h b/src/include/error.h
new file mode 100644
index 000000000..a548d9756
--- /dev/null
+++ b/src/include/error.h
@@ -0,0 +1,41 @@
+// -*- 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-2006 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.
+ *
+ */
+
+
+#include <stdarg.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define SYSERROR() syserror("At %s:%d", __FILE__, __LINE__)
+
+#define ASSERT(c) \
+ ((c) || (exiterror("Assertion failed at %s:%d", __FILE__, __LINE__), 1))
+
+/* print usage error message and exit */
+extern void userror(const char *use, const char *fmt, ...);
+
+/* print system error message and exit */
+extern void syserror(const char *fmt, ...);
+
+/* print error message and exit */
+extern void exiterror(const char *fmt, ...);
+
+/* print error message */
+extern void error(const char *fmt, ...);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
diff --git a/src/include/event_type.h b/src/include/event_type.h
new file mode 100644
index 000000000..aa6ddedb4
--- /dev/null
+++ b/src/include/event_type.h
@@ -0,0 +1,24 @@
+// -*- 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) 2015 XSky <haomai@xsky.com>
+ *
+ * Author: Haomai Wang <haomaiwang@gmail.com>
+ *
+ * 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_COMMON_EVENT_TYPE_H
+#define CEPH_COMMON_EVENT_TYPE_H
+
+#define EVENT_SOCKET_TYPE_NONE 0
+#define EVENT_SOCKET_TYPE_PIPE 1
+#define EVENT_SOCKET_TYPE_EVENTFD 2
+
+#endif
diff --git a/src/include/expected.hpp b/src/include/expected.hpp
new file mode 100644
index 000000000..740c6ad24
--- /dev/null
+++ b/src/include/expected.hpp
@@ -0,0 +1,2282 @@
+///
+// expected - An implementation of std::expected with extensions
+// Written in 2017 by Simon Brand (@TartanLlama)
+//
+// To the extent possible under law, the author(s) have dedicated all
+// copyright and related and neighboring rights to this software to the
+// public domain worldwide. This software is distributed without any warranty.
+//
+// You should have received a copy of the CC0 Public Domain Dedication
+// along with this software. If not, see
+// <http://creativecommons.org/publicdomain/zero/1.0/>.
+///
+
+#ifndef TL_EXPECTED_HPP
+#define TL_EXPECTED_HPP
+
+#define TL_EXPECTED_VERSION_MAJOR 0
+#define TL_EXPECTED_VERSION_MINOR 2
+
+#include <exception>
+#include <functional>
+#include <type_traits>
+#include <utility>
+
+#if defined(__EXCEPTIONS) || defined(_CPPUNWIND)
+#define TL_EXPECTED_EXCEPTIONS_ENABLED
+#endif
+
+#if (defined(_MSC_VER) && _MSC_VER == 1900)
+/// \exclude
+#define TL_EXPECTED_MSVC2015
+#define TL_EXPECTED_MSVC2015_CONSTEXPR
+#else
+#define TL_EXPECTED_MSVC2015_CONSTEXPR constexpr
+#endif
+
+#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \
+ !defined(__clang__))
+/// \exclude
+#define TL_EXPECTED_GCC49
+#endif
+
+#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 4 && \
+ !defined(__clang__))
+/// \exclude
+#define TL_EXPECTED_GCC54
+#endif
+
+#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 5 && \
+ !defined(__clang__))
+/// \exclude
+#define TL_EXPECTED_GCC55
+#endif
+
+#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \
+ !defined(__clang__))
+// GCC < 5 doesn't support overloading on const&& for member functions
+/// \exclude
+#define TL_EXPECTED_NO_CONSTRR
+
+// GCC < 5 doesn't support some standard C++11 type traits
+/// \exclude
+#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \
+ std::has_trivial_copy_constructor<T>
+/// \exclude
+#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \
+ std::has_trivial_copy_assign<T>
+
+// This one will be different for GCC 5.7 if it's ever supported
+/// \exclude
+#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \
+ std::is_trivially_destructible<T>
+
+// GCC 5 < v < 8 has a bug in is_trivially_copy_constructible which breaks std::vector
+// for non-copyable types
+#elif (defined(__GNUC__) && __GNUC__ < 8 && \
+ !defined(__clang__))
+#ifndef TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX
+#define TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX
+namespace tl {
+ namespace detail {
+ template<class T>
+ struct is_trivially_copy_constructible : std::is_trivially_copy_constructible<T>{};
+#ifdef _GLIBCXX_VECTOR
+ template<class T, class A>
+ struct is_trivially_copy_constructible<std::vector<T,A>>
+ : std::is_trivially_copy_constructible<T>{};
+#endif
+ }
+}
+#endif
+
+#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \
+ tl::detail::is_trivially_copy_constructible<T>
+#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \
+ std::is_trivially_copy_assignable<T>
+#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) std::is_trivially_destructible<T>
+#else
+/// \exclude
+#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \
+ std::is_trivially_copy_constructible<T>
+/// \exclude
+#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \
+ std::is_trivially_copy_assignable<T>
+/// \exclude
+#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \
+ std::is_trivially_destructible<T>
+#endif
+
+#if __cplusplus > 201103L
+/// \exclude
+#define TL_EXPECTED_CXX14
+#endif
+
+#ifdef TL_EXPECTED_GCC49
+#define TL_EXPECTED_GCC49_CONSTEXPR
+#else
+#define TL_EXPECTED_GCC49_CONSTEXPR constexpr
+#endif
+
+#if (__cplusplus == 201103L || defined(TL_EXPECTED_MSVC2015) || \
+ defined(TL_EXPECTED_GCC49))
+/// \exclude
+#define TL_EXPECTED_11_CONSTEXPR
+#else
+/// \exclude
+#define TL_EXPECTED_11_CONSTEXPR constexpr
+#endif
+
+namespace tl {
+template <class T, class E> class expected;
+
+#ifndef TL_MONOSTATE_INPLACE_MUTEX
+#define TL_MONOSTATE_INPLACE_MUTEX
+/// \brief Used to represent an expected with no data
+class monostate {};
+
+/// \brief A tag type to tell expected to construct its value in-place
+struct in_place_t {
+ explicit in_place_t() = default;
+};
+/// \brief A tag to tell expected to construct its value in-place
+static constexpr in_place_t in_place{};
+#endif
+
+/// Used as a wrapper to store the unexpected value
+template <class E> class unexpected {
+public:
+ static_assert(!std::is_same<E, void>::value, "E must not be void");
+
+ unexpected() = delete;
+ constexpr explicit unexpected(const E &e) : m_val(e) {}
+
+ constexpr explicit unexpected(E &&e) : m_val(std::move(e)) {}
+
+ /// \returns the contained value
+ /// \group unexpected_value
+ constexpr const E &value() const & { return m_val; }
+ /// \group unexpected_value
+ TL_EXPECTED_11_CONSTEXPR E &value() & { return m_val; }
+ /// \group unexpected_value
+ TL_EXPECTED_11_CONSTEXPR E &&value() && { return std::move(m_val); }
+ /// \exclude
+ constexpr const E &&value() const && { return std::move(m_val); }
+
+private:
+ E m_val;
+};
+
+/// \brief Compares two unexpected objects
+/// \details Simply compares lhs.value() to rhs.value()
+/// \group unexpected_relop
+template <class E>
+constexpr bool operator==(const unexpected<E> &lhs, const unexpected<E> &rhs) {
+ return lhs.value() == rhs.value();
+}
+/// \group unexpected_relop
+template <class E>
+constexpr bool operator!=(const unexpected<E> &lhs, const unexpected<E> &rhs) {
+ return lhs.value() != rhs.value();
+}
+/// \group unexpected_relop
+template <class E>
+constexpr bool operator<(const unexpected<E> &lhs, const unexpected<E> &rhs) {
+ return lhs.value() < rhs.value();
+}
+/// \group unexpected_relop
+template <class E>
+constexpr bool operator<=(const unexpected<E> &lhs, const unexpected<E> &rhs) {
+ return lhs.value() <= rhs.value();
+}
+/// \group unexpected_relop
+template <class E>
+constexpr bool operator>(const unexpected<E> &lhs, const unexpected<E> &rhs) {
+ return lhs.value() > rhs.value();
+}
+/// \group unexpected_relop
+template <class E>
+constexpr bool operator>=(const unexpected<E> &lhs, const unexpected<E> &rhs) {
+ return lhs.value() >= rhs.value();
+}
+
+/// Create an `unexpected` from `e`, deducing the return type
+///
+/// *Example:*
+/// auto e1 = tl::make_unexpected(42);
+/// unexpected<int> e2 (42); //same semantics
+template <class E>
+unexpected<typename std::decay<E>::type> make_unexpected(E &&e) {
+ return unexpected<typename std::decay<E>::type>(std::forward<E>(e));
+}
+
+/// \brief A tag type to tell expected to construct the unexpected value
+struct unexpect_t {
+ unexpect_t() = default;
+};
+/// \brief A tag to tell expected to construct the unexpected value
+static constexpr unexpect_t unexpect{};
+
+/// \exclude
+namespace detail {
+template<typename E>
+[[noreturn]] TL_EXPECTED_11_CONSTEXPR void throw_exception(E &&e) {
+#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
+ throw std::forward<E>(e);
+#else
+ #ifdef _MSC_VER
+ __assume(0);
+ #else
+ __builtin_unreachable();
+ #endif
+#endif
+}
+
+#ifndef TL_TRAITS_MUTEX
+#define TL_TRAITS_MUTEX
+// C++14-style aliases for brevity
+template <class T> using remove_const_t = typename std::remove_const<T>::type;
+template <class T>
+using remove_reference_t = typename std::remove_reference<T>::type;
+template <class T> using decay_t = typename std::decay<T>::type;
+template <bool E, class T = void>
+using enable_if_t = typename std::enable_if<E, T>::type;
+template <bool B, class T, class F>
+using conditional_t = typename std::conditional<B, T, F>::type;
+
+// std::conjunction from C++17
+template <class...> struct conjunction : std::true_type {};
+template <class B> struct conjunction<B> : B {};
+template <class B, class... Bs>
+struct conjunction<B, Bs...>
+ : std::conditional<bool(B::value), conjunction<Bs...>, B>::type {};
+
+// std::invoke from C++17
+// https://stackoverflow.com/questions/38288042/c11-14-invoke-workaround
+template <typename Fn, typename... Args,
+ typename = enable_if_t<std::is_member_pointer<decay_t<Fn>>{}>,
+ int = 0>
+constexpr auto invoke(Fn &&f, Args &&... args) noexcept(
+ noexcept(std::mem_fn(f)(std::forward<Args>(args)...)))
+ -> decltype(std::mem_fn(f)(std::forward<Args>(args)...)) {
+ return std::mem_fn(f)(std::forward<Args>(args)...);
+}
+
+template <typename Fn, typename... Args,
+ typename = enable_if_t<!std::is_member_pointer<decay_t<Fn>>{}>>
+constexpr auto invoke(Fn &&f, Args &&... args) noexcept(
+ noexcept(std::forward<Fn>(f)(std::forward<Args>(args)...)))
+ -> decltype(std::forward<Fn>(f)(std::forward<Args>(args)...)) {
+ return std::forward<Fn>(f)(std::forward<Args>(args)...);
+}
+
+// std::invoke_result from C++17
+template <class F, class, class... Us> struct invoke_result_impl;
+
+template <class F, class... Us>
+struct invoke_result_impl<
+ F, decltype(detail::invoke(std::declval<F>(), std::declval<Us>()...), void()),
+ Us...> {
+ using type = decltype(detail::invoke(std::declval<F>(), std::declval<Us>()...));
+};
+
+template <class F, class... Us>
+using invoke_result = invoke_result_impl<F, void, Us...>;
+
+template <class F, class... Us>
+using invoke_result_t = typename invoke_result<F, Us...>::type;
+#endif
+
+// Trait for checking if a type is a tl::expected
+template <class T> struct is_expected_impl : std::false_type {};
+template <class T, class E>
+struct is_expected_impl<expected<T, E>> : std::true_type {};
+template <class T> using is_expected = is_expected_impl<decay_t<T>>;
+
+template <class T, class E, class U>
+using expected_enable_forward_value = detail::enable_if_t<
+ std::is_constructible<T, U &&>::value &&
+ !std::is_same<detail::decay_t<U>, in_place_t>::value &&
+ !std::is_same<expected<T, E>, detail::decay_t<U>>::value &&
+ !std::is_same<unexpected<E>, detail::decay_t<U>>::value>;
+
+template <class T, class E, class U, class G, class UR, class GR>
+using expected_enable_from_other = detail::enable_if_t<
+ std::is_constructible<T, UR>::value &&
+ std::is_constructible<E, GR>::value &&
+ !std::is_constructible<T, expected<U, G> &>::value &&
+ !std::is_constructible<T, expected<U, G> &&>::value &&
+ !std::is_constructible<T, const expected<U, G> &>::value &&
+ !std::is_constructible<T, const expected<U, G> &&>::value &&
+ !std::is_convertible<expected<U, G> &, T>::value &&
+ !std::is_convertible<expected<U, G> &&, T>::value &&
+ !std::is_convertible<const expected<U, G> &, T>::value &&
+ !std::is_convertible<const expected<U, G> &&, T>::value>;
+
+template <class T, class U>
+using is_void_or = conditional_t<std::is_void<T>::value, std::true_type, U>;
+
+template <class T>
+using is_copy_constructible_or_void =
+ is_void_or<T, std::is_copy_constructible<T>>;
+
+template <class T>
+using is_move_constructible_or_void =
+ is_void_or<T, std::is_move_constructible<T>>;
+
+template <class T>
+using is_copy_assignable_or_void =
+ is_void_or<T, std::is_copy_assignable<T>>;
+
+
+template <class T>
+using is_move_assignable_or_void =
+ is_void_or<T, std::is_move_assignable<T>>;
+
+
+} // namespace detail
+
+/// \exclude
+namespace detail {
+struct no_init_t {};
+static constexpr no_init_t no_init{};
+
+// Implements the storage of the values, and ensures that the destructor is
+// trivial if it can be.
+//
+// This specialization is for where neither `T` or `E` is trivially
+// destructible, so the destructors must be called on destruction of the
+// `expected`
+template <class T, class E, bool = std::is_trivially_destructible<T>::value,
+ bool = std::is_trivially_destructible<E>::value>
+struct expected_storage_base {
+ constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
+ constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {}
+
+ template <class... Args,
+ detail::enable_if_t<std::is_constructible<T, Args &&...>::value> * =
+ nullptr>
+ constexpr expected_storage_base(in_place_t, Args &&... args)
+ : m_val(std::forward<Args>(args)...), m_has_val(true) {}
+
+ template <class U, class... Args,
+ detail::enable_if_t<std::is_constructible<
+ T, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+ constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
+ Args &&... args)
+ : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
+ template <class... Args,
+ detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
+ nullptr>
+ constexpr explicit expected_storage_base(unexpect_t, Args &&... args)
+ : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+ template <class U, class... Args,
+ detail::enable_if_t<std::is_constructible<
+ E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+ constexpr explicit expected_storage_base(unexpect_t,
+ std::initializer_list<U> il,
+ Args &&... args)
+ : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+ ~expected_storage_base() {
+ if (m_has_val) {
+ m_val.~T();
+ } else {
+ m_unexpect.~unexpected<E>();
+ }
+ }
+ union {
+ char m_no_init;
+ T m_val;
+ unexpected<E> m_unexpect;
+ };
+ bool m_has_val;
+};
+
+// This specialization is for when both `T` and `E` are trivially-destructible,
+// so the destructor of the `expected` can be trivial.
+template <class T, class E> struct expected_storage_base<T, E, true, true> {
+ constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
+ constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {}
+
+ template <class... Args,
+ detail::enable_if_t<std::is_constructible<T, Args &&...>::value> * =
+ nullptr>
+ constexpr expected_storage_base(in_place_t, Args &&... args)
+ : m_val(std::forward<Args>(args)...), m_has_val(true) {}
+
+ template <class U, class... Args,
+ detail::enable_if_t<std::is_constructible<
+ T, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+ constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
+ Args &&... args)
+ : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
+ template <class... Args,
+ detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
+ nullptr>
+ constexpr explicit expected_storage_base(unexpect_t, Args &&... args)
+ : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+ template <class U, class... Args,
+ detail::enable_if_t<std::is_constructible<
+ E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+ constexpr explicit expected_storage_base(unexpect_t,
+ std::initializer_list<U> il,
+ Args &&... args)
+ : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+ ~expected_storage_base() = default;
+ union {
+ char m_no_init;
+ T m_val;
+ unexpected<E> m_unexpect;
+ };
+ bool m_has_val;
+};
+
+// T is trivial, E is not.
+template <class T, class E> struct expected_storage_base<T, E, true, false> {
+ constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
+ TL_EXPECTED_MSVC2015_CONSTEXPR expected_storage_base(no_init_t)
+ : m_no_init(), m_has_val(false) {}
+
+ template <class... Args,
+ detail::enable_if_t<std::is_constructible<T, Args &&...>::value> * =
+ nullptr>
+ constexpr expected_storage_base(in_place_t, Args &&... args)
+ : m_val(std::forward<Args>(args)...), m_has_val(true) {}
+
+ template <class U, class... Args,
+ detail::enable_if_t<std::is_constructible<
+ T, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+ constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
+ Args &&... args)
+ : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
+ template <class... Args,
+ detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
+ nullptr>
+ constexpr explicit expected_storage_base(unexpect_t, Args &&... args)
+ : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+ template <class U, class... Args,
+ detail::enable_if_t<std::is_constructible<
+ E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+ constexpr explicit expected_storage_base(unexpect_t,
+ std::initializer_list<U> il,
+ Args &&... args)
+ : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+ ~expected_storage_base() {
+ if (!m_has_val) {
+ m_unexpect.~unexpected<E>();
+ }
+ }
+
+ union {
+ char m_no_init;
+ T m_val;
+ unexpected<E> m_unexpect;
+ };
+ bool m_has_val;
+};
+
+// E is trivial, T is not.
+template <class T, class E> struct expected_storage_base<T, E, false, true> {
+ constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
+ constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {}
+
+ template <class... Args,
+ detail::enable_if_t<std::is_constructible<T, Args &&...>::value> * =
+ nullptr>
+ constexpr expected_storage_base(in_place_t, Args &&... args)
+ : m_val(std::forward<Args>(args)...), m_has_val(true) {}
+
+ template <class U, class... Args,
+ detail::enable_if_t<std::is_constructible<
+ T, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+ constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
+ Args &&... args)
+ : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
+ template <class... Args,
+ detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
+ nullptr>
+ constexpr explicit expected_storage_base(unexpect_t, Args &&... args)
+ : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+ template <class U, class... Args,
+ detail::enable_if_t<std::is_constructible<
+ E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+ constexpr explicit expected_storage_base(unexpect_t,
+ std::initializer_list<U> il,
+ Args &&... args)
+ : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+ ~expected_storage_base() {
+ if (m_has_val) {
+ m_val.~T();
+ }
+ }
+ union {
+ char m_no_init;
+ T m_val;
+ unexpected<E> m_unexpect;
+ };
+ bool m_has_val;
+};
+
+// `T` is `void`, `E` is trivially-destructible
+template <class E> struct expected_storage_base<void, E, false, true> {
+ TL_EXPECTED_MSVC2015_CONSTEXPR expected_storage_base() : m_has_val(true) {}
+ constexpr expected_storage_base(no_init_t) : m_val(), m_has_val(false) {}
+
+ constexpr expected_storage_base(in_place_t) : m_has_val(true) {}
+
+ template <class... Args,
+ detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
+ nullptr>
+ constexpr explicit expected_storage_base(unexpect_t, Args &&... args)
+ : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+ template <class U, class... Args,
+ detail::enable_if_t<std::is_constructible<
+ E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+ constexpr explicit expected_storage_base(unexpect_t,
+ std::initializer_list<U> il,
+ Args &&... args)
+ : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+ ~expected_storage_base() = default;
+ struct dummy {};
+ union {
+ dummy m_val;
+ unexpected<E> m_unexpect;
+ };
+ bool m_has_val;
+};
+
+// `T` is `void`, `E` is not trivially-destructible
+template <class E> struct expected_storage_base<void, E, false, false> {
+ constexpr expected_storage_base() : m_dummy(), m_has_val(true) {}
+ constexpr expected_storage_base(no_init_t) : m_dummy(), m_has_val(false) {}
+
+ constexpr expected_storage_base(in_place_t) : m_dummy(), m_has_val(true) {}
+
+ template <class... Args,
+ detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
+ nullptr>
+ constexpr explicit expected_storage_base(unexpect_t, Args &&... args)
+ : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+ template <class U, class... Args,
+ detail::enable_if_t<std::is_constructible<
+ E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+ constexpr explicit expected_storage_base(unexpect_t,
+ std::initializer_list<U> il,
+ Args &&... args)
+ : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+ ~expected_storage_base() {
+ if (!m_has_val) {
+ m_unexpect.~unexpected<E>();
+ }
+ }
+
+ union {
+ char m_dummy;
+ unexpected<E> m_unexpect;
+ };
+ bool m_has_val;
+};
+
+// This base class provides some handy member functions which can be used in
+// further derived classes
+template <class T, class E>
+struct expected_operations_base : expected_storage_base<T, E> {
+ using expected_storage_base<T, E>::expected_storage_base;
+
+ template <class... Args> void construct(Args &&... args) noexcept {
+ new (std::addressof(this->m_val)) T(std::forward<Args>(args)...);
+ this->m_has_val = true;
+ }
+
+ template <class Rhs> void construct_with(Rhs &&rhs) noexcept {
+ new (std::addressof(this->m_val)) T(std::forward<Rhs>(rhs).get());
+ this->m_has_val = true;
+ }
+
+ template <class... Args> void construct_error(Args &&... args) noexcept {
+ new (std::addressof(this->m_unexpect))
+ unexpected<E>(std::forward<Args>(args)...);
+ this->m_has_val = false;
+ }
+
+ #ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
+
+ // These assign overloads ensure that the most efficient assignment
+ // implementation is used while maintaining the strong exception guarantee.
+ // The problematic case is where rhs has a value, but *this does not.
+ //
+ // This overload handles the case where we can just copy-construct `T`
+ // directly into place without throwing.
+ template <class U = T,
+ detail::enable_if_t<std::is_nothrow_copy_constructible<U>::value>
+ * = nullptr>
+ void assign(const expected_operations_base &rhs) noexcept {
+ if (!this->m_has_val && rhs.m_has_val) {
+ geterr().~unexpected<E>();
+ construct(rhs.get());
+ } else {
+ assign_common(rhs);
+ }
+ }
+
+ // This overload handles the case where we can attempt to create a copy of
+ // `T`, then no-throw move it into place if the copy was successful.
+ template <class U = T,
+ detail::enable_if_t<!std::is_nothrow_copy_constructible<U>::value &&
+ std::is_nothrow_move_constructible<U>::value>
+ * = nullptr>
+ void assign(const expected_operations_base &rhs) noexcept {
+ if (!this->m_has_val && rhs.m_has_val) {
+ T tmp = rhs.get();
+ geterr().~unexpected<E>();
+ construct(std::move(tmp));
+ } else {
+ assign_common(rhs);
+ }
+ }
+
+ // This overload is the worst-case, where we have to move-construct the
+ // unexpected value into temporary storage, then try to copy the T into place.
+ // If the construction succeeds, then everything is fine, but if it throws,
+ // then we move the old unexpected value back into place before rethrowing the
+ // exception.
+ template <class U = T,
+ detail::enable_if_t<!std::is_nothrow_copy_constructible<U>::value &&
+ !std::is_nothrow_move_constructible<U>::value>
+ * = nullptr>
+ void assign(const expected_operations_base &rhs) {
+ if (!this->m_has_val && rhs.m_has_val) {
+ auto tmp = std::move(geterr());
+ geterr().~unexpected<E>();
+
+ try {
+ construct(rhs.get());
+ } catch (...) {
+ geterr() = std::move(tmp);
+ throw;
+ }
+ } else {
+ assign_common(rhs);
+ }
+ }
+
+ // These overloads do the same as above, but for rvalues
+ template <class U = T,
+ detail::enable_if_t<std::is_nothrow_move_constructible<U>::value>
+ * = nullptr>
+ void assign(expected_operations_base &&rhs) noexcept {
+ if (!this->m_has_val && rhs.m_has_val) {
+ geterr().~unexpected<E>();
+ construct(std::move(rhs).get());
+ } else {
+ assign_common(std::move(rhs));
+ }
+ }
+
+ template <class U = T,
+ detail::enable_if_t<!std::is_nothrow_move_constructible<U>::value>
+ * = nullptr>
+ void assign(expected_operations_base &&rhs) {
+ if (!this->m_has_val && rhs.m_has_val) {
+ auto tmp = std::move(geterr());
+ geterr().~unexpected<E>();
+ try {
+ construct(std::move(rhs).get());
+ } catch (...) {
+ geterr() = std::move(tmp);
+ throw;
+ }
+ } else {
+ assign_common(std::move(rhs));
+ }
+ }
+
+ #else
+
+ // If exceptions are disabled then we can just copy-construct
+ void assign(const expected_operations_base &rhs) noexcept {
+ if (!this->m_has_val && rhs.m_has_val) {
+ geterr().~unexpected<E>();
+ construct(rhs.get());
+ } else {
+ assign_common(rhs);
+ }
+ }
+
+ void assign(expected_operations_base &&rhs) noexcept {
+ if (!this->m_has_val && rhs.m_has_val) {
+ geterr().~unexpected<E>();
+ construct(std::move(rhs).get());
+ } else {
+ assign_common(rhs);
+ }
+ }
+
+ #endif
+
+ // The common part of move/copy assigning
+ template <class Rhs> void assign_common(Rhs &&rhs) {
+ if (this->m_has_val) {
+ if (rhs.m_has_val) {
+ get() = std::forward<Rhs>(rhs).get();
+ } else {
+ destroy_val();
+ construct_error(std::forward<Rhs>(rhs).geterr());
+ }
+ } else {
+ if (!rhs.m_has_val) {
+ geterr() = std::forward<Rhs>(rhs).geterr();
+ }
+ }
+ }
+
+ bool has_value() const { return this->m_has_val; }
+
+ TL_EXPECTED_11_CONSTEXPR T &get() & { return this->m_val; }
+ constexpr const T &get() const & { return this->m_val; }
+ TL_EXPECTED_11_CONSTEXPR T &&get() && { return std::move(this->m_val); }
+#ifndef TL_EXPECTED_NO_CONSTRR
+ constexpr const T &&get() const && { return std::move(this->m_val); }
+#endif
+
+ TL_EXPECTED_11_CONSTEXPR unexpected<E> &geterr() & {
+ return this->m_unexpect;
+ }
+ constexpr const unexpected<E> &geterr() const & { return this->m_unexpect; }
+ TL_EXPECTED_11_CONSTEXPR unexpected<E> &&geterr() && {
+ return std::move(this->m_unexpect);
+ }
+#ifndef TL_EXPECTED_NO_CONSTRR
+ constexpr const unexpected<E> &&geterr() const && {
+ return std::move(this->m_unexpect);
+ }
+#endif
+
+ constexpr void destroy_val() {
+ get().~T();
+ }
+};
+
+// This base class provides some handy member functions which can be used in
+// further derived classes
+template <class E>
+struct expected_operations_base<void, E> : expected_storage_base<void, E> {
+ using expected_storage_base<void, E>::expected_storage_base;
+
+ template <class... Args> void construct() noexcept { this->m_has_val = true; }
+
+ // This function doesn't use its argument, but needs it so that code in
+ // levels above this can work independently of whether T is void
+ template <class Rhs> void construct_with(Rhs &&) noexcept {
+ this->m_has_val = true;
+ }
+
+ template <class... Args> void construct_error(Args &&... args) noexcept {
+ new (std::addressof(this->m_unexpect))
+ unexpected<E>(std::forward<Args>(args)...);
+ this->m_has_val = false;
+ }
+
+ template <class Rhs> void assign(Rhs &&rhs) noexcept {
+ if (!this->m_has_val) {
+ if (rhs.m_has_val) {
+ geterr().~unexpected<E>();
+ construct();
+ } else {
+ geterr() = std::forward<Rhs>(rhs).geterr();
+ }
+ } else {
+ if (!rhs.m_has_val) {
+ construct_error(std::forward<Rhs>(rhs).geterr());
+ }
+ }
+ }
+
+ bool has_value() const { return this->m_has_val; }
+
+ TL_EXPECTED_11_CONSTEXPR unexpected<E> &geterr() & {
+ return this->m_unexpect;
+ }
+ constexpr const unexpected<E> &geterr() const & { return this->m_unexpect; }
+ TL_EXPECTED_11_CONSTEXPR unexpected<E> &&geterr() && {
+ return std::move(this->m_unexpect);
+ }
+#ifndef TL_EXPECTED_NO_CONSTRR
+ constexpr const unexpected<E> &&geterr() const && {
+ return std::move(this->m_unexpect);
+ }
+#endif
+
+ constexpr void destroy_val() {
+ //no-op
+ }
+};
+
+// This class manages conditionally having a trivial copy constructor
+// This specialization is for when T and E are trivially copy constructible
+template <class T, class E,
+ bool = is_void_or<T, TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T)>::
+ value &&TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value>
+struct expected_copy_base : expected_operations_base<T, E> {
+ using expected_operations_base<T, E>::expected_operations_base;
+};
+
+// This specialization is for when T or E are not trivially copy constructible
+template <class T, class E>
+struct expected_copy_base<T, E, false> : expected_operations_base<T, E> {
+ using expected_operations_base<T, E>::expected_operations_base;
+
+ expected_copy_base() = default;
+ expected_copy_base(const expected_copy_base &rhs)
+ : expected_operations_base<T, E>(no_init) {
+ if (rhs.has_value()) {
+ this->construct_with(rhs);
+ } else {
+ this->construct_error(rhs.geterr());
+ }
+ }
+
+ expected_copy_base(expected_copy_base &&rhs) = default;
+ expected_copy_base &operator=(const expected_copy_base &rhs) = default;
+ expected_copy_base &operator=(expected_copy_base &&rhs) = default;
+};
+
+// This class manages conditionally having a trivial move constructor
+// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it
+// doesn't implement an analogue to std::is_trivially_move_constructible. We
+// have to make do with a non-trivial move constructor even if T is trivially
+// move constructible
+#ifndef TL_EXPECTED_GCC49
+template <class T, class E,
+ bool = is_void_or<T, std::is_trivially_move_constructible<T>>::value
+ &&std::is_trivially_move_constructible<E>::value>
+struct expected_move_base : expected_copy_base<T, E> {
+ using expected_copy_base<T, E>::expected_copy_base;
+};
+#else
+template <class T, class E, bool = false> struct expected_move_base;
+#endif
+template <class T, class E>
+struct expected_move_base<T, E, false> : expected_copy_base<T, E> {
+ using expected_copy_base<T, E>::expected_copy_base;
+
+ expected_move_base() = default;
+ expected_move_base(const expected_move_base &rhs) = default;
+
+ expected_move_base(expected_move_base &&rhs) noexcept(
+ std::is_nothrow_move_constructible<T>::value)
+ : expected_copy_base<T, E>(no_init) {
+ if (rhs.has_value()) {
+ this->construct_with(std::move(rhs));
+ } else {
+ this->construct_error(std::move(rhs.geterr()));
+ }
+ }
+ expected_move_base &operator=(const expected_move_base &rhs) = default;
+ expected_move_base &operator=(expected_move_base &&rhs) = default;
+};
+
+// This class manages conditionally having a trivial copy assignment operator
+template <class T, class E,
+ bool = is_void_or<
+ T, conjunction<TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T),
+ TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T),
+ TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T)>>::value
+ &&TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(E)::value
+ &&TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value
+ &&TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(E)::value>
+struct expected_copy_assign_base : expected_move_base<T, E> {
+ using expected_move_base<T, E>::expected_move_base;
+};
+
+template <class T, class E>
+struct expected_copy_assign_base<T, E, false> : expected_move_base<T, E> {
+ using expected_move_base<T, E>::expected_move_base;
+
+ expected_copy_assign_base() = default;
+ expected_copy_assign_base(const expected_copy_assign_base &rhs) = default;
+
+ expected_copy_assign_base(expected_copy_assign_base &&rhs) = default;
+ expected_copy_assign_base &operator=(const expected_copy_assign_base &rhs) {
+ this->assign(rhs);
+ return *this;
+ }
+ expected_copy_assign_base &
+ operator=(expected_copy_assign_base &&rhs) = default;
+};
+
+// This class manages conditionally having a trivial move assignment operator
+// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it
+// doesn't implement an analogue to std::is_trivially_move_assignable. We have
+// to make do with a non-trivial move assignment operator even if T is trivially
+// move assignable
+#ifndef TL_EXPECTED_GCC49
+template <class T, class E,
+ bool =
+ is_void_or<T, conjunction<std::is_trivially_destructible<T>,
+ std::is_trivially_move_constructible<T>,
+ std::is_trivially_move_assignable<T>>>::
+ value &&std::is_trivially_destructible<E>::value
+ &&std::is_trivially_move_constructible<E>::value
+ &&std::is_trivially_move_assignable<E>::value>
+struct expected_move_assign_base : expected_copy_assign_base<T, E> {
+ using expected_copy_assign_base<T, E>::expected_copy_assign_base;
+};
+#else
+template <class T, class E, bool = false> struct expected_move_assign_base;
+#endif
+
+template <class T, class E>
+struct expected_move_assign_base<T, E, false>
+ : expected_copy_assign_base<T, E> {
+ using expected_copy_assign_base<T, E>::expected_copy_assign_base;
+
+ expected_move_assign_base() = default;
+ expected_move_assign_base(const expected_move_assign_base &rhs) = default;
+
+ expected_move_assign_base(expected_move_assign_base &&rhs) = default;
+
+ expected_move_assign_base &
+ operator=(const expected_move_assign_base &rhs) = default;
+
+ expected_move_assign_base &
+ operator=(expected_move_assign_base &&rhs) noexcept(
+ std::is_nothrow_move_constructible<T>::value
+ &&std::is_nothrow_move_assignable<T>::value) {
+ this->assign(std::move(rhs));
+ return *this;
+ }
+};
+
+// expected_delete_ctor_base will conditionally delete copy and move
+// constructors depending on whether T is copy/move constructible
+template <class T, class E,
+ bool EnableCopy = (is_copy_constructible_or_void<T>::value &&
+ std::is_copy_constructible<E>::value),
+ bool EnableMove = (is_move_constructible_or_void<T>::value &&
+ std::is_move_constructible<E>::value)>
+struct expected_delete_ctor_base {
+ expected_delete_ctor_base() = default;
+ expected_delete_ctor_base(const expected_delete_ctor_base &) = default;
+ expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = default;
+ expected_delete_ctor_base &
+ operator=(const expected_delete_ctor_base &) = default;
+ expected_delete_ctor_base &
+ operator=(expected_delete_ctor_base &&) noexcept = default;
+};
+
+template <class T, class E>
+struct expected_delete_ctor_base<T, E, true, false> {
+ expected_delete_ctor_base() = default;
+ expected_delete_ctor_base(const expected_delete_ctor_base &) = default;
+ expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = delete;
+ expected_delete_ctor_base &
+ operator=(const expected_delete_ctor_base &) = default;
+ expected_delete_ctor_base &
+ operator=(expected_delete_ctor_base &&) noexcept = default;
+};
+
+template <class T, class E>
+struct expected_delete_ctor_base<T, E, false, true> {
+ expected_delete_ctor_base() = default;
+ expected_delete_ctor_base(const expected_delete_ctor_base &) = delete;
+ expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = default;
+ expected_delete_ctor_base &
+ operator=(const expected_delete_ctor_base &) = default;
+ expected_delete_ctor_base &
+ operator=(expected_delete_ctor_base &&) noexcept = default;
+};
+
+template <class T, class E>
+struct expected_delete_ctor_base<T, E, false, false> {
+ expected_delete_ctor_base() = default;
+ expected_delete_ctor_base(const expected_delete_ctor_base &) = delete;
+ expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = delete;
+ expected_delete_ctor_base &
+ operator=(const expected_delete_ctor_base &) = default;
+ expected_delete_ctor_base &
+ operator=(expected_delete_ctor_base &&) noexcept = default;
+};
+
+// expected_delete_assign_base will conditionally delete copy and move
+// constructors depending on whether T and E are copy/move constructible +
+// assignable
+template <class T, class E,
+ bool EnableCopy = (is_copy_constructible_or_void<T>::value &&
+ std::is_copy_constructible<E>::value &&
+ is_copy_assignable_or_void<T>::value &&
+ std::is_copy_assignable<E>::value),
+ bool EnableMove = (is_move_constructible_or_void<T>::value &&
+ std::is_move_constructible<E>::value &&
+ is_move_assignable_or_void<T>::value &&
+ std::is_move_assignable<E>::value)>
+struct expected_delete_assign_base {
+ expected_delete_assign_base() = default;
+ expected_delete_assign_base(const expected_delete_assign_base &) = default;
+ expected_delete_assign_base(expected_delete_assign_base &&) noexcept =
+ default;
+ expected_delete_assign_base &
+ operator=(const expected_delete_assign_base &) = default;
+ expected_delete_assign_base &
+ operator=(expected_delete_assign_base &&) noexcept = default;
+};
+
+template <class T, class E>
+struct expected_delete_assign_base<T, E, true, false> {
+ expected_delete_assign_base() = default;
+ expected_delete_assign_base(const expected_delete_assign_base &) = default;
+ expected_delete_assign_base(expected_delete_assign_base &&) noexcept =
+ default;
+ expected_delete_assign_base &
+ operator=(const expected_delete_assign_base &) = default;
+ expected_delete_assign_base &
+ operator=(expected_delete_assign_base &&) noexcept = delete;
+};
+
+template <class T, class E>
+struct expected_delete_assign_base<T, E, false, true> {
+ expected_delete_assign_base() = default;
+ expected_delete_assign_base(const expected_delete_assign_base &) = default;
+ expected_delete_assign_base(expected_delete_assign_base &&) noexcept =
+ default;
+ expected_delete_assign_base &
+ operator=(const expected_delete_assign_base &) = delete;
+ expected_delete_assign_base &
+ operator=(expected_delete_assign_base &&) noexcept = default;
+};
+
+template <class T, class E>
+struct expected_delete_assign_base<T, E, false, false> {
+ expected_delete_assign_base() = default;
+ expected_delete_assign_base(const expected_delete_assign_base &) = default;
+ expected_delete_assign_base(expected_delete_assign_base &&) noexcept =
+ default;
+ expected_delete_assign_base &
+ operator=(const expected_delete_assign_base &) = delete;
+ expected_delete_assign_base &
+ operator=(expected_delete_assign_base &&) noexcept = delete;
+};
+
+// This is needed to be able to construct the expected_default_ctor_base which
+// follows, while still conditionally deleting the default constructor.
+struct default_constructor_tag {
+ explicit constexpr default_constructor_tag() = default;
+};
+
+// expected_default_ctor_base will ensure that expected has a deleted default
+// consturctor if T is not default constructible.
+// This specialization is for when T is default constructible
+template <class T, class E,
+ bool Enable =
+ std::is_default_constructible<T>::value || std::is_void<T>::value>
+struct expected_default_ctor_base {
+ constexpr expected_default_ctor_base() noexcept = default;
+ constexpr expected_default_ctor_base(
+ expected_default_ctor_base const &) noexcept = default;
+ constexpr expected_default_ctor_base(expected_default_ctor_base &&) noexcept =
+ default;
+ expected_default_ctor_base &
+ operator=(expected_default_ctor_base const &) noexcept = default;
+ expected_default_ctor_base &
+ operator=(expected_default_ctor_base &&) noexcept = default;
+
+ constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
+};
+
+// This specialization is for when T is not default constructible
+template <class T, class E> struct expected_default_ctor_base<T, E, false> {
+ constexpr expected_default_ctor_base() noexcept = delete;
+ constexpr expected_default_ctor_base(
+ expected_default_ctor_base const &) noexcept = default;
+ constexpr expected_default_ctor_base(expected_default_ctor_base &&) noexcept =
+ default;
+ expected_default_ctor_base &
+ operator=(expected_default_ctor_base const &) noexcept = default;
+ expected_default_ctor_base &
+ operator=(expected_default_ctor_base &&) noexcept = default;
+
+ constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
+};
+} // namespace detail
+
+template <class E> class bad_expected_access : public std::exception {
+public:
+ explicit bad_expected_access(E e) : m_val(std::move(e)) {}
+
+ virtual const char *what() const noexcept override {
+ return "Bad expected access";
+ }
+
+ const E &error() const & { return m_val; }
+ E &error() & { return m_val; }
+ const E &&error() const && { return std::move(m_val); }
+ E &&error() && { return std::move(m_val); }
+
+private:
+ E m_val;
+};
+
+/// An `expected<T, E>` object is an object that contains the storage for
+/// another object and manages the lifetime of this contained object `T`.
+/// Alternatively it could contain the storage for another unexpected object
+/// `E`. The contained object may not be initialized after the expected object
+/// has been initialized, and may not be destroyed before the expected object
+/// has been destroyed. The initialization state of the contained object is
+/// tracked by the expected object.
+template <class T, class E>
+class expected : private detail::expected_move_assign_base<T, E>,
+ private detail::expected_delete_ctor_base<T, E>,
+ private detail::expected_delete_assign_base<T, E>,
+ private detail::expected_default_ctor_base<T, E> {
+ static_assert(!std::is_reference<T>::value, "T must not be a reference");
+ static_assert(!std::is_same<T, std::remove_cv<in_place_t>>::value,
+ "T must not be in_place_t");
+ static_assert(!std::is_same<T, std::remove_cv<unexpect_t>>::value,
+ "T must not be unexpect_t");
+ static_assert(!std::is_same<T, std::remove_cv<unexpected<E>>>::value,
+ "T must not be unexpected<E>");
+ static_assert(!std::is_reference<E>::value, "E must not be a reference");
+
+ T *valptr() { return std::addressof(this->m_val); }
+ const T *valptr() const { return std::addressof(this->m_val); }
+ unexpected<E> *errptr() { return std::addressof(this->m_unexpect); }
+ const unexpected<E> *errptr() const { return std::addressof(this->m_unexpect); }
+
+ template <class U = T,
+ detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
+ U &val() {
+ return this->m_val;
+ }
+ unexpected<E> &err() { return this->m_unexpect; }
+
+ template <class U = T,
+ detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
+ const U &val() const {
+ return this->m_val;
+ }
+ const unexpected<E> &err() const { return this->m_unexpect; }
+
+ using impl_base = detail::expected_move_assign_base<T, E>;
+ using ctor_base = detail::expected_default_ctor_base<T, E>;
+
+public:
+ typedef T value_type;
+ typedef E error_type;
+ typedef unexpected<E> unexpected_type;
+
+#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
+ !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
+ /// \group and_then
+ /// Carries out some operation which returns an expected on the stored object
+ /// if there is one. \requires `std::invoke(std::forward<F>(f), value())`
+ /// returns an `expected<U>` for some `U`. \returns Let `U` be the result
+ /// of `std::invoke(std::forward<F>(f), value())`. Returns an
+ /// `expected<U>`. The return value is empty if `*this` is empty,
+ /// otherwise the return value of `std::invoke(std::forward<F>(f), value())`
+ /// is returned.
+ /// \synopsis template <class F>\nconstexpr auto and_then(F &&f) &;
+ template <class F> TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) & {
+ return and_then_impl(*this, std::forward<F>(f));
+ }
+
+ /// \group and_then
+ /// \synopsis template <class F>\nconstexpr auto and_then(F &&f) &&;
+ template <class F> TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) && {
+ return and_then_impl(std::move(*this), std::forward<F>(f));
+ }
+
+ /// \group and_then
+ /// \synopsis template <class F>\nconstexpr auto and_then(F &&f) const &;
+ template <class F> constexpr auto and_then(F &&f) const & {
+ return and_then_impl(*this, std::forward<F>(f));
+ }
+
+#ifndef TL_EXPECTED_NO_CONSTRR
+ /// \group and_then
+ /// \synopsis template <class F>\nconstexpr auto and_then(F &&f) const &&;
+ template <class F> constexpr auto and_then(F &&f) const && {
+ return and_then_impl(std::move(*this), std::forward<F>(f));
+ }
+#endif
+
+#else
+ /// \group and_then
+ /// Carries out some operation which returns an expected on the stored object
+ /// if there is one. \requires `std::invoke(std::forward<F>(f), value())`
+ /// returns an `expected<U>` for some `U`. \returns Let `U` be the result
+ /// of `std::invoke(std::forward<F>(f), value())`. Returns an
+ /// `expected<U>`. The return value is empty if `*this` is empty,
+ /// otherwise the return value of `std::invoke(std::forward<F>(f), value())`
+ /// is returned.
+ /// \synopsis template <class F>\nconstexpr auto and_then(F &&f) &;
+ template <class F>
+ TL_EXPECTED_11_CONSTEXPR auto
+ and_then(F &&f) & -> decltype(and_then_impl(*this, std::forward<F>(f))) {
+ return and_then_impl(*this, std::forward<F>(f));
+ }
+
+ /// \group and_then
+ /// \synopsis template <class F>\nconstexpr auto and_then(F &&f) &&;
+ template <class F>
+ TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) && -> decltype(
+ and_then_impl(std::move(*this), std::forward<F>(f))) {
+ return and_then_impl(std::move(*this), std::forward<F>(f));
+ }
+
+ /// \group and_then
+ /// \synopsis template <class F>\nconstexpr auto and_then(F &&f) const &;
+ template <class F>
+ constexpr auto and_then(F &&f) const & -> decltype(
+ and_then_impl(*this, std::forward<F>(f))) {
+ return and_then_impl(*this, std::forward<F>(f));
+ }
+
+#ifndef TL_EXPECTED_NO_CONSTRR
+ /// \group and_then
+ /// \synopsis template <class F>\nconstexpr auto and_then(F &&f) const &&;
+ template <class F>
+ constexpr auto and_then(F &&f) const && -> decltype(
+ and_then_impl(std::move(*this), std::forward<F>(f))) {
+ return and_then_impl(std::move(*this), std::forward<F>(f));
+ }
+#endif
+#endif
+
+#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
+ !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
+ /// \brief Carries out some operation on the stored object if there is one.
+ /// \returns Let `U` be the result of `std::invoke(std::forward<F>(f),
+ /// value())`. If `U` is `void`, returns an `expected<monostate,E>, otherwise
+ // returns an `expected<U,E>`. If `*this` is unexpected, the
+ /// result is `*this`, otherwise an `expected<U,E>` is constructed from the
+ /// return value of `std::invoke(std::forward<F>(f), value())` and is
+ /// returned.
+ ///
+ /// \group map
+ /// \synopsis template <class F> constexpr auto map(F &&f) &;
+ template <class F> TL_EXPECTED_11_CONSTEXPR auto map(F &&f) & {
+ return expected_map_impl(*this, std::forward<F>(f));
+ }
+
+ /// \group map
+ /// \synopsis template <class F> constexpr auto map(F &&f) &&;
+ template <class F> TL_EXPECTED_11_CONSTEXPR auto map(F &&f) && {
+ return expected_map_impl(std::move(*this), std::forward<F>(f));
+ }
+
+ /// \group map
+ /// \synopsis template <class F> constexpr auto map(F &&f) const &;
+ template <class F> constexpr auto map(F &&f) const & {
+ return expected_map_impl(*this, std::forward<F>(f));
+ }
+
+ /// \group map
+ /// \synopsis template <class F> constexpr auto map(F &&f) const &&;
+ template <class F> constexpr auto map(F &&f) const && {
+ return expected_map_impl(std::move(*this), std::forward<F>(f));
+ }
+#else
+ /// \brief Carries out some operation on the stored object if there is one.
+ /// \returns Let `U` be the result of `std::invoke(std::forward<F>(f),
+ /// value())`. If `U` is `void`, returns an `expected<monostate,E>, otherwise
+ // returns an `expected<U,E>`. If `*this` is unexpected, the
+ /// result is `*this`, otherwise an `expected<U,E>` is constructed from the
+ /// return value of `std::invoke(std::forward<F>(f), value())` and is
+ /// returned.
+ ///
+ /// \group map
+ /// \synopsis template <class F> constexpr auto map(F &&f) &;
+ template <class F>
+ TL_EXPECTED_11_CONSTEXPR decltype(
+ expected_map_impl(std::declval<expected &>(), std::declval<F &&>()))
+ map(F &&f) & {
+ return expected_map_impl(*this, std::forward<F>(f));
+ }
+
+ /// \group map
+ /// \synopsis template <class F> constexpr auto map(F &&f) &&;
+ template <class F>
+ TL_EXPECTED_11_CONSTEXPR decltype(
+ expected_map_impl(std::declval<expected>(), std::declval<F &&>()))
+ map(F &&f) && {
+ return expected_map_impl(std::move(*this), std::forward<F>(f));
+ }
+
+ /// \group map
+ /// \synopsis template <class F> constexpr auto map(F &&f) const &;
+ template <class F>
+ constexpr decltype(expected_map_impl(std::declval<const expected &>(),
+ std::declval<F &&>()))
+ map(F &&f) const & {
+ return expected_map_impl(*this, std::forward<F>(f));
+ }
+
+#ifndef TL_EXPECTED_NO_CONSTRR
+ /// \group map
+ /// \synopsis template <class F> constexpr auto map(F &&f) const &&;
+ template <class F>
+ constexpr decltype(expected_map_impl(std::declval<const expected &&>(),
+ std::declval<F &&>()))
+ map(F &&f) const && {
+ return expected_map_impl(std::move(*this), std::forward<F>(f));
+ }
+#endif
+#endif
+
+#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
+ !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
+ /// \brief Carries out some operation on the stored unexpected object if there
+ /// is one.
+ /// \returns Let `U` be the result of `std::invoke(std::forward<F>(f),
+ /// value())`. If `U` is `void`, returns an `expected<T,monostate>`, otherwise
+ /// returns an `expected<T,U>`. If `*this` has an expected
+ /// value, the result is `*this`, otherwise an `expected<T,U>` is constructed
+ /// from `make_unexpected(std::invoke(std::forward<F>(f), value()))` and is
+ /// returned.
+ ///
+ /// \group map_error
+ /// \synopsis template <class F> constexpr auto map_error(F &&f) &;
+ template <class F> TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) & {
+ return map_error_impl(*this, std::forward<F>(f));
+ }
+
+ /// \group map_error
+ /// \synopsis template <class F> constexpr auto map_error(F &&f) &&;
+ template <class F> TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) && {
+ return map_error_impl(std::move(*this), std::forward<F>(f));
+ }
+
+ /// \group map_error
+ /// \synopsis template <class F> constexpr auto map_error(F &&f) const &;
+ template <class F> constexpr auto map_error(F &&f) const & {
+ return map_error_impl(*this, std::forward<F>(f));
+ }
+
+ /// \group map_error
+ /// \synopsis template <class F> constexpr auto map_error(F &&f) const &&;
+ template <class F> constexpr auto map_error(F &&f) const && {
+ return map_error_impl(std::move(*this), std::forward<F>(f));
+ }
+#else
+ /// \brief Carries out some operation on the stored unexpected object if there
+ /// is one.
+ /// \returns Let `U` be the result of `std::invoke(std::forward<F>(f),
+ /// value())`. Returns an `expected<T,U>`. If `*this` has an expected
+ /// value, the result is `*this`, otherwise an `expected<T,U>` is constructed
+ /// from `make_unexpected(std::invoke(std::forward<F>(f), value()))` and is
+ /// returned.
+ ///
+ /// \group map_error
+ /// \synopsis template <class F> constexpr auto map_error(F &&f) &;
+ template <class F>
+ TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected &>(),
+ std::declval<F &&>()))
+ map_error(F &&f) & {
+ return map_error_impl(*this, std::forward<F>(f));
+ }
+
+ /// \group map_error
+ /// \synopsis template <class F> constexpr auto map_error(F &&f) &&;
+ template <class F>
+ TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected &&>(),
+ std::declval<F &&>()))
+ map_error(F &&f) && {
+ return map_error_impl(std::move(*this), std::forward<F>(f));
+ }
+
+ /// \group map_error
+ /// \synopsis template <class F> constexpr auto map_error(F &&f) const &;
+ template <class F>
+ constexpr decltype(map_error_impl(std::declval<const expected &>(),
+ std::declval<F &&>()))
+ map_error(F &&f) const & {
+ return map_error_impl(*this, std::forward<F>(f));
+ }
+
+#ifndef TL_EXPECTED_NO_CONSTRR
+ /// \group map_error
+ /// \synopsis template <class F> constexpr auto map_error(F &&f) const &&;
+ template <class F>
+ constexpr decltype(map_error_impl(std::declval<const expected &&>(),
+ std::declval<F &&>()))
+ map_error(F &&f) const && {
+ return map_error_impl(std::move(*this), std::forward<F>(f));
+ }
+#endif
+#endif
+
+ /// \brief Calls `f` if the expectd is in the unexpected state
+ /// \requires `F` is invokable with `E`, and `std::invoke_result_t<F>`
+ /// must be void or convertible to `expcted<T,E>`.
+ /// \effects If `*this` has a value, returns `*this`.
+ /// Otherwise, if `f` returns `void`, calls `std::forward<F>(f)(E)` and returns
+ /// `std::nullopt`. Otherwise, returns `std::forward<F>(f)(E)`.
+ ///
+ /// \group or_else
+ template <class F> expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) & {
+ return or_else_impl(*this, std::forward<F>(f));
+ }
+
+ template <class F> expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) && {
+ return or_else_impl(std::move(*this), std::forward<F>(f));
+ }
+
+ template <class F> expected constexpr or_else(F &&f) const & {
+ return or_else_impl(*this, std::forward<F>(f));
+ }
+
+#ifndef TL_EXPECTED_NO_CONSTRR
+ template <class F> expected constexpr or_else(F &&f) const && {
+ return or_else_impl(std::move(*this), std::forward<F>(f));
+ }
+#endif
+ constexpr expected() = default;
+ constexpr expected(const expected &rhs) = default;
+ constexpr expected(expected &&rhs) = default;
+ expected &operator=(const expected &rhs) = default;
+ expected &operator=(expected &&rhs) = default;
+
+ template <class... Args,
+ detail::enable_if_t<std::is_constructible<T, Args &&...>::value> * =
+ nullptr>
+ constexpr expected(in_place_t, Args &&... args)
+ : impl_base(in_place, std::forward<Args>(args)...),
+ ctor_base(detail::default_constructor_tag{}) {}
+
+ template <class U, class... Args,
+ detail::enable_if_t<std::is_constructible<
+ T, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+ constexpr expected(in_place_t, std::initializer_list<U> il, Args &&... args)
+ : impl_base(in_place, il, std::forward<Args>(args)...),
+ ctor_base(detail::default_constructor_tag{}) {}
+
+ /// \group unexpected_ctor
+ /// \synopsis EXPLICIT constexpr expected(const unexpected<G> &e);
+ template <class G = E,
+ detail::enable_if_t<std::is_constructible<E, const G &>::value> * =
+ nullptr,
+ detail::enable_if_t<!std::is_convertible<const G &, E>::value> * =
+ nullptr>
+ explicit constexpr expected(const unexpected<G> &e)
+ : impl_base(unexpect, e.value()),
+ ctor_base(detail::default_constructor_tag{}) {}
+
+ /// \exclude
+ template <
+ class G = E,
+ detail::enable_if_t<std::is_constructible<E, const G &>::value> * =
+ nullptr,
+ detail::enable_if_t<std::is_convertible<const G &, E>::value> * = nullptr>
+ constexpr expected(unexpected<G> const &e)
+ : impl_base(unexpect, e.value()),
+ ctor_base(detail::default_constructor_tag{}) {}
+
+ /// \group unexpected_ctor
+ /// \synopsis EXPLICIT constexpr expected(unexpected<G> &&e);
+ template <
+ class G = E,
+ detail::enable_if_t<std::is_constructible<E, G &&>::value> * = nullptr,
+ detail::enable_if_t<!std::is_convertible<G &&, E>::value> * = nullptr>
+ explicit constexpr expected(unexpected<G> &&e) noexcept(
+ std::is_nothrow_constructible<E, G &&>::value)
+ : impl_base(unexpect, std::move(e.value())),
+ ctor_base(detail::default_constructor_tag{}) {}
+
+ /// \exclude
+ template <
+ class G = E,
+ detail::enable_if_t<std::is_constructible<E, G &&>::value> * = nullptr,
+ detail::enable_if_t<std::is_convertible<G &&, E>::value> * = nullptr>
+ constexpr expected(unexpected<G> &&e) noexcept(
+ std::is_nothrow_constructible<E, G &&>::value)
+ : impl_base(unexpect, std::move(e.value())),
+ ctor_base(detail::default_constructor_tag{}) {}
+
+ template <class... Args,
+ detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
+ nullptr>
+ constexpr explicit expected(unexpect_t, Args &&... args)
+ : impl_base(unexpect, std::forward<Args>(args)...),
+ ctor_base(detail::default_constructor_tag{}) {}
+
+ /// \exclude
+ template <class U, class... Args,
+ detail::enable_if_t<std::is_constructible<
+ E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+ constexpr explicit expected(unexpect_t, std::initializer_list<U> il,
+ Args &&... args)
+ : impl_base(unexpect, il, std::forward<Args>(args)...),
+ ctor_base(detail::default_constructor_tag{}) {}
+
+ template <class U, class G,
+ detail::enable_if_t<!(std::is_convertible<U const &, T>::value &&
+ std::is_convertible<G const &, E>::value)> * =
+ nullptr,
+ detail::expected_enable_from_other<T, E, U, G, const U &, const G &>
+ * = nullptr>
+ explicit TL_EXPECTED_11_CONSTEXPR expected(const expected<U, G> &rhs)
+ : ctor_base(detail::default_constructor_tag{}) {
+ if (rhs.has_value()) {
+ this->construct(*rhs);
+ } else {
+ this->construct_error(rhs.error());
+ }
+ }
+
+ /// \exclude
+ template <class U, class G,
+ detail::enable_if_t<(std::is_convertible<U const &, T>::value &&
+ std::is_convertible<G const &, E>::value)> * =
+ nullptr,
+ detail::expected_enable_from_other<T, E, U, G, const U &, const G &>
+ * = nullptr>
+ TL_EXPECTED_11_CONSTEXPR expected(const expected<U, G> &rhs)
+ : ctor_base(detail::default_constructor_tag{}) {
+ if (rhs.has_value()) {
+ this->construct(*rhs);
+ } else {
+ this->construct_error(rhs.error());
+ }
+ }
+
+ template <
+ class U, class G,
+ detail::enable_if_t<!(std::is_convertible<U &&, T>::value &&
+ std::is_convertible<G &&, E>::value)> * = nullptr,
+ detail::expected_enable_from_other<T, E, U, G, U &&, G &&> * = nullptr>
+ explicit TL_EXPECTED_11_CONSTEXPR expected(expected<U, G> &&rhs)
+ : ctor_base(detail::default_constructor_tag{}) {
+ if (rhs.has_value()) {
+ this->construct(std::move(*rhs));
+ } else {
+ this->construct_error(std::move(rhs.error()));
+ }
+ }
+
+ /// \exclude
+ template <
+ class U, class G,
+ detail::enable_if_t<(std::is_convertible<U &&, T>::value &&
+ std::is_convertible<G &&, E>::value)> * = nullptr,
+ detail::expected_enable_from_other<T, E, U, G, U &&, G &&> * = nullptr>
+ TL_EXPECTED_11_CONSTEXPR expected(expected<U, G> &&rhs)
+ : ctor_base(detail::default_constructor_tag{}) {
+ if (rhs.has_value()) {
+ this->construct(std::move(*rhs));
+ } else {
+ this->construct_error(std::move(rhs.error()));
+ }
+ }
+
+ template <
+ class U = T,
+ detail::enable_if_t<!std::is_convertible<U &&, T>::value> * = nullptr,
+ detail::expected_enable_forward_value<T, E, U> * = nullptr>
+ explicit TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v)
+ : expected(in_place, std::forward<U>(v)) {}
+
+ /// \exclude
+ template <
+ class U = T,
+ detail::enable_if_t<std::is_convertible<U &&, T>::value> * = nullptr,
+ detail::expected_enable_forward_value<T, E, U> * = nullptr>
+ TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v)
+ : expected(in_place, std::forward<U>(v)) {}
+
+ template <
+ class U = T, class G = T,
+ detail::enable_if_t<std::is_nothrow_constructible<T, U &&>::value> * =
+ nullptr,
+ detail::enable_if_t<!std::is_void<G>::value> * = nullptr,
+ detail::enable_if_t<
+ (!std::is_same<expected<T, E>, detail::decay_t<U>>::value &&
+ !detail::conjunction<std::is_scalar<T>,
+ std::is_same<T, detail::decay_t<U>>>::value &&
+ std::is_constructible<T, U>::value &&
+ std::is_assignable<G &, U>::value &&
+ std::is_nothrow_move_constructible<E>::value)> * = nullptr>
+ expected &operator=(U &&v) {
+ if (has_value()) {
+ val() = std::forward<U>(v);
+ } else {
+ err().~unexpected<E>();
+ ::new (valptr()) T(std::forward<U>(v));
+ this->m_has_val = true;
+ }
+
+ return *this;
+ }
+
+ /// \exclude
+ template <
+ class U = T, class G = T,
+ detail::enable_if_t<!std::is_nothrow_constructible<T, U &&>::value> * =
+ nullptr,
+ detail::enable_if_t<!std::is_void<U>::value> * = nullptr,
+ detail::enable_if_t<
+ (!std::is_same<expected<T, E>, detail::decay_t<U>>::value &&
+ !detail::conjunction<std::is_scalar<T>,
+ std::is_same<T, detail::decay_t<U>>>::value &&
+ std::is_constructible<T, U>::value &&
+ std::is_assignable<G &, U>::value &&
+ std::is_nothrow_move_constructible<E>::value)> * = nullptr>
+ expected &operator=(U &&v) {
+ if (has_value()) {
+ val() = std::forward<U>(v);
+ } else {
+ auto tmp = std::move(err());
+ err().~unexpected<E>();
+
+ #ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
+ try {
+ ::new (valptr()) T(std::move(v));
+ this->m_has_val = true;
+ } catch (...) {
+ err() = std::move(tmp);
+ throw;
+ }
+ #else
+ ::new (valptr()) T(std::move(v));
+ this->m_has_val = true;
+ #endif
+ }
+
+ return *this;
+ }
+
+ template <class G = E,
+ detail::enable_if_t<std::is_nothrow_copy_constructible<G>::value &&
+ std::is_assignable<G &, G>::value> * = nullptr>
+ expected &operator=(const unexpected<G> &rhs) {
+ if (!has_value()) {
+ err() = rhs;
+ } else {
+ this->destroy_val();
+ ::new (errptr()) unexpected<E>(rhs);
+ this->m_has_val = false;
+ }
+
+ return *this;
+ }
+
+ template <class G = E,
+ detail::enable_if_t<std::is_nothrow_move_constructible<G>::value &&
+ std::is_move_assignable<G>::value> * = nullptr>
+ expected &operator=(unexpected<G> &&rhs) noexcept {
+ if (!has_value()) {
+ err() = std::move(rhs);
+ } else {
+ this->destroy_val();
+ ::new (errptr()) unexpected<E>(std::move(rhs));
+ this->m_has_val = false;
+ }
+
+ return *this;
+ }
+
+ template <class... Args, detail::enable_if_t<std::is_nothrow_constructible<
+ T, Args &&...>::value> * = nullptr>
+ void emplace(Args &&... args) {
+ if (has_value()) {
+ val() = T(std::forward<Args>(args)...);
+ } else {
+ err().~unexpected<E>();
+ ::new (valptr()) T(std::forward<Args>(args)...);
+ this->m_has_val = true;
+ }
+ }
+
+ /// \exclude
+ template <class... Args, detail::enable_if_t<!std::is_nothrow_constructible<
+ T, Args &&...>::value> * = nullptr>
+ void emplace(Args &&... args) {
+ if (has_value()) {
+ val() = T(std::forward<Args>(args)...);
+ } else {
+ auto tmp = std::move(err());
+ err().~unexpected<E>();
+
+ #ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
+ try {
+ ::new (valptr()) T(std::forward<Args>(args)...);
+ this->m_has_val = true;
+ } catch (...) {
+ err() = std::move(tmp);
+ throw;
+ }
+ #else
+ ::new (valptr()) T(std::forward<Args>(args)...);
+ this->m_has_val = true;
+ #endif
+ }
+ }
+
+ template <class U, class... Args,
+ detail::enable_if_t<std::is_nothrow_constructible<
+ T, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+ void emplace(std::initializer_list<U> il, Args &&... args) {
+ if (has_value()) {
+ T t(il, std::forward<Args>(args)...);
+ val() = std::move(t);
+ } else {
+ err().~unexpected<E>();
+ ::new (valptr()) T(il, std::forward<Args>(args)...);
+ this->m_has_val = true;
+ }
+ }
+
+ /// \exclude
+ template <class U, class... Args,
+ detail::enable_if_t<!std::is_nothrow_constructible<
+ T, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+ void emplace(std::initializer_list<U> il, Args &&... args) {
+ if (has_value()) {
+ T t(il, std::forward<Args>(args)...);
+ val() = std::move(t);
+ } else {
+ auto tmp = std::move(err());
+ err().~unexpected<E>();
+
+ #ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
+ try {
+ ::new (valptr()) T(il, std::forward<Args>(args)...);
+ this->m_has_val = true;
+ } catch (...) {
+ err() = std::move(tmp);
+ throw;
+ }
+ #else
+ ::new (valptr()) T(il, std::forward<Args>(args)...);
+ this->m_has_val = true;
+ #endif
+ }
+ }
+
+ // TODO SFINAE
+ void swap(expected &rhs) noexcept(
+ std::is_nothrow_move_constructible<T>::value &&noexcept(
+ swap(std::declval<T &>(), std::declval<T &>())) &&
+ std::is_nothrow_move_constructible<E>::value &&
+ noexcept(swap(std::declval<E &>(), std::declval<E &>()))) {
+ if (has_value() && rhs.has_value()) {
+ using std::swap;
+ swap(val(), rhs.val());
+ } else if (!has_value() && rhs.has_value()) {
+ using std::swap;
+ swap(err(), rhs.err());
+ } else if (has_value()) {
+ auto temp = std::move(rhs.err());
+ ::new (rhs.valptr()) T(val());
+ ::new (errptr()) unexpected_type(std::move(temp));
+ std::swap(this->m_has_val, rhs.m_has_val);
+ } else {
+ auto temp = std::move(this->err());
+ ::new (valptr()) T(rhs.val());
+ ::new (errptr()) unexpected_type(std::move(temp));
+ std::swap(this->m_has_val, rhs.m_has_val);
+ }
+ }
+
+ /// \returns a pointer to the stored value
+ /// \requires a value is stored
+ /// \group pointer
+ constexpr const T *operator->() const { return valptr(); }
+ /// \group pointer
+ TL_EXPECTED_11_CONSTEXPR T *operator->() { return valptr(); }
+
+ /// \returns the stored value
+ /// \requires a value is stored
+ /// \group deref
+ template <class U = T,
+ detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
+ constexpr const U &operator*() const & {
+ return val();
+ }
+ /// \group deref
+ template <class U = T,
+ detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
+ TL_EXPECTED_11_CONSTEXPR U &operator*() & {
+ return val();
+ }
+ /// \group deref
+ template <class U = T,
+ detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
+ constexpr const U &&operator*() const && {
+ return std::move(val());
+ }
+ /// \group deref
+ template <class U = T,
+ detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
+ TL_EXPECTED_11_CONSTEXPR U &&operator*() && {
+ return std::move(val());
+ }
+
+ /// \returns whether or not the optional has a value
+ /// \group has_value
+ constexpr bool has_value() const noexcept { return this->m_has_val; }
+ /// \group has_value
+ constexpr explicit operator bool() const noexcept { return this->m_has_val; }
+
+ /// \returns the contained value if there is one, otherwise throws
+ /// [bad_expected_access]
+ ///
+ /// \group value
+ template <class U = T,
+ detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
+ TL_EXPECTED_11_CONSTEXPR const U &value() const & {
+ if (!has_value())
+ detail::throw_exception(bad_expected_access<E>(err().value()));
+ return val();
+ }
+ /// \group value
+ template <class U = T,
+ detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
+ TL_EXPECTED_11_CONSTEXPR U &value() & {
+ if (!has_value())
+ detail::throw_exception(bad_expected_access<E>(err().value()));
+ return val();
+ }
+ /// \group value
+ template <class U = T,
+ detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
+ TL_EXPECTED_11_CONSTEXPR const U &&value() const && {
+ if (!has_value())
+ detail::throw_exception(bad_expected_access<E>(err().value()));
+ return std::move(val());
+ }
+ /// \group value
+ template <class U = T,
+ detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
+ TL_EXPECTED_11_CONSTEXPR U &&value() && {
+ if (!has_value())
+ detail::throw_exception(bad_expected_access<E>(err().value()));
+ return std::move(val());
+ }
+
+ /// \returns the unexpected value
+ /// \requires there is an unexpected value
+ /// \group error
+ constexpr const E &error() const & { return err().value(); }
+ /// \group error
+ TL_EXPECTED_11_CONSTEXPR E &error() & { return err().value(); }
+ /// \group error
+ constexpr const E &&error() const && { return std::move(err().value()); }
+ /// \group error
+ TL_EXPECTED_11_CONSTEXPR E &&error() && { return std::move(err().value()); }
+
+ /// \returns the stored value if there is one, otherwise returns `u`
+ /// \group value_or
+ template <class U> constexpr T value_or(U &&v) const & {
+ static_assert(std::is_copy_constructible<T>::value &&
+ std::is_convertible<U &&, T>::value,
+ "T must be copy-constructible and convertible to from U&&");
+ return bool(*this) ? **this : static_cast<T>(std::forward<U>(v));
+ }
+ /// \group value_or
+ template <class U> TL_EXPECTED_11_CONSTEXPR T value_or(U &&v) && {
+ static_assert(std::is_move_constructible<T>::value &&
+ std::is_convertible<U &&, T>::value,
+ "T must be move-constructible and convertible to from U&&");
+ return bool(*this) ? std::move(**this) : static_cast<T>(std::forward<U>(v));
+ }
+};
+
+/// \exclude
+namespace detail {
+template <class Exp> using exp_t = typename detail::decay_t<Exp>::value_type;
+template <class Exp> using err_t = typename detail::decay_t<Exp>::error_type;
+template <class Exp, class Ret> using ret_t = expected<Ret, err_t<Exp>>;
+
+#ifdef TL_EXPECTED_CXX14
+template <class Exp, class F,
+ detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
+ class Ret = decltype(detail::invoke(std::declval<F>(),
+ *std::declval<Exp>()))>
+constexpr auto and_then_impl(Exp &&exp, F &&f) {
+ static_assert(detail::is_expected<Ret>::value, "F must return an expected");
+
+ return exp.has_value()
+ ? detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp))
+ : Ret(unexpect, exp.error());
+}
+
+template <class Exp, class F,
+ detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
+ class Ret = decltype(detail::invoke(std::declval<F>()))>
+constexpr auto and_then_impl(Exp &&exp, F &&f) {
+ static_assert(detail::is_expected<Ret>::value, "F must return an expected");
+
+ return exp.has_value() ? detail::invoke(std::forward<F>(f))
+ : Ret(unexpect, exp.error());
+}
+#else
+template <class> struct TC;
+template <class Exp, class F,
+ class Ret = decltype(detail::invoke(std::declval<F>(),
+ *std::declval<Exp>())),
+ detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr>
+auto and_then_impl(Exp &&exp, F &&f) -> Ret {
+ static_assert(detail::is_expected<Ret>::value, "F must return an expected");
+
+ return exp.has_value()
+ ? detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp))
+ : Ret(unexpect, exp.error());
+}
+
+template <class Exp, class F,
+ class Ret = decltype(detail::invoke(std::declval<F>())),
+ detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr>
+constexpr auto and_then_impl(Exp &&exp, F &&f) -> Ret {
+ static_assert(detail::is_expected<Ret>::value, "F must return an expected");
+
+ return exp.has_value() ? detail::invoke(std::forward<F>(f))
+ : Ret(unexpect, exp.error());
+}
+#endif
+
+#ifdef TL_EXPECTED_CXX14
+template <class Exp, class F,
+ detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
+ class Ret = decltype(detail::invoke(std::declval<F>(),
+ *std::declval<Exp>())),
+ detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
+constexpr auto expected_map_impl(Exp &&exp, F &&f) {
+ using result = ret_t<Exp, detail::decay_t<Ret>>;
+ return exp.has_value() ? result(detail::invoke(std::forward<F>(f),
+ *std::forward<Exp>(exp)))
+ : result(unexpect, std::forward<Exp>(exp).error());
+}
+
+template <class Exp, class F,
+ detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
+ class Ret = decltype(detail::invoke(std::declval<F>(),
+ *std::declval<Exp>())),
+ detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
+auto expected_map_impl(Exp &&exp, F &&f) {
+ using result = expected<void, err_t<Exp>>;
+ if (exp.has_value()) {
+ detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp));
+ return result();
+ }
+
+ return result(unexpect, std::forward<Exp>(exp).error());
+}
+
+template <class Exp, class F,
+ detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
+ class Ret = decltype(detail::invoke(std::declval<F>())),
+ detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
+constexpr auto expected_map_impl(Exp &&exp, F &&f) {
+ using result = ret_t<Exp, detail::decay_t<Ret>>;
+ return exp.has_value() ? result(detail::invoke(std::forward<F>(f)))
+ : result(unexpect, std::forward<Exp>(exp).error());
+}
+
+template <class Exp, class F,
+ detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
+ class Ret = decltype(detail::invoke(std::declval<F>())),
+ detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
+auto expected_map_impl(Exp &&exp, F &&f) {
+ using result = expected<void, err_t<Exp>>;
+ if (exp.has_value()) {
+ detail::invoke(std::forward<F>(f));
+ return result();
+ }
+
+ return result(unexpect, std::forward<Exp>(exp).error());
+}
+#else
+template <class Exp, class F,
+ detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
+ class Ret = decltype(detail::invoke(std::declval<F>(),
+ *std::declval<Exp>())),
+ detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
+
+constexpr auto expected_map_impl(Exp &&exp, F &&f)
+ -> ret_t<Exp, detail::decay_t<Ret>> {
+ using result = ret_t<Exp, detail::decay_t<Ret>>;
+
+ return exp.has_value() ? result(detail::invoke(std::forward<F>(f),
+ *std::forward<Exp>(exp)))
+ : result(unexpect, std::forward<Exp>(exp).error());
+}
+
+template <class Exp, class F,
+ detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
+ class Ret = decltype(detail::invoke(std::declval<F>(),
+ *std::declval<Exp>())),
+ detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
+
+auto expected_map_impl(Exp &&exp, F &&f) -> expected<void, err_t<Exp>> {
+ if (exp.has_value()) {
+ detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp));
+ return {};
+ }
+
+ return unexpected<err_t<Exp>>(std::forward<Exp>(exp).error());
+}
+
+template <class Exp, class F,
+ detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
+ class Ret = decltype(detail::invoke(std::declval<F>())),
+ detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
+
+constexpr auto expected_map_impl(Exp &&exp, F &&f)
+ -> ret_t<Exp, detail::decay_t<Ret>> {
+ using result = ret_t<Exp, detail::decay_t<Ret>>;
+
+ return exp.has_value() ? result(detail::invoke(std::forward<F>(f)))
+ : result(unexpect, std::forward<Exp>(exp).error());
+}
+
+template <class Exp, class F,
+ detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
+ class Ret = decltype(detail::invoke(std::declval<F>())),
+ detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
+
+auto expected_map_impl(Exp &&exp, F &&f) -> expected<void, err_t<Exp>> {
+ if (exp.has_value()) {
+ detail::invoke(std::forward<F>(f));
+ return {};
+ }
+
+ return unexpected<err_t<Exp>>(std::forward<Exp>(exp).error());
+}
+#endif
+
+#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
+ !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
+template <class Exp, class F,
+ detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
+ class Ret = decltype(detail::invoke(std::declval<F>(),
+ std::declval<Exp>().error())),
+ detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
+constexpr auto map_error_impl(Exp &&exp, F &&f) {
+ using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
+ return exp.has_value()
+ ? result(*std::forward<Exp>(exp))
+ : result(unexpect, detail::invoke(std::forward<F>(f),
+ std::forward<Exp>(exp).error()));
+}
+template <class Exp, class F,
+ detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
+ class Ret = decltype(detail::invoke(std::declval<F>(),
+ std::declval<Exp>().error())),
+ detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
+auto map_error_impl(Exp &&exp, F &&f) {
+ using result = expected<exp_t<Exp>, monostate>;
+ if (exp.has_value()) {
+ return result(*std::forward<Exp>(exp));
+ }
+
+ detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
+ return result(unexpect, monostate{});
+}
+template <class Exp, class F,
+ detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
+ class Ret = decltype(detail::invoke(std::declval<F>(),
+ std::declval<Exp>().error())),
+ detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
+constexpr auto map_error_impl(Exp &&exp, F &&f) {
+ using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
+ return exp.has_value()
+ ? result()
+ : result(unexpect, detail::invoke(std::forward<F>(f),
+ std::forward<Exp>(exp).error()));
+}
+template <class Exp, class F,
+ detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
+ class Ret = decltype(detail::invoke(std::declval<F>(),
+ std::declval<Exp>().error())),
+ detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
+auto map_error_impl(Exp &&exp, F &&f) {
+ using result = expected<exp_t<Exp>, monostate>;
+ if (exp.has_value()) {
+ return result();
+ }
+
+ detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
+ return result(unexpect, monostate{});
+}
+#else
+template <class Exp, class F,
+ detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
+ class Ret = decltype(detail::invoke(std::declval<F>(),
+ std::declval<Exp>().error())),
+ detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
+constexpr auto map_error_impl(Exp &&exp, F &&f)
+ -> expected<exp_t<Exp>, detail::decay_t<Ret>> {
+ using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
+
+ return exp.has_value()
+ ? result(*std::forward<Exp>(exp))
+ : result(unexpect, detail::invoke(std::forward<F>(f),
+ std::forward<Exp>(exp).error()));
+}
+
+template <class Exp, class F,
+ detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
+ class Ret = decltype(detail::invoke(std::declval<F>(),
+ std::declval<Exp>().error())),
+ detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
+auto map_error_impl(Exp &&exp, F &&f) -> expected<exp_t<Exp>, monostate> {
+ using result = expected<exp_t<Exp>, monostate>;
+ if (exp.has_value()) {
+ return result(*std::forward<Exp>(exp));
+ }
+
+ detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
+ return result(unexpect, monostate{});
+}
+
+template <class Exp, class F,
+ detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
+ class Ret = decltype(detail::invoke(std::declval<F>(),
+ std::declval<Exp>().error())),
+ detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
+constexpr auto map_error_impl(Exp &&exp, F &&f)
+ -> expected<exp_t<Exp>, detail::decay_t<Ret>> {
+ using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
+
+ return exp.has_value()
+ ? result()
+ : result(unexpect, detail::invoke(std::forward<F>(f),
+ std::forward<Exp>(exp).error()));
+}
+
+template <class Exp, class F,
+ detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
+ class Ret = decltype(detail::invoke(std::declval<F>(),
+ std::declval<Exp>().error())),
+ detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
+auto map_error_impl(Exp &&exp, F &&f) -> expected<exp_t<Exp>, monostate> {
+ using result = expected<exp_t<Exp>, monostate>;
+ if (exp.has_value()) {
+ return result();
+ }
+
+ detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
+ return result(unexpect, monostate{});
+}
+#endif
+
+#ifdef TL_EXPECTED_CXX14
+template <class Exp, class F,
+ class Ret = decltype(detail::invoke(std::declval<F>(),
+ std::declval<Exp>().error())),
+ detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
+constexpr auto or_else_impl(Exp &&exp, F &&f) {
+ static_assert(detail::is_expected<Ret>::value, "F must return an expected");
+ return exp.has_value()
+ ? std::forward<Exp>(exp)
+ : detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
+}
+
+template <class Exp, class F,
+ class Ret = decltype(detail::invoke(std::declval<F>(),
+ std::declval<Exp>().error())),
+ detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
+detail::decay_t<Exp> or_else_impl(Exp &&exp, F &&f) {
+ return exp.has_value()
+ ? std::forward<Exp>(exp)
+ : (detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error()),
+ std::forward<Exp>(exp));
+}
+#else
+template <class Exp, class F,
+ class Ret = decltype(detail::invoke(std::declval<F>(),
+ std::declval<Exp>().error())),
+ detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
+auto or_else_impl(Exp &&exp, F &&f) -> Ret {
+ static_assert(detail::is_expected<Ret>::value, "F must return an expected");
+ return exp.has_value()
+ ? std::forward<Exp>(exp)
+ : detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
+}
+
+template <class Exp, class F,
+ class Ret = decltype(detail::invoke(std::declval<F>(),
+ std::declval<Exp>().error())),
+ detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
+detail::decay_t<Exp> or_else_impl(Exp &&exp, F &&f) {
+ return exp.has_value()
+ ? std::forward<Exp>(exp)
+ : (detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error()),
+ std::forward<Exp>(exp));
+}
+#endif
+} // namespace detail
+
+template <class T, class E, class U, class F>
+constexpr bool operator==(const expected<T, E> &lhs,
+ const expected<U, F> &rhs) {
+ return (lhs.has_value() != rhs.has_value())
+ ? false
+ : (!lhs.has_value() ? lhs.error() == rhs.error() : *lhs == *rhs);
+}
+template <class T, class E, class U, class F>
+constexpr bool operator!=(const expected<T, E> &lhs,
+ const expected<U, F> &rhs) {
+ return (lhs.has_value() != rhs.has_value())
+ ? true
+ : (!lhs.has_value() ? lhs.error() != rhs.error() : *lhs != *rhs);
+}
+
+template <class T, class E, class U>
+constexpr bool operator==(const expected<T, E> &x, const U &v) {
+ return x.has_value() ? *x == v : false;
+}
+template <class T, class E, class U>
+constexpr bool operator==(const U &v, const expected<T, E> &x) {
+ return x.has_value() ? *x == v : false;
+}
+template <class T, class E, class U>
+constexpr bool operator!=(const expected<T, E> &x, const U &v) {
+ return x.has_value() ? *x != v : true;
+}
+template <class T, class E, class U>
+constexpr bool operator!=(const U &v, const expected<T, E> &x) {
+ return x.has_value() ? *x != v : true;
+}
+
+template <class T, class E>
+constexpr bool operator==(const expected<T, E> &x, const unexpected<E> &e) {
+ return x.has_value() ? false : x.error() == e.value();
+}
+template <class T, class E>
+constexpr bool operator==(const unexpected<E> &e, const expected<T, E> &x) {
+ return x.has_value() ? false : x.error() == e.value();
+}
+template <class T, class E>
+constexpr bool operator!=(const expected<T, E> &x, const unexpected<E> &e) {
+ return x.has_value() ? true : x.error() != e.value();
+}
+template <class T, class E>
+constexpr bool operator!=(const unexpected<E> &e, const expected<T, E> &x) {
+ return x.has_value() ? true : x.error() != e.value();
+}
+
+// TODO is_swappable
+template <class T, class E,
+ detail::enable_if_t<std::is_move_constructible<T>::value &&
+ std::is_move_constructible<E>::value> * = nullptr>
+void swap(expected<T, E> &lhs,
+ expected<T, E> &rhs) noexcept(noexcept(lhs.swap(rhs))) {
+ lhs.swap(rhs);
+}
+} // namespace tl
+
+#define TL_OPTIONAL_EXPECTED_MUTEX
+#endif
diff --git a/src/include/filepath.h b/src/include/filepath.h
new file mode 100644
index 000000000..d0965ad0c
--- /dev/null
+++ b/src/include/filepath.h
@@ -0,0 +1,250 @@
+// -*- 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-2006 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_FILEPATH_H
+#define CEPH_FILEPATH_H
+
+/*
+ * BUG: /a/b/c is equivalent to a/b/c in dentry-breakdown, but not string.
+ * -> should it be different? how? should this[0] be "", with depth 4?
+ *
+ */
+
+
+#include <iosfwd>
+#include <string>
+#include <string_view>
+#include <vector>
+
+#include "buffer.h"
+#include "encoding.h"
+#include "include/types.h"
+#include "include/fs_types.h"
+
+#include "common/Formatter.h"
+
+
+class filepath {
+ inodeno_t ino = 0; // base inode. ino=0 implies pure relative path.
+ std::string path; // relative path.
+
+ /** bits - path segments
+ * this is ['a', 'b', 'c'] for both the aboslute and relative case.
+ *
+ * NOTE: this value is LAZILY maintained... i.e. it's a cache
+ */
+ mutable std::vector<std::string> bits;
+ bool encoded = false;
+
+ void rebuild_path() {
+ path.clear();
+ for (unsigned i=0; i<bits.size(); i++) {
+ if (i) path += "/";
+ path += bits[i];
+ }
+ }
+ void parse_bits() const {
+ bits.clear();
+ int off = 0;
+ while (off < (int)path.length()) {
+ int nextslash = path.find('/', off);
+ if (nextslash < 0)
+ nextslash = path.length(); // no more slashes
+ if (((nextslash - off) > 0) || encoded) {
+ // skip empty components unless they were introduced deliberately
+ // see commit message for more detail
+ bits.push_back( path.substr(off,nextslash-off) );
+ }
+ off = nextslash+1;
+ }
+ }
+
+ public:
+ filepath() = default;
+ filepath(std::string_view p, inodeno_t i) : ino(i), path(p) {}
+ filepath(const filepath& o) {
+ ino = o.ino;
+ path = o.path;
+ bits = o.bits;
+ encoded = o.encoded;
+ }
+ filepath(inodeno_t i) : ino(i) {}
+ filepath& operator=(const char* path) {
+ set_path(path);
+ return *this;
+ }
+
+ /*
+ * if we are fed a relative path as a string, either set ino=0 (strictly
+ * relative) or 1 (absolute). throw out any leading '/'.
+ */
+ filepath(std::string_view s) { set_path(s); }
+ filepath(const char* s) { set_path(s); }
+
+ void set_path(std::string_view s, inodeno_t b) {
+ path = s;
+ ino = b;
+ }
+ void set_path(std::string_view s) {
+ if (s[0] == '/') {
+ path = s.substr(1);
+ ino = 1;
+ } else {
+ ino = 0;
+ path = s;
+ }
+ bits.clear();
+ }
+
+
+ // accessors
+ inodeno_t get_ino() const { return ino; }
+ const std::string& get_path() const { return path; }
+ const char *c_str() const { return path.c_str(); }
+
+ int length() const { return path.length(); }
+ unsigned depth() const {
+ if (bits.empty() && path.length() > 0) parse_bits();
+ return bits.size();
+ }
+ bool empty() const { return path.length() == 0 && ino == 0; }
+
+ bool absolute() const { return ino == 1; }
+ bool pure_relative() const { return ino == 0; }
+ bool ino_relative() const { return ino > 0; }
+
+ const std::string& operator[](int i) const {
+ if (bits.empty() && path.length() > 0) parse_bits();
+ return bits[i];
+ }
+
+ const std::string& last_dentry() const {
+ if (bits.empty() && path.length() > 0) parse_bits();
+ ceph_assert(!bits.empty());
+ return bits[ bits.size()-1 ];
+ }
+
+ filepath prefixpath(int s) const {
+ filepath t(ino);
+ for (int i=0; i<s; i++)
+ t.push_dentry(bits[i]);
+ return t;
+ }
+ filepath postfixpath(int s) const {
+ filepath t;
+ for (unsigned i=s; i<bits.size(); i++)
+ t.push_dentry(bits[i]);
+ return t;
+ }
+
+
+ // modifiers
+ // string can be relative "a/b/c" (ino=0) or absolute "/a/b/c" (ino=1)
+ void _set_ino(inodeno_t i) { ino = i; }
+ void clear() {
+ ino = 0;
+ path = "";
+ bits.clear();
+ }
+
+ void pop_dentry() {
+ if (bits.empty() && path.length() > 0)
+ parse_bits();
+ bits.pop_back();
+ rebuild_path();
+ }
+ void push_dentry(std::string_view s) {
+ if (bits.empty() && path.length() > 0)
+ parse_bits();
+ if (!bits.empty())
+ path += "/";
+ path += s;
+ bits.emplace_back(s);
+ }
+ void push_dentry(const std::string& s) {
+ push_dentry(std::string_view(s));
+ }
+ void push_dentry(const char *cs) {
+ push_dentry(std::string_view(cs, strlen(cs)));
+ }
+ void push_front_dentry(const std::string& s) {
+ bits.insert(bits.begin(), s);
+ rebuild_path();
+ }
+ void append(const filepath& a) {
+ ceph_assert(a.pure_relative());
+ for (unsigned i=0; i<a.depth(); i++)
+ push_dentry(a[i]);
+ }
+
+ // encoding
+ void encode(ceph::buffer::list& bl) const {
+ using ceph::encode;
+ __u8 struct_v = 1;
+ encode(struct_v, bl);
+ encode(ino, bl);
+ encode(path, bl);
+ }
+ void decode(ceph::buffer::list::const_iterator& blp) {
+ using ceph::decode;
+ bits.clear();
+ __u8 struct_v;
+ decode(struct_v, blp);
+ decode(ino, blp);
+ decode(path, blp);
+ encoded = true;
+ }
+ void dump(ceph::Formatter *f) const {
+ f->dump_unsigned("base_ino", ino);
+ f->dump_string("relative_path", path);
+ }
+ static void generate_test_instances(std::list<filepath*>& o) {
+ o.push_back(new filepath);
+ o.push_back(new filepath("/usr/bin", 0));
+ o.push_back(new filepath("/usr/sbin", 1));
+ o.push_back(new filepath("var/log", 1));
+ o.push_back(new filepath("foo/bar", 101));
+ }
+
+ bool is_last_dot_or_dotdot() const {
+ if (depth() > 0) {
+ std::string dname = last_dentry();
+ if (dname == "." || dname == "..") {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ bool is_last_snap() const {
+ // walk into snapdir?
+ return depth() > 0 && bits[0].length() == 0;
+ }
+};
+
+WRITE_CLASS_ENCODER(filepath)
+
+inline std::ostream& operator<<(std::ostream& out, const filepath& path)
+{
+ if (path.get_ino()) {
+ out << '#' << path.get_ino();
+ if (path.length())
+ out << '/';
+ }
+ return out << path.get_path();
+}
+
+#endif
diff --git a/src/include/frag.h b/src/include/frag.h
new file mode 100644
index 000000000..ec18bddfb
--- /dev/null
+++ b/src/include/frag.h
@@ -0,0 +1,615 @@
+// -*- 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-2006 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_FRAG_H
+#define CEPH_FRAG_H
+
+#include <boost/container/small_vector.hpp>
+
+#include <iostream>
+
+#include <stdint.h>
+#include <stdio.h>
+
+#include "buffer.h"
+#include "compact_map.h"
+
+#include "ceph_frag.h"
+#include "include/encoding.h"
+#include "include/ceph_assert.h"
+
+#include "common/dout.h"
+
+/*
+ *
+ * the goal here is to use a binary split strategy to partition a namespace.
+ * frag_t represents a particular fragment. bits() tells you the size of the
+ * fragment, and value() it's name. this is roughly analogous to an ip address
+ * and netmask.
+ *
+ * fragtree_t represents an entire namespace and it's partition. it essentially
+ * tells you where fragments are split into other fragments, and by how much
+ * (i.e. by how many bits, resulting in a power of 2 number of child fragments).
+ *
+ * this vaguely resembles a btree, in that when a fragment becomes large or small
+ * we can split or merge, except that there is no guarantee of being balanced.
+ *
+ * presumably we are partitioning the output of a (perhaps specialized) hash
+ * function.
+ */
+
+/**
+ * frag_t
+ *
+ * description of an individual fragment. that is, a particular piece
+ * of the overall namespace.
+ *
+ * this is conceptually analogous to an ip address and netmask.
+ *
+ * a value v falls "within" fragment f iff (v & f.mask()) == f.value().
+ *
+ * we write it as v/b, where v is a value and b is the number of bits.
+ * 0/0 (bits==0) corresponds to the entire namespace. if we bisect that,
+ * we get 0/1 and 1/1. quartering gives us 0/2, 1/2, 2/2, 3/2. and so on.
+ *
+ * this makes the right most bit of v the "most significant", which is the
+ * opposite of what we usually see.
+ */
+
+/*
+ * TODO:
+ * - get_first_child(), next_sibling(int parent_bits) to make (possibly partial)
+ * iteration efficient (see, e.g., try_assimilate_children()
+ * - rework frag_t so that we mask the left-most (most significant) bits instead of
+ * the right-most (least significant) bits. just because it's more intuitive, and
+ * matches the network/netmask concept.
+ */
+
+class frag_t {
+ /*
+ * encoding is dictated by frag_* functions in ceph_fs.h. use those
+ * helpers _exclusively_.
+ */
+public:
+ using _frag_t = uint32_t;
+
+ frag_t() = default;
+ frag_t(unsigned v, unsigned b) : _enc(ceph_frag_make(b, v)) { }
+ frag_t(_frag_t e) : _enc(e) { }
+
+ // constructors
+ void from_unsigned(unsigned e) { _enc = e; }
+
+ // accessors
+ unsigned value() const { return ceph_frag_value(_enc); }
+ unsigned bits() const { return ceph_frag_bits(_enc); }
+ unsigned mask() const { return ceph_frag_mask(_enc); }
+ unsigned mask_shift() const { return ceph_frag_mask_shift(_enc); }
+
+ operator _frag_t() const { return _enc; }
+
+ // tests
+ bool contains(unsigned v) const { return ceph_frag_contains_value(_enc, v); }
+ bool contains(frag_t sub) const { return ceph_frag_contains_frag(_enc, sub._enc); }
+ bool is_root() const { return bits() == 0; }
+ frag_t parent() const {
+ ceph_assert(bits() > 0);
+ return frag_t(ceph_frag_parent(_enc));
+ }
+
+ // splitting
+ frag_t make_child(int i, int nb) const {
+ ceph_assert(i < (1<<nb));
+ return frag_t(ceph_frag_make_child(_enc, nb, i));
+ }
+ template<typename T>
+ void split(int nb, T& fragments) const {
+ ceph_assert(nb > 0);
+ unsigned nway = 1 << nb;
+ for (unsigned i=0; i<nway; i++)
+ fragments.push_back(make_child(i, nb));
+ }
+
+ // binary splitting
+ frag_t left_child() const { return frag_t(ceph_frag_left_child(_enc)); }
+ frag_t right_child() const { return frag_t(ceph_frag_right_child(_enc)); }
+
+ bool is_left() const { return ceph_frag_is_left_child(_enc); }
+ bool is_right() const { return ceph_frag_is_right_child(_enc); }
+ frag_t get_sibling() const {
+ ceph_assert(!is_root());
+ return frag_t(ceph_frag_sibling(_enc));
+ }
+
+ // sequencing
+ bool is_leftmost() const { return ceph_frag_is_leftmost(_enc); }
+ bool is_rightmost() const { return ceph_frag_is_rightmost(_enc); }
+ frag_t next() const {
+ ceph_assert(!is_rightmost());
+ return frag_t(ceph_frag_next(_enc));
+ }
+
+ // parse
+ bool parse(const char *s) {
+ int pvalue, pbits;
+ int r = sscanf(s, "%x/%d", &pvalue, &pbits);
+ if (r == 2) {
+ *this = frag_t(pvalue, pbits);
+ return true;
+ }
+ return false;
+ }
+
+ void encode(ceph::buffer::list& bl) const {
+ ceph::encode_raw(_enc, bl);
+ }
+ void decode(ceph::buffer::list::const_iterator& p) {
+ __u32 v;
+ ceph::decode_raw(v, p);
+ _enc = v;
+ }
+ bool operator<(const frag_t& b) const
+ {
+ if (value() != b.value())
+ return value() < b.value();
+ else
+ return bits() < b.bits();
+ }
+private:
+ _frag_t _enc = 0;
+};
+WRITE_CLASS_ENCODER(frag_t)
+
+inline std::ostream& operator<<(std::ostream& out, const frag_t& hb)
+{
+ //out << std::hex << hb.value() << std::dec << "/" << hb.bits() << '=';
+ unsigned num = hb.bits();
+ if (num) {
+ unsigned val = hb.value();
+ for (unsigned bit = 23; num; num--, bit--)
+ out << ((val & (1<<bit)) ? '1':'0');
+ }
+ return out << '*';
+}
+
+
+using frag_vec_t = boost::container::small_vector<frag_t, 4>;
+
+/**
+ * fragtree_t -- partition an entire namespace into one or more frag_t's.
+ */
+class fragtree_t {
+ // pairs <f, b>:
+ // frag_t f is split by b bits.
+ // if child frag_t does not appear, it is not split.
+public:
+ compact_map<frag_t,int32_t> _splits;
+
+public:
+ // -------------
+ // basics
+ void swap(fragtree_t& other) {
+ _splits.swap(other._splits);
+ }
+ void clear() {
+ _splits.clear();
+ }
+
+ // -------------
+ // accessors
+ bool empty() const {
+ return _splits.empty();
+ }
+ int get_split(const frag_t hb) const {
+ compact_map<frag_t,int32_t>::const_iterator p = _splits.find(hb);
+ if (p == _splits.end())
+ return 0;
+ else
+ return p->second;
+ }
+
+
+ bool is_leaf(frag_t x) const {
+ frag_vec_t s;
+ get_leaves_under(x, s);
+ //generic_dout(10) << "is_leaf(" << x << ") -> " << ls << dendl;
+ return s.size() == 1 && s.front() == x;
+ }
+
+ /**
+ * get_leaves -- list all leaves
+ */
+ template<typename T>
+ void get_leaves(T& c) const {
+ return get_leaves_under_split(frag_t(), c);
+ }
+
+ /**
+ * get_leaves_under_split -- list all leaves under a known split point (or root)
+ */
+ template<typename T>
+ void get_leaves_under_split(frag_t under, T& c) const {
+ frag_vec_t s;
+ s.push_back(under);
+ while (!s.empty()) {
+ frag_t t = s.back();
+ s.pop_back();
+ int nb = get_split(t);
+ if (nb)
+ t.split(nb, s); // queue up children
+ else
+ c.push_back(t); // not spit, it's a leaf.
+ }
+ }
+
+ /**
+ * get_branch -- get branch point at OR above frag @a x
+ * - may be @a x itself, if @a x is a split
+ * - may be root (frag_t())
+ */
+ frag_t get_branch(frag_t x) const {
+ while (1) {
+ if (x == frag_t()) return x; // root
+ if (get_split(x)) return x; // found it!
+ x = x.parent();
+ }
+ }
+
+ /**
+ * get_branch_above -- get a branch point above frag @a x
+ * - may be root (frag_t())
+ * - may NOT be @a x, even if @a x is a split.
+ */
+ frag_t get_branch_above(frag_t x) const {
+ while (1) {
+ if (x == frag_t()) return x; // root
+ x = x.parent();
+ if (get_split(x)) return x; // found it!
+ }
+ }
+
+
+ /**
+ * get_branch_or_leaf -- get branch or leaf point parent for frag @a x
+ * - may be @a x itself, if @a x is a split or leaf
+ * - may be root (frag_t())
+ */
+ frag_t get_branch_or_leaf(frag_t x) const {
+ frag_t branch = get_branch(x);
+ int nb = get_split(branch);
+ if (nb > 0 && // if branch is a split, and
+ branch.bits() + nb <= x.bits()) // one of the children is or contains x
+ return frag_t(x.value(), branch.bits()+nb); // then return that child (it's a leaf)
+ else
+ return branch;
+ }
+
+ /**
+ * get_leaves_under(x, ls) -- search for any leaves fully contained by x
+ */
+ template<typename T>
+ void get_leaves_under(frag_t x, T& c) const {
+ frag_vec_t s;
+ s.push_back(get_branch_or_leaf(x));
+ while (!s.empty()) {
+ frag_t t = s.back();
+ s.pop_back();
+ if (t.bits() >= x.bits() && // if t is more specific than x, and
+ !x.contains(t)) // x does not contain t,
+ continue; // then skip
+ int nb = get_split(t);
+ if (nb)
+ t.split(nb, s); // queue up children
+ else if (x.contains(t))
+ c.push_back(t); // not spit, it's a leaf.
+ }
+ }
+
+ /**
+ * contains(fg) -- does fragtree contain the specific frag @a x
+ */
+ bool contains(frag_t x) const {
+ frag_vec_t s;
+ s.push_back(get_branch(x));
+ while (!s.empty()) {
+ frag_t t = s.back();
+ s.pop_back();
+ if (t.bits() >= x.bits() && // if t is more specific than x, and
+ !x.contains(t)) // x does not contain t,
+ continue; // then skip
+ int nb = get_split(t);
+ if (nb) {
+ if (t == x) return false; // it's split.
+ t.split(nb, s); // queue up children
+ } else {
+ if (t == x) return true; // it's there.
+ }
+ }
+ return false;
+ }
+
+ /**
+ * operator[] -- map a (hash?) value to a frag
+ */
+ frag_t operator[](unsigned v) const {
+ frag_t t;
+ while (1) {
+ ceph_assert(t.contains(v));
+ int nb = get_split(t);
+
+ // is this a leaf?
+ if (nb == 0) return t; // done.
+
+ // pick appropriate child fragment.
+ unsigned nway = 1 << nb;
+ unsigned i;
+ for (i=0; i<nway; i++) {
+ frag_t n = t.make_child(i, nb);
+ if (n.contains(v)) {
+ t = n;
+ break;
+ }
+ }
+ ceph_assert(i < nway);
+ }
+ }
+
+
+ // ---------------
+ // modifiers
+ void split(frag_t x, int b, bool simplify=true) {
+ ceph_assert(is_leaf(x));
+ _splits[x] = b;
+
+ if (simplify)
+ try_assimilate_children(get_branch_above(x));
+ }
+ void merge(frag_t x, int b, bool simplify=true) {
+ ceph_assert(!is_leaf(x));
+ ceph_assert(_splits[x] == b);
+ _splits.erase(x);
+
+ if (simplify)
+ try_assimilate_children(get_branch_above(x));
+ }
+
+ /*
+ * if all of a given split's children are identically split,
+ * then the children can be assimilated.
+ */
+ void try_assimilate_children(frag_t x) {
+ int nb = get_split(x);
+ if (!nb) return;
+ frag_vec_t children;
+ x.split(nb, children);
+ int childbits = 0;
+ for (auto& frag : children) {
+ int cb = get_split(frag);
+ if (!cb) return; // nope.
+ if (childbits && cb != childbits) return; // not the same
+ childbits = cb;
+ }
+ // all children are split with childbits!
+ for (auto& frag : children)
+ _splits.erase(frag);
+ _splits[x] += childbits;
+ }
+
+ bool force_to_leaf(CephContext *cct, frag_t x) {
+ if (is_leaf(x))
+ return false;
+
+ lgeneric_dout(cct, 10) << "force_to_leaf " << x << " on " << _splits << dendl;
+
+ frag_t parent = get_branch_or_leaf(x);
+ ceph_assert(parent.bits() <= x.bits());
+ lgeneric_dout(cct, 10) << "parent is " << parent << dendl;
+
+ // do we need to split from parent to x?
+ if (parent.bits() < x.bits()) {
+ int spread = x.bits() - parent.bits();
+ int nb = get_split(parent);
+ lgeneric_dout(cct, 10) << "spread " << spread << ", parent splits by " << nb << dendl;
+ if (nb == 0) {
+ // easy: split parent (a leaf) by the difference
+ lgeneric_dout(cct, 10) << "splitting parent " << parent << " by spread " << spread << dendl;
+ split(parent, spread);
+ ceph_assert(is_leaf(x));
+ return true;
+ }
+ ceph_assert(nb > spread);
+
+ // add an intermediary split
+ merge(parent, nb, false);
+ split(parent, spread, false);
+
+ frag_vec_t subs;
+ parent.split(spread, subs);
+ for (auto& frag : subs) {
+ lgeneric_dout(cct, 10) << "splitting intermediate " << frag << " by " << (nb-spread) << dendl;
+ split(frag, nb - spread, false);
+ }
+ }
+
+ // x is now a leaf or split.
+ // hoover up any children.
+ frag_vec_t s;
+ s.push_back(x);
+ while (!s.empty()) {
+ frag_t t = s.back();
+ s.pop_back();
+ int nb = get_split(t);
+ if (nb) {
+ lgeneric_dout(cct, 10) << "merging child " << t << " by " << nb << dendl;
+ merge(t, nb, false); // merge this point, and
+ t.split(nb, s); // queue up children
+ }
+ }
+
+ lgeneric_dout(cct, 10) << "force_to_leaf done" << dendl;
+ ceph_assert(is_leaf(x));
+ return true;
+ }
+
+ // encoding
+ void encode(ceph::buffer::list& bl) const {
+ using ceph::encode;
+ encode(_splits, bl);
+ }
+ void decode(ceph::buffer::list::const_iterator& p) {
+ using ceph::decode;
+ decode(_splits, p);
+ }
+ void encode_nohead(ceph::buffer::list& bl) const {
+ using ceph::encode;
+ for (compact_map<frag_t,int32_t>::const_iterator p = _splits.begin();
+ p != _splits.end();
+ ++p) {
+ encode(p->first, bl);
+ encode(p->second, bl);
+ }
+ }
+ void decode_nohead(int n, ceph::buffer::list::const_iterator& p) {
+ using ceph::decode;
+ _splits.clear();
+ while (n-- > 0) {
+ frag_t f;
+ decode(f, p);
+ decode(_splits[f], p);
+ }
+ }
+
+ void print(std::ostream& out) {
+ out << "fragtree_t(";
+ frag_vec_t s;
+ s.push_back(frag_t());
+ while (!s.empty()) {
+ frag_t t = s.back();
+ s.pop_back();
+ // newline + indent?
+ if (t.bits()) {
+ out << std::endl;
+ for (unsigned i=0; i<t.bits(); i++) out << ' ';
+ }
+ int nb = get_split(t);
+ if (nb) {
+ out << t << " %" << nb;
+ t.split(nb, s); // queue up children
+ } else {
+ out << t;
+ }
+ }
+ out << ")";
+ }
+
+ void dump(ceph::Formatter *f) const {
+ f->open_array_section("splits");
+ for (auto p = _splits.begin(); p != _splits.end(); ++p) {
+ f->open_object_section("split");
+ std::ostringstream frag_str;
+ frag_str << p->first;
+ f->dump_string("frag", frag_str.str());
+ f->dump_int("children", p->second);
+ f->close_section(); // split
+ }
+ f->close_section(); // splits
+ }
+};
+WRITE_CLASS_ENCODER(fragtree_t)
+
+inline bool operator==(const fragtree_t& l, const fragtree_t& r) {
+ return l._splits == r._splits;
+}
+inline bool operator!=(const fragtree_t& l, const fragtree_t& r) {
+ return l._splits != r._splits;
+}
+
+inline std::ostream& operator<<(std::ostream& out, const fragtree_t& ft)
+{
+ out << "fragtree_t(";
+
+ for (compact_map<frag_t,int32_t>::const_iterator p = ft._splits.begin();
+ p != ft._splits.end();
+ ++p) {
+ if (p != ft._splits.begin())
+ out << " ";
+ out << p->first << "^" << p->second;
+ }
+ return out << ")";
+}
+
+/**
+ * fragset_t -- a set of fragments
+ */
+class fragset_t {
+ std::set<frag_t> _set;
+
+public:
+ const std::set<frag_t> &get() const { return _set; }
+ std::set<frag_t>::const_iterator begin() const { return _set.begin(); }
+ std::set<frag_t>::const_iterator end() const { return _set.end(); }
+
+ bool empty() const { return _set.empty(); }
+
+ bool contains(frag_t f) const {
+ while (1) {
+ if (_set.count(f)) return true;
+ if (f.bits() == 0) return false;
+ f = f.parent();
+ }
+ }
+
+ void clear() {
+ _set.clear();
+ }
+
+ void insert_raw(frag_t f){
+ _set.insert(f);
+ }
+ void insert(frag_t f) {
+ _set.insert(f);
+ simplify();
+ }
+
+ void simplify() {
+ auto it = _set.begin();
+ while (it != _set.end()) {
+ if (!it->is_root() &&
+ _set.count(it->get_sibling())) {
+ _set.erase(it->get_sibling());
+ auto ret = _set.insert(it->parent());
+ _set.erase(it);
+ it = ret.first;
+ } else {
+ ++it;
+ }
+ }
+ }
+
+ void encode(ceph::buffer::list& bl) const {
+ ceph::encode(_set, bl);
+ }
+ void decode(ceph::buffer::list::const_iterator& p) {
+ ceph::decode(_set, p);
+ }
+};
+WRITE_CLASS_ENCODER(fragset_t)
+
+
+inline std::ostream& operator<<(std::ostream& out, const fragset_t& fs)
+{
+ return out << "fragset_t(" << fs.get() << ")";
+}
+
+#endif
diff --git a/src/include/fs_types.h b/src/include/fs_types.h
new file mode 100644
index 000000000..c1932bfcc
--- /dev/null
+++ b/src/include/fs_types.h
@@ -0,0 +1,175 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+#ifndef CEPH_INCLUDE_FS_TYPES_H
+#define CEPH_INCLUDE_FS_TYPES_H
+
+#include "types.h"
+class JSONObj;
+
+#define CEPHFS_EBLOCKLISTED 108
+#define CEPHFS_EPERM 1
+#define CEPHFS_ESTALE 116
+#define CEPHFS_ENOSPC 28
+#define CEPHFS_ETIMEDOUT 110
+#define CEPHFS_EIO 5
+#define CEPHFS_ENOTCONN 107
+#define CEPHFS_EEXIST 17
+#define CEPHFS_EINTR 4
+#define CEPHFS_EINVAL 22
+#define CEPHFS_EBADF 9
+#define CEPHFS_EROFS 30
+#define CEPHFS_EAGAIN 11
+#define CEPHFS_EACCES 13
+#define CEPHFS_ELOOP 40
+#define CEPHFS_EISDIR 21
+#define CEPHFS_ENOENT 2
+#define CEPHFS_ENOTDIR 20
+#define CEPHFS_ENAMETOOLONG 36
+#define CEPHFS_EBUSY 16
+#define CEPHFS_EDQUOT 122
+#define CEPHFS_EFBIG 27
+#define CEPHFS_ERANGE 34
+#define CEPHFS_ENXIO 6
+#define CEPHFS_ECANCELED 125
+#define CEPHFS_ENODATA 61
+#define CEPHFS_EOPNOTSUPP 95
+#define CEPHFS_EXDEV 18
+#define CEPHFS_ENOMEM 12
+#define CEPHFS_ENOTRECOVERABLE 131
+#define CEPHFS_ENOSYS 38
+#define CEPHFS_EWOULDBLOCK CEPHFS_EAGAIN
+#define CEPHFS_ENOTEMPTY 39
+#define CEPHFS_EDEADLK 35
+#define CEPHFS_EDEADLOCK CEPHFS_EDEADLK
+#define CEPHFS_EDOM 33
+#define CEPHFS_EMLINK 31
+#define CEPHFS_ETIME 62
+#define CEPHFS_EOLDSNAPC 85
+#define CEPHFS_EFAULT 14
+#define CEPHFS_EISCONN 106
+#define CEPHFS_EMULTIHOP 72
+
+// taken from linux kernel: include/uapi/linux/fcntl.h
+#define CEPHFS_AT_FDCWD -100 /* Special value used to indicate
+ openat should use the current
+ working directory. */
+
+// --------------------------------------
+// ino
+
+typedef uint64_t _inodeno_t;
+
+struct inodeno_t {
+ _inodeno_t val;
+ inodeno_t() : val(0) {}
+ // cppcheck-suppress noExplicitConstructor
+ inodeno_t(_inodeno_t v) : val(v) {}
+ inodeno_t operator+=(inodeno_t o) { val += o.val; return *this; }
+ operator _inodeno_t() const { return val; }
+
+ void encode(ceph::buffer::list& bl) const {
+ using ceph::encode;
+ encode(val, bl);
+ }
+ void decode(ceph::buffer::list::const_iterator& p) {
+ using ceph::decode;
+ decode(val, p);
+ }
+} __attribute__ ((__may_alias__));
+WRITE_CLASS_ENCODER(inodeno_t)
+
+template<>
+struct denc_traits<inodeno_t> {
+ static constexpr bool supported = true;
+ static constexpr bool featured = false;
+ static constexpr bool bounded = true;
+ static constexpr bool need_contiguous = true;
+ static void bound_encode(const inodeno_t &o, size_t& p) {
+ denc(o.val, p);
+ }
+ static void encode(const inodeno_t &o, ceph::buffer::list::contiguous_appender& p) {
+ denc(o.val, p);
+ }
+ static void decode(inodeno_t& o, ceph::buffer::ptr::const_iterator &p) {
+ denc(o.val, p);
+ }
+};
+
+inline std::ostream& operator<<(std::ostream& out, const inodeno_t& ino) {
+ return out << std::hex << "0x" << ino.val << std::dec;
+}
+
+namespace std {
+template<>
+struct hash<inodeno_t> {
+ size_t operator()( const inodeno_t& x ) const {
+ static rjhash<uint64_t> H;
+ return H(x.val);
+ }
+};
+} // namespace std
+
+
+// file modes
+
+inline bool file_mode_is_readonly(int mode) {
+ return (mode & CEPH_FILE_MODE_WR) == 0;
+}
+
+
+// dentries
+#define MAX_DENTRY_LEN 255
+
+// --
+namespace ceph {
+ class Formatter;
+}
+void dump(const ceph_file_layout& l, ceph::Formatter *f);
+void dump(const ceph_dir_layout& l, ceph::Formatter *f);
+
+
+
+// file_layout_t
+
+struct file_layout_t {
+ // file -> object mapping
+ uint32_t stripe_unit; ///< stripe unit, in bytes,
+ uint32_t stripe_count; ///< over this many objects
+ uint32_t object_size; ///< until objects are this big
+
+ int64_t pool_id; ///< rados pool id
+ std::string pool_ns; ///< rados pool namespace
+
+ file_layout_t(uint32_t su=0, uint32_t sc=0, uint32_t os=0)
+ : stripe_unit(su),
+ stripe_count(sc),
+ object_size(os),
+ pool_id(-1) {
+ }
+
+ bool operator==(const file_layout_t&) const = default;
+
+ static file_layout_t get_default() {
+ return file_layout_t(1<<22, 1, 1<<22);
+ }
+
+ uint64_t get_period() const {
+ return static_cast<uint64_t>(stripe_count) * object_size;
+ }
+
+ void from_legacy(const ceph_file_layout& fl);
+ void to_legacy(ceph_file_layout *fl) const;
+
+ bool is_valid() const;
+
+ void encode(ceph::buffer::list& bl, uint64_t features) const;
+ void decode(ceph::buffer::list::const_iterator& p);
+ void dump(ceph::Formatter *f) const;
+ void decode_json(JSONObj *obj);
+ static void generate_test_instances(std::list<file_layout_t*>& o);
+};
+WRITE_CLASS_ENCODER_FEATURES(file_layout_t)
+
+std::ostream& operator<<(std::ostream& out, const file_layout_t &layout);
+
+#endif
diff --git a/src/include/function2.hpp b/src/include/function2.hpp
new file mode 100644
index 000000000..613e651c7
--- /dev/null
+++ b/src/include/function2.hpp
@@ -0,0 +1,1581 @@
+
+// Copyright 2015-2018 Denis Blank <denis.blank at outlook dot com>
+// Distributed under the Boost Software License, Version 1.0
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef FU2_INCLUDED_FUNCTION2_HPP_
+#define FU2_INCLUDED_FUNCTION2_HPP_
+
+#include <cassert>
+#include <cstdlib>
+#include <memory>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+// Defines:
+// - FU2_HAS_DISABLED_EXCEPTIONS
+#if defined(FU2_WITH_DISABLED_EXCEPTIONS) || \
+ defined(FU2_MACRO_DISABLE_EXCEPTIONS)
+#define FU2_HAS_DISABLED_EXCEPTIONS
+#else // FU2_WITH_DISABLED_EXCEPTIONS
+#if defined(_MSC_VER)
+#if !defined(_HAS_EXCEPTIONS) || (_HAS_EXCEPTIONS == 0)
+#define FU2_HAS_DISABLED_EXCEPTIONS
+#endif
+#elif defined(__clang__)
+#if !(__EXCEPTIONS && __has_feature(cxx_exceptions))
+#define FU2_HAS_DISABLED_EXCEPTIONS
+#endif
+#elif defined(__GNUC__)
+#if !__EXCEPTIONS
+#define FU2_HAS_DISABLED_EXCEPTIONS
+#endif
+#endif
+#endif // FU2_WITH_DISABLED_EXCEPTIONS
+// - FU2_HAS_NO_FUNCTIONAL_HEADER
+#if !defined(FU2_WITH_NO_FUNCTIONAL_HEADER) || \
+ !defined(FU2_NO_FUNCTIONAL_HEADER) || \
+ !defined(FU2_HAS_DISABLED_EXCEPTIONS)
+#define FU2_HAS_NO_FUNCTIONAL_HEADER
+#include <functional>
+#endif
+// - FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE
+#if defined(FU2_WITH_CXX17_NOEXCEPT_FUNCTION_TYPE)
+#define FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE
+#else // FU2_WITH_CXX17_NOEXCEPT_FUNCTION_TYPE
+#if defined(_MSC_VER)
+#if defined(_HAS_CXX17) && _HAS_CXX17
+#define FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE
+#endif
+#elif defined(__cpp_noexcept_function_type)
+#define FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE
+#elif defined(__cplusplus) && (__cplusplus >= 201703L)
+#define FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE
+#endif
+#endif // FU2_WITH_CXX17_NOEXCEPT_FUNCTION_TYPE
+
+#if !defined(FU2_HAS_DISABLED_EXCEPTIONS)
+#include <exception>
+#endif
+
+namespace fu2 {
+inline namespace abi_310 {
+namespace detail {
+template <typename Config, typename Property>
+class function;
+
+template <typename...>
+struct identity {};
+
+// Equivalent to C++17's std::void_t which is targets a bug in GCC,
+// that prevents correct SFINAE behavior.
+// See http://stackoverflow.com/questions/35753920 for details.
+template <typename...>
+struct deduce_to_void : std::common_type<void> {};
+
+template <typename... T>
+using void_t = typename deduce_to_void<T...>::type;
+
+// Copy enabler helper class
+template <bool /*Copyable*/>
+struct copyable {};
+template <>
+struct copyable<false> {
+ copyable() = default;
+ ~copyable() = default;
+ copyable(copyable const&) = delete;
+ copyable(copyable&&) = default;
+ copyable& operator=(copyable const&) = delete;
+ copyable& operator=(copyable&&) = default;
+};
+
+/// Configuration trait to configure the function_base class.
+template <bool Owning, bool Copyable, std::size_t Capacity>
+struct config {
+ // Is true if the function is copyable.
+ static constexpr auto const is_owning = Owning;
+
+ // Is true if the function is copyable.
+ static constexpr auto const is_copyable = Copyable;
+
+ // The internal capacity of the function
+ // used in small functor optimization.
+ static constexpr auto const capacity = Capacity;
+};
+
+/// A config which isn't compatible to other configs
+template <bool Throws, bool HasStrongExceptGuarantee, typename... Args>
+struct property {
+ // Is true when the function throws an exception on empty invocation.
+ static constexpr auto const is_throwing = Throws;
+
+ // Is true when the function throws an exception on empty invocation.
+ static constexpr auto const is_strong_exception_guaranteed = Throws;
+};
+
+/// Provides utilities for invocing callable objects
+namespace invocation {
+/// Invokes the given callable object with the given arguments
+template <typename Callable, typename... Args>
+constexpr auto invoke(Callable&& callable, Args&&... args) noexcept(
+ noexcept(std::forward<Callable>(callable)(std::forward<Args>(args)...)))
+ -> decltype(std::forward<Callable>(callable)(std::forward<Args>(args)...)) {
+
+ return std::forward<Callable>(callable)(std::forward<Args>(args)...);
+}
+/// Invokes the given member function pointer by reference
+template <typename T, typename Type, typename Self, typename... Args>
+constexpr auto invoke(Type T::*member, Self&& self, Args&&... args) noexcept(
+ noexcept((std::forward<Self>(self).*member)(std::forward<Args>(args)...)))
+ -> decltype((std::forward<Self>(self).*
+ member)(std::forward<Args>(args)...)) {
+ return (std::forward<Self>(self).*member)(std::forward<Args>(args)...);
+}
+/// Invokes the given member function pointer by pointer
+template <typename T, typename Type, typename Self, typename... Args>
+constexpr auto invoke(Type T::*member, Self&& self, Args&&... args) noexcept(
+ noexcept((std::forward<Self>(self)->*member)(std::forward<Args>(args)...)))
+ -> decltype(
+ (std::forward<Self>(self)->*member)(std::forward<Args>(args)...)) {
+ return (std::forward<Self>(self)->*member)(std::forward<Args>(args)...);
+}
+/// Invokes the given pointer to a scalar member by reference
+template <typename T, typename Type, typename Self>
+constexpr auto
+invoke(Type T::*member,
+ Self&& self) noexcept(noexcept(std::forward<Self>(self).*member))
+ -> decltype(std::forward<Self>(self).*member) {
+ return (std::forward<Self>(self).*member);
+}
+/// Invokes the given pointer to a scalar member by pointer
+template <typename T, typename Type, typename Self>
+constexpr auto
+invoke(Type T::*member,
+ Self&& self) noexcept(noexcept(std::forward<Self>(self)->*member))
+ -> decltype(std::forward<Self>(self)->*member) {
+ return std::forward<Self>(self)->*member;
+}
+
+/// Deduces to a true type if the callable object can be invoked with
+/// the given arguments.
+/// We don't use invoke here because MSVC can't evaluate the nested expression
+/// SFINAE here.
+template <typename T, typename Args, typename = void>
+struct can_invoke : std::false_type {};
+template <typename T, typename... Args>
+struct can_invoke<T, identity<Args...>,
+ decltype((void)std::declval<T>()(std::declval<Args>()...))>
+ : std::true_type {};
+template <typename Pointer, typename T, typename... Args>
+struct can_invoke<Pointer, identity<T&, Args...>,
+ decltype((void)((std::declval<T&>().*std::declval<Pointer>())(
+ std::declval<Args>()...)))> : std::true_type {};
+template <typename Pointer, typename T, typename... Args>
+struct can_invoke<Pointer, identity<T&&, Args...>,
+ decltype(
+ (void)((std::declval<T&&>().*std::declval<Pointer>())(
+ std::declval<Args>()...)))> : std::true_type {};
+template <typename Pointer, typename T, typename... Args>
+struct can_invoke<Pointer, identity<T*, Args...>,
+ decltype(
+ (void)((std::declval<T*>()->*std::declval<Pointer>())(
+ std::declval<Args>()...)))> : std::true_type {};
+template <typename Pointer, typename T>
+struct can_invoke<Pointer, identity<T&>,
+ decltype((void)(std::declval<T&>().*std::declval<Pointer>()))>
+ : std::true_type {};
+template <typename Pointer, typename T>
+struct can_invoke<Pointer, identity<T&&>,
+ decltype((void)(std::declval<T&&>().*
+ std::declval<Pointer>()))> : std::true_type {
+};
+template <typename Pointer, typename T>
+struct can_invoke<Pointer, identity<T*>,
+ decltype(
+ (void)(std::declval<T*>()->*std::declval<Pointer>()))>
+ : std::true_type {};
+
+template <bool RequiresNoexcept, typename T, typename Args>
+struct is_noexcept_correct : std::true_type {};
+template <typename T, typename... Args>
+struct is_noexcept_correct<true, T, identity<Args...>>
+ : std::integral_constant<bool, noexcept(invoke(std::declval<T>(),
+ std::declval<Args>()...))> {
+};
+} // end namespace invocation
+
+namespace overloading {
+template <typename... Args>
+struct overload_impl;
+template <typename Current, typename Next, typename... Rest>
+struct overload_impl<Current, Next, Rest...> : Current,
+ overload_impl<Next, Rest...> {
+ explicit overload_impl(Current current, Next next, Rest... rest)
+ : Current(std::move(current)), overload_impl<Next, Rest...>(
+ std::move(next), std::move(rest)...) {
+ }
+
+ using Current::operator();
+ using overload_impl<Next, Rest...>::operator();
+};
+template <typename Current>
+struct overload_impl<Current> : Current {
+ explicit overload_impl(Current current) : Current(std::move(current)) {
+ }
+
+ using Current::operator();
+};
+
+template <typename... T>
+constexpr auto overload(T&&... callables) {
+ return overload_impl<std::decay_t<T>...>{std::forward<T>(callables)...};
+}
+} // namespace overloading
+
+/// Declares the namespace which provides the functionality to work with a
+/// type-erased object.
+namespace type_erasure {
+/// Specialization to work with addresses of callable objects
+template <typename T, typename = void>
+struct address_taker {
+ template <typename O>
+ static void* take(O&& obj) {
+ return std::addressof(obj);
+ }
+ static T& restore(void* ptr) {
+ return *static_cast<T*>(ptr);
+ }
+ static T const& restore(void const* ptr) {
+ return *static_cast<T const*>(ptr);
+ }
+ static T volatile& restore(void volatile* ptr) {
+ return *static_cast<T volatile*>(ptr);
+ }
+ static T const volatile& restore(void const volatile* ptr) {
+ return *static_cast<T const volatile*>(ptr);
+ }
+};
+/// Specialization to work with addresses of raw function pointers
+template <typename T>
+struct address_taker<T, std::enable_if_t<std::is_pointer<T>::value>> {
+ template <typename O>
+ static void* take(O&& obj) {
+ return reinterpret_cast<void*>(obj);
+ }
+ template <typename O>
+ static T restore(O ptr) {
+ return reinterpret_cast<T>(const_cast<void*>(ptr));
+ }
+};
+
+template <typename Box>
+struct box_factory;
+/// Store the allocator inside the box
+template <bool IsCopyable, typename T, typename Allocator>
+struct box : private Allocator {
+ friend box_factory<box>;
+
+ T value_;
+
+ explicit box(T value, Allocator allocator)
+ : Allocator(std::move(allocator)), value_(std::move(value)) {
+ }
+
+ box(box&&) = default;
+ box(box const&) = default;
+ box& operator=(box&&) = default;
+ box& operator=(box const&) = default;
+ ~box() = default;
+};
+template <typename T, typename Allocator>
+struct box<false, T, Allocator> : private Allocator {
+ friend box_factory<box>;
+
+ T value_;
+
+ explicit box(T value, Allocator allocator)
+ : Allocator(std::move(allocator)), value_(std::move(value)) {
+ }
+
+ box(box&&) = default;
+ box(box const&) = delete;
+ box& operator=(box&&) = default;
+ box& operator=(box const&) = delete;
+ ~box() = default;
+};
+
+template <bool IsCopyable, typename T, typename Allocator>
+struct box_factory<box<IsCopyable, T, Allocator>> {
+ using real_allocator =
+ typename std::allocator_traits<std::decay_t<Allocator>>::
+ template rebind_alloc<box<IsCopyable, T, Allocator>>;
+
+ /// Allocates space through the boxed allocator
+ static box<IsCopyable, T, Allocator>*
+ box_allocate(box<IsCopyable, T, Allocator> const* me) {
+ real_allocator allocator(*static_cast<Allocator const*>(me));
+
+ return static_cast<box<IsCopyable, T, Allocator>*>(
+ std::allocator_traits<real_allocator>::allocate(allocator, 1U));
+ }
+
+ /// Destroys the box through the given allocator
+ static void box_deallocate(box<IsCopyable, T, Allocator>* me) {
+ real_allocator allocator(*static_cast<Allocator const*>(me));
+
+ me->~box();
+ std::allocator_traits<real_allocator>::deallocate(allocator, me, 1U);
+ }
+};
+
+/// Creates a box containing the given value and allocator
+template <bool IsCopyable, typename T,
+ typename Allocator = std::allocator<std::decay_t<T>>>
+auto make_box(std::integral_constant<bool, IsCopyable>, T&& value,
+ Allocator&& allocator = Allocator{}) {
+ return box<IsCopyable, std::decay_t<T>, std::decay_t<Allocator>>{
+ std::forward<T>(value), std::forward<Allocator>(allocator)};
+}
+
+template <typename T>
+struct is_box : std::false_type {};
+template <bool IsCopyable, typename T, typename Allocator>
+struct is_box<box<IsCopyable, T, Allocator>> : std::true_type {};
+
+/// Provides access to the pointer to a heal allocated erased object
+/// as well to the inplace storage.
+union data_accessor {
+ data_accessor() = default;
+ explicit constexpr data_accessor(std::nullptr_t) noexcept : ptr_(nullptr) {
+ }
+ explicit constexpr data_accessor(void* ptr) noexcept : ptr_(ptr) {
+ }
+
+ /// The pointer we use if the object is on the heap
+ void* ptr_;
+ /// The first field of the inplace storage
+ std::size_t inplace_storage_;
+};
+
+/// See opcode::op_fetch_empty
+constexpr void write_empty(data_accessor* accessor, bool empty) noexcept {
+ accessor->inplace_storage_ = std::size_t(empty);
+}
+
+template <typename From, typename To>
+using transfer_const_t =
+ std::conditional_t<std::is_const<std::remove_pointer_t<From>>::value,
+ std::add_const_t<To>, To>;
+template <typename From, typename To>
+using transfer_volatile_t =
+ std::conditional_t<std::is_volatile<std::remove_pointer_t<From>>::value,
+ std::add_volatile_t<To>, To>;
+
+/// The retriever when the object is allocated inplace
+template <typename T, typename Accessor>
+constexpr auto retrieve(std::true_type /*is_inplace*/, Accessor from,
+ std::size_t from_capacity) {
+ using type = transfer_const_t<Accessor, transfer_volatile_t<Accessor, void>>*;
+
+ /// Process the command by using the data inside the internal capacity
+ auto storage = &(from->inplace_storage_);
+ auto inplace = const_cast<void*>(static_cast<type>(storage));
+ return type(std::align(alignof(T), sizeof(T), inplace, from_capacity));
+}
+
+/// The retriever which is used when the object is allocated
+/// through the allocator
+template <typename T, typename Accessor>
+constexpr auto retrieve(std::false_type /*is_inplace*/, Accessor from,
+ std::size_t /*from_capacity*/) {
+
+ return from->ptr_;
+}
+
+namespace invocation_table {
+#if !defined(FU2_HAS_DISABLED_EXCEPTIONS)
+#if defined(FU2_HAS_NO_FUNCTIONAL_HEADER)
+struct bad_function_call : std::exception {
+ bad_function_call() noexcept {
+ }
+
+ char const* what() const noexcept override {
+ return "bad function call";
+ }
+};
+#elif
+using std::bad_function_call;
+#endif
+#endif
+
+#ifdef FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE
+#define FU2_EXPAND_QUALIFIERS_NOEXCEPT(F) \
+ F(, , noexcept, , &) \
+ F(const, , noexcept, , &) \
+ F(, volatile, noexcept, , &) \
+ F(const, volatile, noexcept, , &) \
+ F(, , noexcept, &, &) \
+ F(const, , noexcept, &, &) \
+ F(, volatile, noexcept, &, &) \
+ F(const, volatile, noexcept, &, &) \
+ F(, , noexcept, &&, &&) \
+ F(const, , noexcept, &&, &&) \
+ F(, volatile, noexcept, &&, &&) \
+ F(const, volatile, noexcept, &&, &&)
+#else // FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE
+#define FU2_EXPAND_QUALIFIERS_NOEXCEPT(F)
+#endif // FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE
+
+#define FU2_EXPAND_QUALIFIERS(F) \
+ F(, , , , &) \
+ F(const, , , , &) \
+ F(, volatile, , , &) \
+ F(const, volatile, , , &) \
+ F(, , , &, &) \
+ F(const, , , &, &) \
+ F(, volatile, , &, &) \
+ F(const, volatile, , &, &) \
+ F(, , , &&, &&) \
+ F(const, , , &&, &&) \
+ F(, volatile, , &&, &&) \
+ F(const, volatile, , &&, &&) \
+ FU2_EXPAND_QUALIFIERS_NOEXCEPT(F)
+
+/// If the function is qualified as noexcept, the call will never throw
+template <bool IsNoexcept>
+[[noreturn]] void throw_or_abortnoexcept(
+ std::integral_constant<bool, IsNoexcept> /*is_throwing*/) noexcept {
+ std::abort();
+}
+/// Calls std::abort on empty function calls
+[[noreturn]] inline void
+throw_or_abort(std::false_type /*is_throwing*/) noexcept {
+ std::abort();
+}
+/// Throws bad_function_call on empty funciton calls
+[[noreturn]] inline void throw_or_abort(std::true_type /*is_throwing*/) {
+#ifdef FU2_HAS_DISABLED_EXCEPTIONS
+ throw_or_abort(std::false_type{});
+#else
+ throw bad_function_call{};
+#endif
+}
+
+template <typename T>
+struct function_trait;
+
+using is_noexcept_ = std::false_type;
+using is_noexcept_noexcept = std::true_type;
+
+#define FU2_DEFINE_FUNCTION_TRAIT(CONST, VOLATILE, NOEXCEPT, OVL_REF, REF) \
+ template <typename Ret, typename... Args> \
+ struct function_trait<Ret(Args...) CONST VOLATILE OVL_REF NOEXCEPT> { \
+ using pointer_type = Ret (*)(data_accessor CONST VOLATILE*, \
+ std::size_t capacity, Args...); \
+ template <typename T, bool IsInplace> \
+ struct internal_invoker { \
+ static Ret invoke(data_accessor CONST VOLATILE* data, \
+ std::size_t capacity, Args... args) NOEXCEPT { \
+ auto obj = retrieve<T>(std::integral_constant<bool, IsInplace>{}, \
+ data, capacity); \
+ auto box = static_cast<T CONST VOLATILE*>(obj); \
+ return invocation::invoke( \
+ static_cast<std::decay_t<decltype(box->value_)> CONST VOLATILE \
+ REF>(box->value_), \
+ std::forward<Args>(args)...); \
+ } \
+ }; \
+ \
+ template <typename T> \
+ struct view_invoker { \
+ static Ret invoke(data_accessor CONST VOLATILE* data, std::size_t, \
+ Args... args) NOEXCEPT { \
+ \
+ auto ptr = static_cast<void CONST VOLATILE*>(data->ptr_); \
+ return invocation::invoke(address_taker<T>::restore(ptr), \
+ std::forward<Args>(args)...); \
+ } \
+ }; \
+ \
+ template <typename T> \
+ using callable = T CONST VOLATILE REF; \
+ \
+ using arguments = identity<Args...>; \
+ \
+ using is_noexcept = is_noexcept_##NOEXCEPT; \
+ \
+ template <bool Throws> \
+ struct empty_invoker { \
+ static Ret invoke(data_accessor CONST VOLATILE* /*data*/, \
+ std::size_t /*capacity*/, Args... /*args*/) NOEXCEPT { \
+ throw_or_abort##NOEXCEPT(std::integral_constant<bool, Throws>{}); \
+ } \
+ }; \
+ };
+
+FU2_EXPAND_QUALIFIERS(FU2_DEFINE_FUNCTION_TRAIT)
+#undef FU2_DEFINE_FUNCTION_TRAIT
+
+/// Deduces to the function pointer to the given signature
+template <typename Signature>
+using function_pointer_of = typename function_trait<Signature>::pointer_type;
+
+template <typename... Args>
+struct invoke_table;
+
+/// We optimize the vtable_t in case there is a single function overload
+template <typename First>
+struct invoke_table<First> {
+ using type = function_pointer_of<First>;
+
+ /// Return the function pointer itself
+ template <std::size_t Index>
+ static constexpr auto fetch(type pointer) noexcept {
+ static_assert(Index == 0U, "The index should be 0 here!");
+ return pointer;
+ }
+
+ /// Returns the thunk of an single overloaded callable
+ template <typename T, bool IsInplace>
+ static constexpr type get_invocation_table_of() noexcept {
+ return &function_trait<First>::template internal_invoker<T,
+ IsInplace>::invoke;
+ }
+ /// Returns the thunk of an single overloaded callable
+ template <typename T>
+ static constexpr type get_invocation_view_table_of() noexcept {
+ return &function_trait<First>::template view_invoker<T>::invoke;
+ }
+ /// Returns the thunk of an empty single overloaded callable
+ template <bool IsThrowing>
+ static constexpr type get_empty_invocation_table() noexcept {
+ return &function_trait<First>::template empty_invoker<IsThrowing>::invoke;
+ }
+};
+/// We generate a table in case of multiple function overloads
+template <typename First, typename Second, typename... Args>
+struct invoke_table<First, Second, Args...> {
+ using type =
+ std::tuple<function_pointer_of<First>, function_pointer_of<Second>,
+ function_pointer_of<Args>...> const*;
+
+ /// Return the function pointer at the particular index
+ template <std::size_t Index>
+ static constexpr auto fetch(type table) noexcept {
+ return std::get<Index>(*table);
+ }
+
+ /// The invocation vtable for a present object
+ template <typename T, bool IsInplace>
+ struct invocation_vtable : public std::tuple<function_pointer_of<First>,
+ function_pointer_of<Second>,
+ function_pointer_of<Args>...> {
+ constexpr invocation_vtable() noexcept
+ : std::tuple<function_pointer_of<First>, function_pointer_of<Second>,
+ function_pointer_of<Args>...>(std::make_tuple(
+ &function_trait<First>::template internal_invoker<
+ T, IsInplace>::invoke,
+ &function_trait<Second>::template internal_invoker<
+ T, IsInplace>::invoke,
+ &function_trait<Args>::template internal_invoker<
+ T, IsInplace>::invoke...)) {
+ }
+ };
+
+ /// Returns the thunk of an multi overloaded callable
+ template <typename T, bool IsInplace>
+ static type get_invocation_table_of() noexcept {
+ static invocation_vtable<T, IsInplace> const table;
+ return &table;
+ }
+
+ /// The invocation vtable for a present object
+ template <typename T>
+ struct invocation_view_vtable
+ : public std::tuple<function_pointer_of<First>,
+ function_pointer_of<Second>,
+ function_pointer_of<Args>...> {
+ constexpr invocation_view_vtable() noexcept
+ : std::tuple<function_pointer_of<First>, function_pointer_of<Second>,
+ function_pointer_of<Args>...>(std::make_tuple(
+ &function_trait<First>::template view_invoker<T>::invoke,
+ &function_trait<Second>::template view_invoker<T>::invoke,
+ &function_trait<Args>::template view_invoker<T>::invoke...)) {
+ }
+ };
+
+ /// Returns the thunk of an multi overloaded callable
+ template <typename T>
+ static type get_invocation_view_table_of() noexcept {
+ static invocation_view_vtable<T> const table;
+ return &table;
+ }
+
+ /// The invocation table for an empty wrapper
+ template <bool IsThrowing>
+ struct empty_vtable : public std::tuple<function_pointer_of<First>,
+ function_pointer_of<Second>,
+ function_pointer_of<Args>...> {
+ constexpr empty_vtable() noexcept
+ : std::tuple<function_pointer_of<First>, function_pointer_of<Second>,
+ function_pointer_of<Args>...>(
+ std::make_tuple(&function_trait<First>::template empty_invoker<
+ IsThrowing>::invoke,
+ &function_trait<Second>::template empty_invoker<
+ IsThrowing>::invoke,
+ &function_trait<Args>::template empty_invoker<
+ IsThrowing>::invoke...)) {
+ }
+ };
+
+ /// Returns the thunk of an multi single overloaded callable
+ template <bool IsThrowing>
+ static type get_empty_invocation_table() noexcept {
+ static empty_vtable<IsThrowing> const table;
+ return &table;
+ }
+};
+
+template <std::size_t Index, typename Function, typename... Signatures>
+class operator_impl;
+
+#define FU2_DEFINE_FUNCTION_TRAIT(CONST, VOLATILE, NOEXCEPT, OVL_REF, REF) \
+ template <std::size_t Index, typename Function, typename Ret, \
+ typename... Args, typename Next, typename... Signatures> \
+ class operator_impl<Index, Function, \
+ Ret(Args...) CONST VOLATILE OVL_REF NOEXCEPT, Next, \
+ Signatures...> \
+ : operator_impl<Index + 1, Function, Next, Signatures...> { \
+ \
+ template <std::size_t, typename, typename...> \
+ friend class operator_impl; \
+ \
+ protected: \
+ operator_impl() = default; \
+ ~operator_impl() = default; \
+ operator_impl(operator_impl const&) = default; \
+ operator_impl(operator_impl&&) = default; \
+ operator_impl& operator=(operator_impl const&) = default; \
+ operator_impl& operator=(operator_impl&&) = default; \
+ \
+ using operator_impl<Index + 1, Function, Next, Signatures...>::operator(); \
+ \
+ Ret operator()(Args... args) CONST VOLATILE OVL_REF NOEXCEPT { \
+ auto parent = static_cast<Function CONST VOLATILE*>(this); \
+ using erasure_t = std::decay_t<decltype(parent->erasure_)>; \
+ \
+ return erasure_t::template invoke<Index>( \
+ static_cast<erasure_t CONST VOLATILE REF>(parent->erasure_), \
+ std::forward<Args>(args)...); \
+ } \
+ }; \
+ template <std::size_t Index, typename Config, typename Property, \
+ typename Ret, typename... Args> \
+ class operator_impl<Index, function<Config, Property>, \
+ Ret(Args...) CONST VOLATILE OVL_REF NOEXCEPT> \
+ : copyable<Config::is_owning || Config::is_copyable> { \
+ \
+ template <std::size_t, typename, typename...> \
+ friend class operator_impl; \
+ \
+ protected: \
+ operator_impl() = default; \
+ ~operator_impl() = default; \
+ operator_impl(operator_impl const&) = default; \
+ operator_impl(operator_impl&&) = default; \
+ operator_impl& operator=(operator_impl const&) = default; \
+ operator_impl& operator=(operator_impl&&) = default; \
+ \
+ Ret operator()(Args... args) CONST VOLATILE OVL_REF NOEXCEPT { \
+ auto parent = \
+ static_cast<function<Config, Property> CONST VOLATILE*>(this); \
+ using erasure_t = std::decay_t<decltype(parent->erasure_)>; \
+ \
+ return erasure_t::template invoke<Index>( \
+ static_cast<erasure_t CONST VOLATILE REF>(parent->erasure_), \
+ std::forward<Args>(args)...); \
+ } \
+ };
+
+FU2_EXPAND_QUALIFIERS(FU2_DEFINE_FUNCTION_TRAIT)
+#undef FU2_DEFINE_FUNCTION_TRAIT
+} // namespace invocation_table
+
+namespace tables {
+/// Identifies the action which is dispatched on the erased object
+enum class opcode {
+ op_move, //< Move the object and set the vtable
+ op_copy, //< Copy the object and set the vtable
+ op_destroy, //< Destroy the object and reset the vtable
+ op_weak_destroy, //< Destroy the object without resetting the vtable
+ op_fetch_empty, //< Stores true or false into the to storage
+ //< to indicate emptiness
+};
+
+/// Abstraction for a vtable together with a command table
+/// TODO Add optimization for a single formal argument
+/// TODO Add optimization to merge both tables if the function is size
+/// optimized
+template <typename Property>
+class vtable;
+template <bool IsThrowing, bool HasStrongExceptGuarantee,
+ typename... FormalArgs>
+class vtable<property<IsThrowing, HasStrongExceptGuarantee, FormalArgs...>> {
+ using command_function_t = void (*)(vtable* /*this*/, opcode /*op*/,
+ data_accessor* /*from*/,
+ std::size_t /*from_capacity*/,
+ data_accessor* /*to*/,
+ std::size_t /*to_capacity*/);
+
+ using invoke_table_t = invocation_table::invoke_table<FormalArgs...>;
+
+ command_function_t cmd_;
+ typename invoke_table_t::type vtable_;
+
+ template <typename T>
+ struct trait {
+ static_assert(is_box<T>::value,
+ "The trait must be specialized with a box!");
+
+ /// The command table
+ template <bool IsInplace>
+ static void process_cmd(vtable* to_table, opcode op, data_accessor* from,
+ std::size_t from_capacity, data_accessor* to,
+ std::size_t to_capacity) {
+
+ switch (op) {
+ case opcode::op_move: {
+ /// Retrieve the pointer to the object
+ auto box = static_cast<T*>(retrieve<T>(
+ std::integral_constant<bool, IsInplace>{}, from, from_capacity));
+ assert(box && "The object must not be over aligned or null!");
+
+ if (!IsInplace) {
+ // Just swap both pointers if we allocated on the heap
+ to->ptr_ = from->ptr_;
+
+#ifndef _NDEBUG
+ // We don't need to null the pointer since we know that
+ // we don't own the data anymore through the vtable
+ // which is set to empty.
+ from->ptr_ = nullptr;
+#endif
+
+ to_table->template set_allocated<T>();
+
+ }
+ // The object is allocated inplace
+ else {
+ construct(std::true_type{}, std::move(*box), to_table, to,
+ to_capacity);
+ box->~T();
+ }
+ return;
+ }
+ case opcode::op_copy: {
+ auto box = static_cast<T const*>(retrieve<T>(
+ std::integral_constant<bool, IsInplace>{}, from, from_capacity));
+ assert(box && "The object must not be over aligned or null!");
+
+ assert(std::is_copy_constructible<T>::value &&
+ "The box is required to be copyable here!");
+
+ // Try to allocate the object inplace
+ construct(std::is_copy_constructible<T>{}, *box, to_table, to,
+ to_capacity);
+ return;
+ }
+ case opcode::op_destroy:
+ case opcode::op_weak_destroy: {
+
+ assert(!to && !to_capacity && "Arg overflow!");
+ auto box = static_cast<T*>(retrieve<T>(
+ std::integral_constant<bool, IsInplace>{}, from, from_capacity));
+
+ if (IsInplace) {
+ box->~T();
+ } else {
+ box_factory<T>::box_deallocate(box);
+ }
+
+ if (op == opcode::op_destroy) {
+ to_table->set_empty();
+ }
+ return;
+ }
+ case opcode::op_fetch_empty: {
+ write_empty(to, false);
+ return;
+ }
+ }
+
+ // TODO Use an unreachable intrinsic
+ assert(false && "Unreachable!");
+ std::exit(-1);
+ }
+
+ template <typename Box>
+ static void
+ construct(std::true_type /*apply*/, Box&& box, vtable* to_table,
+ data_accessor* to,
+ std::size_t to_capacity) noexcept(HasStrongExceptGuarantee) {
+ // Try to allocate the object inplace
+ void* storage = retrieve<T>(std::true_type{}, to, to_capacity);
+ if (storage) {
+ to_table->template set_inplace<T>();
+ } else {
+ // Allocate the object through the allocator
+ to->ptr_ = storage =
+ box_factory<std::decay_t<Box>>::box_allocate(std::addressof(box));
+ to_table->template set_allocated<T>();
+ }
+ new (storage) T(std::forward<Box>(box));
+ }
+
+ template <typename Box>
+ static void
+ construct(std::false_type /*apply*/, Box&& /*box*/, vtable* /*to_table*/,
+ data_accessor* /*to*/,
+ std::size_t /*to_capacity*/) noexcept(HasStrongExceptGuarantee) {
+ }
+ };
+
+ /// The command table
+ static void empty_cmd(vtable* to_table, opcode op, data_accessor* /*from*/,
+ std::size_t /*from_capacity*/, data_accessor* to,
+ std::size_t /*to_capacity*/) {
+
+ switch (op) {
+ case opcode::op_move:
+ case opcode::op_copy: {
+ to_table->set_empty();
+ break;
+ }
+ case opcode::op_destroy:
+ case opcode::op_weak_destroy: {
+ // Do nothing
+ break;
+ }
+ case opcode::op_fetch_empty: {
+ write_empty(to, true);
+ break;
+ }
+ }
+ }
+
+public:
+ vtable() noexcept = default;
+
+ /// Initialize an object at the given position
+ template <typename T>
+ static void init(vtable& table, T&& object, data_accessor* to,
+ std::size_t to_capacity) {
+
+ trait<std::decay_t<T>>::construct(std::true_type{}, std::forward<T>(object),
+ &table, to, to_capacity);
+ }
+
+ /// Moves the object at the given position
+ void move(vtable& to_table, data_accessor* from, std::size_t from_capacity,
+ data_accessor* to,
+ std::size_t to_capacity) noexcept(HasStrongExceptGuarantee) {
+ cmd_(&to_table, opcode::op_move, from, from_capacity, to, to_capacity);
+ set_empty();
+ }
+
+ /// Destroys the object at the given position
+ void copy(vtable& to_table, data_accessor const* from,
+ std::size_t from_capacity, data_accessor* to,
+ std::size_t to_capacity) const {
+ cmd_(&to_table, opcode::op_copy, const_cast<data_accessor*>(from),
+ from_capacity, to, to_capacity);
+ }
+
+ /// Destroys the object at the given position
+ void destroy(data_accessor* from,
+ std::size_t from_capacity) noexcept(HasStrongExceptGuarantee) {
+ cmd_(this, opcode::op_destroy, from, from_capacity, nullptr, 0U);
+ }
+
+ /// Destroys the object at the given position without invalidating the
+ /// vtable
+ void
+ weak_destroy(data_accessor* from,
+ std::size_t from_capacity) noexcept(HasStrongExceptGuarantee) {
+ cmd_(this, opcode::op_weak_destroy, from, from_capacity, nullptr, 0U);
+ }
+
+ /// Returns true when the vtable doesn't hold any erased object
+ bool empty() const noexcept {
+ data_accessor data;
+ cmd_(nullptr, opcode::op_fetch_empty, nullptr, 0U, &data, 0U);
+ return bool(data.inplace_storage_);
+ }
+
+ /// Invoke the function at the given index
+ template <std::size_t Index, typename... Args>
+ constexpr auto invoke(Args&&... args) const {
+ auto thunk = invoke_table_t::template fetch<Index>(vtable_);
+ return thunk(std::forward<Args>(args)...);
+ }
+ /// Invoke the function at the given index
+ template <std::size_t Index, typename... Args>
+ constexpr auto invoke(Args&&... args) const volatile {
+ auto thunk = invoke_table_t::template fetch<Index>(vtable_);
+ return thunk(std::forward<Args>(args)...);
+ }
+
+ template <typename T>
+ void set_inplace() noexcept {
+ using type = std::decay_t<T>;
+ vtable_ = invoke_table_t::template get_invocation_table_of<type, true>();
+ cmd_ = &trait<type>::template process_cmd<true>;
+ }
+
+ template <typename T>
+ void set_allocated() noexcept {
+ using type = std::decay_t<T>;
+ vtable_ = invoke_table_t::template get_invocation_table_of<type, false>();
+ cmd_ = &trait<type>::template process_cmd<false>;
+ }
+
+ void set_empty() noexcept {
+ vtable_ = invoke_table_t::template get_empty_invocation_table<IsThrowing>();
+ cmd_ = &empty_cmd;
+ }
+};
+} // namespace tables
+
+/// A union which makes the pointer to the heap object share the
+/// same space with the internal capacity.
+/// The storage type is distinguished by multiple versions of the
+/// control and vtable.
+template <std::size_t Capacity, typename = void>
+struct internal_capacity {
+ /// We extend the union through a technique similar to the tail object hack
+ typedef union {
+ /// Tag to access the structure in a type-safe way
+ data_accessor accessor_;
+ /// The internal capacity we use to allocate in-place
+ std::aligned_storage_t<Capacity> capacity_;
+ } type;
+};
+template <std::size_t Capacity>
+struct internal_capacity<Capacity,
+ std::enable_if_t<(Capacity < sizeof(void*))>> {
+ typedef struct {
+ /// Tag to access the structure in a type-safe way
+ data_accessor accessor_;
+ } type;
+};
+
+template <std::size_t Capacity>
+class internal_capacity_holder {
+ // Tag to access the structure in a type-safe way
+ typename internal_capacity<Capacity>::type storage_;
+
+public:
+ constexpr internal_capacity_holder() = default;
+
+ constexpr data_accessor* opaque_ptr() noexcept {
+ return &storage_.accessor_;
+ }
+ constexpr data_accessor const* opaque_ptr() const noexcept {
+ return &storage_.accessor_;
+ }
+ constexpr data_accessor volatile* opaque_ptr() volatile noexcept {
+ return &storage_.accessor_;
+ }
+ constexpr data_accessor const volatile* opaque_ptr() const volatile noexcept {
+ return &storage_.accessor_;
+ }
+
+ static constexpr std::size_t capacity() noexcept {
+ return sizeof(storage_);
+ }
+};
+
+/// An owning erasure
+template <bool IsOwning /* = true*/, typename Config, typename Property>
+class erasure : internal_capacity_holder<Config::capacity> {
+ template <bool, typename, typename>
+ friend class erasure;
+ template <std::size_t, typename, typename...>
+ friend class operator_impl;
+
+ using vtable_t = tables::vtable<Property>;
+
+ vtable_t vtable_;
+
+public:
+ /// Returns the capacity of this erasure
+ static constexpr std::size_t capacity() noexcept {
+ return internal_capacity_holder<Config::capacity>::capacity();
+ }
+
+ constexpr erasure() noexcept {
+ vtable_.set_empty();
+ }
+
+ constexpr erasure(std::nullptr_t) noexcept {
+ vtable_.set_empty();
+ }
+
+ constexpr erasure(erasure&& right) noexcept(
+ Property::is_strong_exception_guaranteed) {
+ right.vtable_.move(vtable_, right.opaque_ptr(), right.capacity(),
+ this->opaque_ptr(), capacity());
+ }
+
+ constexpr erasure(erasure const& right) {
+ right.vtable_.copy(vtable_, right.opaque_ptr(), right.capacity(),
+ this->opaque_ptr(), capacity());
+ }
+
+ template <typename OtherConfig>
+ constexpr erasure(erasure<true, OtherConfig, Property> right) noexcept(
+ Property::is_strong_exception_guaranteed) {
+ right.vtable_.move(vtable_, right.opaque_ptr(), right.capacity(),
+ this->opaque_ptr(), capacity());
+ }
+
+ template <typename T, typename Allocator = std::allocator<std::decay_t<T>>>
+ constexpr erasure(T&& callable, Allocator&& allocator = Allocator{}) {
+ vtable_t::init(vtable_,
+ type_erasure::make_box(
+ std::integral_constant<bool, Config::is_copyable>{},
+ std::forward<T>(callable),
+ std::forward<Allocator>(allocator)),
+ this->opaque_ptr(), capacity());
+ }
+
+ ~erasure() {
+ vtable_.weak_destroy(this->opaque_ptr(), capacity());
+ }
+
+ constexpr erasure&
+ operator=(std::nullptr_t) noexcept(Property::is_strong_exception_guaranteed) {
+ vtable_.destroy(this->opaque_ptr(), capacity());
+ return *this;
+ }
+
+ constexpr erasure& operator=(erasure&& right) noexcept(
+ Property::is_strong_exception_guaranteed) {
+ vtable_.weak_destroy(this->opaque_ptr(), capacity());
+ right.vtable_.move(vtable_, right.opaque_ptr(), right.capacity(),
+ this->opaque_ptr(), capacity());
+ return *this;
+ }
+
+ constexpr erasure& operator=(erasure const& right) {
+ vtable_.weak_destroy(this->opaque_ptr(), capacity());
+ right.vtable_.copy(vtable_, right.opaque_ptr(), right.capacity(),
+ this->opaque_ptr(), capacity());
+ return *this;
+ }
+
+ template <typename OtherConfig>
+ constexpr erasure&
+ operator=(erasure<true, OtherConfig, Property> right) noexcept(
+ Property::is_strong_exception_guaranteed) {
+ vtable_.weak_destroy(this->opaque_ptr(), capacity());
+ right.vtable_.move(vtable_, right.opaque_ptr(), right.capacity(),
+ this->opaque_ptr(), capacity());
+ return *this;
+ }
+
+ template <typename T>
+ constexpr erasure& operator=(T&& callable) {
+ vtable_.weak_destroy(this->opaque_ptr(), capacity());
+ vtable_t::init(vtable_,
+ type_erasure::make_box(
+ std::integral_constant<bool, Config::is_copyable>{},
+ std::forward<T>(callable)),
+ this->opaque_ptr(), capacity());
+ return *this;
+ }
+
+ template <typename T, typename Allocator>
+ void assign(T&& callable, Allocator&& allocator) {
+ vtable_.weak_destroy(this->opaque_ptr(), capacity());
+ vtable_t::init(vtable_,
+ type_erasure::make_box(
+ std::integral_constant<bool, Config::is_copyable>{},
+ std::forward<T>(callable),
+ std::forward<Allocator>(allocator)),
+ this->opaque_ptr(), capacity());
+ }
+
+ /// Returns true when the erasure doesn't hold any erased object
+ constexpr bool empty() const noexcept {
+ return vtable_.empty();
+ }
+
+ /// Invoke the function of the erasure at the given index
+ ///
+ /// We define this out of class to be able to forward the qualified
+ /// erasure correctly.
+ template <std::size_t Index, typename Erasure, typename... Args>
+ static constexpr auto invoke(Erasure&& erasure, Args&&... args) {
+ auto const capacity = erasure.capacity();
+ return erasure.vtable_.template invoke<Index>(
+ std::forward<Erasure>(erasure).opaque_ptr(), capacity,
+ std::forward<Args>(args)...);
+ }
+};
+
+// A non owning erasure
+template </*bool IsOwning = false, */ typename Config, bool IsThrowing,
+ bool HasStrongExceptGuarantee, typename... Args>
+class erasure<false, Config,
+ property<IsThrowing, HasStrongExceptGuarantee, Args...>> {
+ template <bool, typename, typename>
+ friend class erasure;
+ template <std::size_t, typename, typename...>
+ friend class operator_impl;
+
+ using property_t = property<IsThrowing, HasStrongExceptGuarantee, Args...>;
+
+ using invoke_table_t = invocation_table::invoke_table<Args...>;
+ typename invoke_table_t::type invoke_table_;
+
+ /// The internal pointer to the non owned object
+ data_accessor view_;
+
+public:
+ // NOLINTNEXTLINE(cppcoreguidlines-pro-type-member-init)
+ constexpr erasure() noexcept
+ : invoke_table_(
+ invoke_table_t::template get_empty_invocation_table<IsThrowing>()),
+ view_(nullptr) {
+ }
+
+ // NOLINTNEXTLINE(cppcoreguidlines-pro-type-member-init)
+ constexpr erasure(std::nullptr_t) noexcept
+ : invoke_table_(
+ invoke_table_t::template get_empty_invocation_table<IsThrowing>()),
+ view_(nullptr) {
+ }
+
+ // NOLINTNEXTLINE(cppcoreguidlines-pro-type-member-init)
+ constexpr erasure(erasure&& right) noexcept
+ : invoke_table_(right.invoke_table_), view_(right.view_) {
+ }
+
+ constexpr erasure(erasure const& /*right*/) = default;
+
+ template <typename OtherConfig>
+ // NOLINTNEXTLINE(cppcoreguidlines-pro-type-member-init)
+ constexpr erasure(erasure<false, OtherConfig, property_t> right) noexcept
+ : invoke_table_(right.invoke_table_), view_(right.view_) {
+ }
+
+ template <typename T>
+ // NOLINTNEXTLINE(cppcoreguidlines-pro-type-member-init)
+ constexpr erasure(T&& object)
+ : invoke_table_(invoke_table_t::template get_invocation_view_table_of<
+ std::decay_t<T>>()),
+ view_(address_taker<std::decay_t<T>>::take(std::forward<T>(object))) {
+ }
+
+ ~erasure() = default;
+
+ constexpr erasure&
+ operator=(std::nullptr_t) noexcept(HasStrongExceptGuarantee) {
+ invoke_table_ =
+ invoke_table_t::template get_empty_invocation_table<IsThrowing>();
+ view_.ptr_ = nullptr;
+ return *this;
+ }
+
+ constexpr erasure& operator=(erasure&& right) noexcept {
+ invoke_table_ = right.invoke_table_;
+ view_ = right.view_;
+ right = nullptr;
+ return *this;
+ }
+
+ constexpr erasure& operator=(erasure const& /*right*/) = default;
+
+ template <typename OtherConfig>
+ constexpr erasure&
+ operator=(erasure<true, OtherConfig, property_t> right) noexcept {
+ invoke_table_ = right.invoke_table_;
+ view_ = right.view_;
+ return *this;
+ }
+
+ template <typename T>
+ constexpr erasure& operator=(T&& object) {
+ invoke_table_ = invoke_table_t::template get_invocation_view_table_of<
+ std::decay_t<T>>();
+ view_.ptr_ = address_taker<std::decay_t<T>>::take(std::forward<T>(object));
+ return *this;
+ }
+
+ /// Returns true when the erasure doesn't hold any erased object
+ constexpr bool empty() const noexcept {
+ return view_.ptr_ == nullptr;
+ }
+
+ template <std::size_t Index, typename Erasure, typename... T>
+ static constexpr auto invoke(Erasure&& erasure, T&&... args) {
+ auto thunk = invoke_table_t::template fetch<Index>(erasure.invoke_table_);
+ return thunk(&(erasure.view_), 0UL, std::forward<T>(args)...);
+ }
+};
+} // namespace type_erasure
+
+/// Deduces to a true_type if the type T provides the given signature and the
+/// signature is noexcept correct callable.
+template <typename T, typename Signature,
+ typename Trait =
+ type_erasure::invocation_table::function_trait<Signature>>
+struct accepts_one
+ : std::integral_constant<
+ bool, invocation::can_invoke<typename Trait::template callable<T>,
+ typename Trait::arguments>::value &&
+ invocation::is_noexcept_correct<
+ Trait::is_noexcept::value,
+ typename Trait::template callable<T>,
+ typename Trait::arguments>::value> {};
+
+/// Deduces to a true_type if the type T provides all signatures
+template <typename T, typename Signatures, typename = void>
+struct accepts_all : std::false_type {};
+template <typename T, typename... Signatures>
+struct accepts_all<
+ T, identity<Signatures...>,
+ void_t<std::enable_if_t<accepts_one<T, Signatures>::value>...>>
+ : std::true_type {};
+
+template <typename Config, typename T>
+struct assert_wrong_copy_assign {
+ static_assert(!Config::is_copyable ||
+ std::is_copy_constructible<std::decay_t<T>>::value,
+ "Can't wrap a non copyable object into a unique function!");
+
+ using type = void;
+};
+
+template <bool IsStrongExceptGuaranteed, typename T>
+struct assert_no_strong_except_guarantee {
+ static_assert(
+ !IsStrongExceptGuaranteed ||
+ (std::is_nothrow_move_constructible<T>::value &&
+ std::is_nothrow_destructible<T>::value),
+ "Can't wrap a object an object that has no strong exception guarantees "
+ "if this is required by the wrapper!");
+
+ using type = void;
+};
+
+/// SFINAES out if the given callable is not copyable correct to the left one.
+template <typename LeftConfig, typename RightConfig>
+using enable_if_copyable_correct_t =
+ std::enable_if_t<(!LeftConfig::is_copyable || RightConfig::is_copyable)>;
+
+template <typename LeftConfig, typename RightConfig>
+using is_owning_correct =
+ std::integral_constant<bool,
+ (LeftConfig::is_owning == RightConfig::is_owning)>;
+
+/// SFINAES out if the given function2 is not owning correct to this one
+template <typename LeftConfig, typename RightConfig>
+using enable_if_owning_correct_t =
+ std::enable_if_t<is_owning_correct<LeftConfig, RightConfig>::value>;
+
+template <typename Config, bool IsThrowing, bool HasStrongExceptGuarantee,
+ typename... Args>
+class function<Config, property<IsThrowing, HasStrongExceptGuarantee, Args...>>
+ : type_erasure::invocation_table::operator_impl<
+ 0U,
+ function<Config,
+ property<IsThrowing, HasStrongExceptGuarantee, Args...>>,
+ Args...> {
+
+ template <typename, typename>
+ friend class function;
+
+ template <std::size_t, typename, typename...>
+ friend class type_erasure::invocation_table::operator_impl;
+
+ using property_t = property<IsThrowing, HasStrongExceptGuarantee, Args...>;
+ using erasure_t =
+ type_erasure::erasure<Config::is_owning, Config, property_t>;
+
+ template <typename T>
+ using enable_if_can_accept_all_t =
+ std::enable_if_t<accepts_all<std::decay_t<T>, identity<Args...>>::value>;
+
+ template <typename Function, typename = void>
+ struct is_convertible_to_this : std::false_type {};
+ template <typename RightConfig>
+ struct is_convertible_to_this<
+ function<RightConfig, property_t>,
+ void_t<enable_if_copyable_correct_t<Config, RightConfig>,
+ enable_if_owning_correct_t<Config, RightConfig>>>
+ : std::true_type {};
+
+ template <typename T>
+ using enable_if_not_convertible_to_this =
+ std::enable_if_t<!is_convertible_to_this<std::decay_t<T>>::value>;
+
+ template <typename T>
+ using enable_if_owning_t =
+ std::enable_if_t<std::is_same<T, T>::value && Config::is_owning>;
+
+ template <typename T>
+ using assert_wrong_copy_assign_t =
+ typename assert_wrong_copy_assign<Config, std::decay_t<T>>::type;
+
+ template <typename T>
+ using assert_no_strong_except_guarantee_t =
+ typename assert_no_strong_except_guarantee<HasStrongExceptGuarantee,
+ std::decay_t<T>>::type;
+
+ erasure_t erasure_;
+
+public:
+ /// Default constructor which empty constructs the function
+ function() = default;
+ ~function() = default;
+
+ explicit constexpr function(function const& /*right*/) = default;
+ explicit constexpr function(function&& /*right*/) = default;
+
+ /// Copy construction from another copyable function
+ template <typename RightConfig,
+ std::enable_if_t<RightConfig::is_copyable>* = nullptr,
+ enable_if_copyable_correct_t<Config, RightConfig>* = nullptr,
+ enable_if_owning_correct_t<Config, RightConfig>* = nullptr>
+ constexpr function(function<RightConfig, property_t> const& right)
+ : erasure_(right.erasure_) {
+ }
+
+ /// Move construction from another function
+ template <typename RightConfig,
+ enable_if_copyable_correct_t<Config, RightConfig>* = nullptr,
+ enable_if_owning_correct_t<Config, RightConfig>* = nullptr>
+ constexpr function(function<RightConfig, property_t>&& right)
+ : erasure_(std::move(right.erasure_)) {
+ }
+
+ /// Construction from a callable object which overloads the `()` operator
+ template <typename T, //
+ enable_if_not_convertible_to_this<T>* = nullptr,
+ enable_if_can_accept_all_t<T>* = nullptr,
+ assert_wrong_copy_assign_t<T>* = nullptr,
+ assert_no_strong_except_guarantee_t<T>* = nullptr>
+ constexpr function(T&& callable) : erasure_(std::forward<T>(callable)) {
+ }
+ template <typename T, typename Allocator, //
+ enable_if_not_convertible_to_this<T>* = nullptr,
+ enable_if_can_accept_all_t<T>* = nullptr,
+ enable_if_owning_t<T>* = nullptr,
+ assert_wrong_copy_assign_t<T>* = nullptr,
+ assert_no_strong_except_guarantee_t<T>* = nullptr>
+ constexpr function(T&& callable, Allocator&& allocator)
+ : erasure_(std::forward<T>(callable),
+ std::forward<Allocator>(allocator)) {
+ }
+
+ /// Empty constructs the function
+ constexpr function(std::nullptr_t np) : erasure_(np) {
+ }
+
+ function& operator=(function const& /*right*/) = default;
+ function& operator=(function&& /*right*/) = default;
+
+ /// Copy assigning from another copyable function
+ template <typename RightConfig,
+ std::enable_if_t<RightConfig::is_copyable>* = nullptr,
+ enable_if_copyable_correct_t<Config, RightConfig>* = nullptr,
+ enable_if_owning_correct_t<Config, RightConfig>* = nullptr>
+ function& operator=(function<RightConfig, property_t> const& right) {
+ erasure_ = right.erasure_;
+ return *this;
+ }
+
+ /// Move assigning from another function
+ template <typename RightConfig,
+ enable_if_copyable_correct_t<Config, RightConfig>* = nullptr,
+ enable_if_owning_correct_t<Config, RightConfig>* = nullptr>
+ function& operator=(function<RightConfig, property_t>&& right) {
+ erasure_ = std::move(right.erasure_);
+ return *this;
+ }
+
+ /// Move assigning from a callable object
+ template <typename T, // ...
+ enable_if_not_convertible_to_this<T>* = nullptr,
+ enable_if_can_accept_all_t<T>* = nullptr,
+ assert_wrong_copy_assign_t<T>* = nullptr,
+ assert_no_strong_except_guarantee_t<T>* = nullptr>
+ function& operator=(T&& callable) {
+ erasure_ = std::forward<T>(callable);
+ return *this;
+ }
+
+ /// Clears the function
+ function& operator=(std::nullptr_t np) {
+ erasure_ = np;
+ return *this;
+ }
+
+ /// Returns true when the function is empty
+ bool empty() const noexcept {
+ return erasure_.empty();
+ }
+
+ /// Returns true when the function isn't empty
+ explicit operator bool() const noexcept {
+ return !empty();
+ }
+
+ /// Assigns a new target with an optional allocator
+ template <typename T, typename Allocator = std::allocator<std::decay_t<T>>,
+ enable_if_not_convertible_to_this<T>* = nullptr,
+ enable_if_can_accept_all_t<T>* = nullptr,
+ assert_wrong_copy_assign_t<T>* = nullptr,
+ assert_no_strong_except_guarantee_t<T>* = nullptr>
+ void assign(T&& callable, Allocator&& allocator = Allocator{}) {
+ erasure_.assign(std::forward<T>(callable),
+ std::forward<Allocator>(allocator));
+ }
+
+ /// Swaps this function with the given function
+ void swap(function& other) noexcept(HasStrongExceptGuarantee) {
+ if (&other == this) {
+ return;
+ }
+
+ function cache = std::move(other);
+ other = std::move(*this);
+ *this = std::move(cache);
+ }
+
+ /// Swaps the left function with the right one
+ friend void swap(function& left,
+ function& right) noexcept(HasStrongExceptGuarantee) {
+ left.swap(right);
+ }
+
+ /// Calls the wrapped callable object
+ using type_erasure::invocation_table::operator_impl<
+ 0U, function<Config, property_t>, Args...>::operator();
+};
+
+template <typename Config, typename Property>
+bool operator==(function<Config, Property> const& f, std::nullptr_t) {
+ return !bool(f);
+}
+
+template <typename Config, typename Property>
+bool operator!=(function<Config, Property> const& f, std::nullptr_t) {
+ return bool(f);
+}
+
+template <typename Config, typename Property>
+bool operator==(std::nullptr_t, function<Config, Property> const& f) {
+ return !bool(f);
+}
+
+template <typename Config, typename Property>
+bool operator!=(std::nullptr_t, function<Config, Property> const& f) {
+ return bool(f);
+}
+
+// Default object size of the function
+using object_size = std::integral_constant<std::size_t, 32U>;
+
+// Default capacity for small functor optimization
+using default_capacity =
+ std::integral_constant<std::size_t,
+ object_size::value - (2 * sizeof(void*))>;
+} // namespace detail
+} // namespace abi_310
+
+/// Adaptable function wrapper base for arbitrary functional types.
+template <
+ /// This is a placeholder for future non owning support
+ bool IsOwning,
+ /// Defines whether the function is copyable or not
+ bool IsCopyable,
+ /// Defines the internal capacity of the function
+ /// for small functor optimization.
+ /// The size of the whole function object will be the capacity plus
+ /// the size of two pointers.
+ /// If the capacity is zero, the size will increase through one additional
+ /// pointer so the whole object has the size of 3 * sizeof(void*).
+ std::size_t Capacity,
+ /// Defines whether the function throws an exception on empty function
+ /// call, `std::abort` is called otherwise.
+ bool IsThrowing,
+ /// Defines whether all objects satisfy the strong exception guarantees,
+ /// which means the function type will satisfy the strong exception
+ /// guarantees too.
+ bool HasStrongExceptGuarantee,
+ /// Defines the signature of the function wrapper
+ typename... Signatures>
+using function_base = detail::function<
+ detail::config<IsOwning, IsCopyable, Capacity>,
+ detail::property<IsThrowing, HasStrongExceptGuarantee, Signatures...>>;
+
+/// An owning copyable function wrapper for arbitrary callable types.
+template <typename... Signatures>
+using function = function_base<true, true, detail::default_capacity::value,
+ true, false, Signatures...>;
+
+/// An owning non copyable function wrapper for arbitrary callable types.
+template <typename... Signatures>
+using unique_function =
+ function_base<true, false, detail::default_capacity::value, true, false,
+ Signatures...>;
+
+/// A non owning copyable function wrapper for arbitrary callable types.
+template <typename... Signatures>
+using function_view =
+ function_base<false, true, detail::default_capacity::value, true, false,
+ Signatures...>;
+
+#if !defined(FU2_HAS_DISABLED_EXCEPTIONS)
+/// Exception type that is thrown when invoking empty function objects
+/// and exception support isn't disabled.
+///
+/// Exception suport is enabled if
+/// the template parameter 'Throwing' is set to true (default).
+///
+/// This type will default to std::bad_function_call if the
+/// functional header is used, otherwise the library provides its own type.
+///
+/// You may disable the inclusion of the functionl header
+/// through defining `FU2_WITH_NO_FUNCTIONAL_HEADER`.
+///
+using detail::type_erasure::invocation_table::bad_function_call;
+#endif
+
+/// Returns a callable object, which unifies all callable objects
+/// that were passed to this function.
+///
+/// ```cpp
+/// auto overloaded = fu2::overload([](std::true_type) { return true; },
+/// [](std::false_type) { return false; });
+/// ```
+///
+/// \param callables A pack of callable objects with arbitrary signatures.
+///
+/// \returns A callable object which exposes the
+///
+template <typename... T>
+constexpr auto overload(T&&... callables) {
+ return detail::overloading::overload(std::forward<T>(callables)...);
+}
+} // namespace fu2
+
+#undef FU2_EXPAND_QUALIFIERS
+#undef FU2_EXPAND_QUALIFIERS_NOEXCEPT
+
+#endif // FU2_INCLUDED_FUNCTION2_HPP_
diff --git a/src/include/hash.h b/src/include/hash.h
new file mode 100644
index 000000000..2ab95448b
--- /dev/null
+++ b/src/include/hash.h
@@ -0,0 +1,64 @@
+#ifndef CEPH_HASH_H
+#define CEPH_HASH_H
+
+#include "acconfig.h"
+
+// Robert Jenkins' function for mixing 32-bit values
+// http://burtleburtle.net/bob/hash/evahash.html
+// a, b = random bits, c = input and output
+
+#define hashmix(a,b,c) \
+ a=a-b; a=a-c; a=a^(c>>13); \
+ b=b-c; b=b-a; b=b^(a<<8); \
+ c=c-a; c=c-b; c=c^(b>>13); \
+ a=a-b; a=a-c; a=a^(c>>12); \
+ b=b-c; b=b-a; b=b^(a<<16); \
+ c=c-a; c=c-b; c=c^(b>>5); \
+ a=a-b; a=a-c; a=a^(c>>3); \
+ b=b-c; b=b-a; b=b^(a<<10); \
+ c=c-a; c=c-b; c=c^(b>>15);
+
+
+//namespace ceph {
+
+template <class _Key> struct rjhash { };
+
+inline uint64_t rjhash64(uint64_t key) {
+ key = (~key) + (key << 21); // key = (key << 21) - key - 1;
+ key = key ^ (key >> 24);
+ key = (key + (key << 3)) + (key << 8); // key * 265
+ key = key ^ (key >> 14);
+ key = (key + (key << 2)) + (key << 4); // key * 21
+ key = key ^ (key >> 28);
+ key = key + (key << 31);
+ return key;
+}
+
+inline uint32_t rjhash32(uint32_t a) {
+ a = (a+0x7ed55d16) + (a<<12);
+ a = (a^0xc761c23c) ^ (a>>19);
+ a = (a+0x165667b1) + (a<<5);
+ a = (a+0xd3a2646c) ^ (a<<9);
+ a = (a+0xfd7046c5) + (a<<3);
+ a = (a^0xb55a4f09) ^ (a>>16);
+ return a;
+}
+
+
+template<> struct rjhash<uint32_t> {
+ inline size_t operator()(const uint32_t x) const {
+ return rjhash32(x);
+ }
+};
+
+template<> struct rjhash<uint64_t> {
+ inline size_t operator()(const uint64_t x) const {
+ return rjhash64(x);
+ }
+};
+
+//}
+
+
+
+#endif
diff --git a/src/include/health.h b/src/include/health.h
new file mode 100644
index 000000000..03191eff7
--- /dev/null
+++ b/src/include/health.h
@@ -0,0 +1,83 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#pragma once
+
+#include <ostream>
+#include <string>
+
+#include "include/encoding.h"
+
+// health_status_t
+enum health_status_t {
+ HEALTH_ERR = 0,
+ HEALTH_WARN = 1,
+ HEALTH_OK = 2,
+};
+
+inline void encode(health_status_t hs, ceph::buffer::list& bl) {
+ using ceph::encode;
+ uint8_t v = hs;
+ encode(v, bl);
+}
+inline void decode(health_status_t& hs, ceph::buffer::list::const_iterator& p) {
+ using ceph::decode;
+ uint8_t v;
+ decode(v, p);
+ hs = health_status_t(v);
+}
+template<>
+struct denc_traits<health_status_t> {
+ static constexpr bool supported = true;
+ static constexpr bool featured = false;
+ static constexpr bool bounded = true;
+ static constexpr bool need_contiguous = false;
+ static void bound_encode(const ceph::buffer::ptr& v, size_t& p, uint64_t f=0) {
+ p++;
+ }
+ static void encode(const health_status_t& v,
+ ceph::buffer::list::contiguous_appender& p,
+ uint64_t f=0) {
+ ::denc((uint8_t)v, p);
+ }
+ static void decode(health_status_t& v, ceph::buffer::ptr::const_iterator& p,
+ uint64_t f=0) {
+ uint8_t tmp;
+ ::denc(tmp, p);
+ v = health_status_t(tmp);
+ }
+ static void decode(health_status_t& v, ceph::buffer::list::const_iterator& p,
+ uint64_t f=0) {
+ uint8_t tmp;
+ ::denc(tmp, p);
+ v = health_status_t(tmp);
+ }
+};
+
+inline std::ostream& operator<<(std::ostream &oss, const health_status_t status) {
+ switch (status) {
+ case HEALTH_ERR:
+ oss << "HEALTH_ERR";
+ break;
+ case HEALTH_WARN:
+ oss << "HEALTH_WARN";
+ break;
+ case HEALTH_OK:
+ oss << "HEALTH_OK";
+ break;
+ }
+ return oss;
+}
+
+inline const char *short_health_string(const health_status_t status) {
+ switch (status) {
+ case HEALTH_ERR:
+ return "ERR";
+ case HEALTH_WARN:
+ return "WRN";
+ case HEALTH_OK:
+ return "OK";
+ default:
+ return "???";
+ }
+}
diff --git a/src/include/inline_memory.h b/src/include/inline_memory.h
new file mode 100644
index 000000000..48d889763
--- /dev/null
+++ b/src/include/inline_memory.h
@@ -0,0 +1,150 @@
+// -*- 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-2006 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_INLINE_MEMORY_H
+#define CEPH_INLINE_MEMORY_H
+
+#if defined(__GNUC__)
+
+// optimize for the common case, which is very small copies
+static inline void *maybe_inline_memcpy(void *dest, const void *src, size_t l,
+ size_t inline_len)
+ __attribute__((always_inline));
+
+void *maybe_inline_memcpy(void *dest, const void *src, size_t l,
+ size_t inline_len)
+{
+ if (l > inline_len) {
+ return memcpy(dest, src, l);
+ }
+ switch (l) {
+ case 8:
+ return __builtin_memcpy(dest, src, 8);
+ case 4:
+ return __builtin_memcpy(dest, src, 4);
+ case 3:
+ return __builtin_memcpy(dest, src, 3);
+ case 2:
+ return __builtin_memcpy(dest, src, 2);
+ case 1:
+ return __builtin_memcpy(dest, src, 1);
+ default:
+ int cursor = 0;
+ while (l >= sizeof(uint64_t)) {
+ __builtin_memcpy((char*)dest + cursor, (char*)src + cursor,
+ sizeof(uint64_t));
+ cursor += sizeof(uint64_t);
+ l -= sizeof(uint64_t);
+ }
+ while (l >= sizeof(uint32_t)) {
+ __builtin_memcpy((char*)dest + cursor, (char*)src + cursor,
+ sizeof(uint32_t));
+ cursor += sizeof(uint32_t);
+ l -= sizeof(uint32_t);
+ }
+ while (l > 0) {
+ *((char*)dest + cursor) = *((char*)src + cursor);
+ cursor++;
+ l--;
+ }
+ }
+ return dest;
+}
+
+#else
+
+#define maybe_inline_memcpy(d, s, l, x) memcpy(d, s, l)
+
+#endif
+
+
+#if defined(__GNUC__) && defined(__x86_64__)
+
+namespace ceph {
+typedef unsigned uint128_t __attribute__ ((mode (TI)));
+}
+using ceph::uint128_t;
+
+static inline bool mem_is_zero(const char *data, size_t len)
+ __attribute__((always_inline));
+
+bool mem_is_zero(const char *data, size_t len)
+{
+ // we do have XMM registers in x86-64, so if we need to check at least
+ // 16 bytes, make use of them
+ if (len / sizeof(uint128_t) > 0) {
+ // align data pointer to 16 bytes, otherwise it'll segfault due to bug
+ // in (at least some) GCC versions (using MOVAPS instead of MOVUPS).
+ // check up to 15 first bytes while at it.
+ while (((unsigned long long)data) & 15) {
+ if (*(uint8_t*)data != 0) {
+ return false;
+ }
+ data += sizeof(uint8_t);
+ --len;
+ }
+
+ const char* data_start = data;
+ const char* max128 = data + (len / sizeof(uint128_t))*sizeof(uint128_t);
+
+ while (data < max128) {
+ if (*(uint128_t*)data != 0) {
+ return false;
+ }
+ data += sizeof(uint128_t);
+ }
+ len -= (data - data_start);
+ }
+
+ const char* max = data + len;
+ const char* max32 = data + (len / sizeof(uint32_t))*sizeof(uint32_t);
+ while (data < max32) {
+ if (*(uint32_t*)data != 0) {
+ return false;
+ }
+ data += sizeof(uint32_t);
+ }
+ while (data < max) {
+ if (*(uint8_t*)data != 0) {
+ return false;
+ }
+ data += sizeof(uint8_t);
+ }
+ return true;
+}
+
+#else // gcc and x86_64
+
+static inline bool mem_is_zero(const char *data, size_t len) {
+ const char *end = data + len;
+ const char* end64 = data + (len / sizeof(uint64_t))*sizeof(uint64_t);
+
+ while (data < end64) {
+ if (*(uint64_t*)data != 0) {
+ return false;
+ }
+ data += sizeof(uint64_t);
+ }
+
+ while (data < end) {
+ if (*data != 0) {
+ return false;
+ }
+ ++data;
+ }
+ return true;
+}
+
+#endif // !x86_64
+
+#endif
diff --git a/src/include/int_types.h b/src/include/int_types.h
new file mode 100644
index 000000000..a704ba71d
--- /dev/null
+++ b/src/include/int_types.h
@@ -0,0 +1,56 @@
+#ifndef CEPH_INTTYPES_H
+#define CEPH_INTTYPES_H
+
+#include "acconfig.h"
+
+#include <inttypes.h>
+
+#ifdef __linux__
+#include <linux/types.h>
+#else
+#ifndef HAVE___U8
+typedef uint8_t __u8;
+#endif
+
+#ifndef HAVE___S8
+typedef int8_t __s8;
+#endif
+
+#ifndef HAVE___U16
+typedef uint16_t __u16;
+#endif
+
+#ifndef HAVE___S16
+typedef int16_t __s16;
+#endif
+
+#ifndef HAVE___U32
+typedef uint32_t __u32;
+#endif
+
+#ifndef HAVE___S32
+typedef int32_t __s32;
+#endif
+
+#ifndef HAVE___U64
+typedef uint64_t __u64;
+#endif
+
+#ifndef HAVE___S64
+typedef int64_t __s64;
+#endif
+#endif /* LINUX_TYPES_H */
+
+#ifndef BOOST_MPL_CFG_NO_PREPROCESSED_HEADERS
+#define BOOST_MPL_CFG_NO_PREPROCESSED_HEADERS
+#endif
+
+#ifndef BOOST_MPL_LIMIT_VECTOR_SIZE
+#define BOOST_MPL_LIMIT_VECTOR_SIZE 30 // or whatever you need
+#endif
+
+#ifndef BOOST_MPL_LIMIT_MAP_SIZE
+#define BOOST_MPL_LIMIT_MAP_SIZE 30 // or whatever you need
+#endif
+
+#endif
diff --git a/src/include/intarith.h b/src/include/intarith.h
new file mode 100644
index 000000000..68b0345a4
--- /dev/null
+++ b/src/include/intarith.h
@@ -0,0 +1,93 @@
+// -*- 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-2006 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_INTARITH_H
+#define CEPH_INTARITH_H
+
+#include <bit>
+#include <climits>
+#include <concepts>
+#include <type_traits>
+
+template<typename T, typename U>
+constexpr inline std::make_unsigned_t<std::common_type_t<T, U>> div_round_up(T n, U d) {
+ return (n + d - 1) / d;
+}
+
+
+template<typename T, typename U>
+constexpr inline std::make_unsigned_t<std::common_type_t<T, U>> round_up_to(T n, U d) {
+ return (n % d ? (n + d - n % d) : n);
+}
+
+template<typename T, typename U>
+constexpr inline std::make_unsigned_t<std::common_type_t<T, U>> shift_round_up(T x, U y) {
+ return (x + (1 << y) - 1) >> y;
+}
+
+/*
+ * Wrappers for various sorts of alignment and rounding. The "align" must
+ * be a power of 2. Often times it is a block, sector, or page.
+ */
+
+/*
+ * return x rounded down to an align boundary
+ * eg, p2align(1200, 1024) == 1024 (1*align)
+ * eg, p2align(1024, 1024) == 1024 (1*align)
+ * eg, p2align(0x1234, 0x100) == 0x1200 (0x12*align)
+ * eg, p2align(0x5600, 0x100) == 0x5600 (0x56*align)
+ */
+template<typename T>
+constexpr inline T p2align(T x, T align) {
+ return x & -align;
+}
+
+/*
+ * return x % (mod) align
+ * eg, p2phase(0x1234, 0x100) == 0x34 (x-0x12*align)
+ * eg, p2phase(0x5600, 0x100) == 0x00 (x-0x56*align)
+ */
+template<typename T>
+constexpr inline T p2phase(T x, T align) {
+ return x & (align - 1);
+}
+
+/*
+ * return how much space is left in this block (but if it's perfectly
+ * aligned, return 0).
+ * eg, p2nphase(0x1234, 0x100) == 0xcc (0x13*align-x)
+ * eg, p2nphase(0x5600, 0x100) == 0x00 (0x56*align-x)
+ */
+template<typename T>
+constexpr inline T p2nphase(T x, T align) {
+ return -x & (align - 1);
+}
+
+/*
+ * return x rounded up to an align boundary
+ * eg, p2roundup(0x1234, 0x100) == 0x1300 (0x13*align)
+ * eg, p2roundup(0x5600, 0x100) == 0x5600 (0x56*align)
+ */
+template<typename T>
+constexpr inline T p2roundup(T x, T align) {
+ return -(-x & -align);
+}
+
+// count bits (set + any 0's that follow)
+template<std::integral T>
+unsigned cbits(T v) {
+ return (sizeof(v) * CHAR_BIT) - std::countl_zero(std::make_unsigned_t<T>(v));
+}
+
+#endif
diff --git a/src/include/interval_set.h b/src/include/interval_set.h
new file mode 100644
index 000000000..dfb2a306c
--- /dev/null
+++ b/src/include/interval_set.h
@@ -0,0 +1,824 @@
+// -*- 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-2006 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_INTERVAL_SET_H
+#define CEPH_INTERVAL_SET_H
+
+#include <iterator>
+#include <map>
+#include <ostream>
+
+#include "encoding.h"
+
+/*
+ * *** NOTE ***
+ *
+ * This class is written to work with a variety of map-like containers,
+ * *include* ones that invalidate iterators when they are modified (e.g.,
+ * flat_map and btree_map).
+ */
+
+template<typename T, template<typename, typename, typename ...> class C = std::map>
+class interval_set {
+ public:
+ using Map = C<T, T>;
+ using value_type = typename Map::value_type;
+ using offset_type = T;
+ using length_type = T;
+ using reference = value_type&;
+ using const_reference = const value_type&;
+ using size_type = typename Map::size_type;
+
+ class const_iterator;
+
+ class iterator
+ {
+ public:
+ using difference_type = ssize_t;
+ using value_type = typename Map::value_type;
+ using pointer = typename Map::value_type*;
+ using reference = typename Map::value_type&;
+ using iterator_category = std::forward_iterator_tag;
+
+ explicit iterator(typename Map::iterator iter)
+ : _iter(iter)
+ { }
+
+ // For the copy constructor and assignment operator, the compiler-generated functions, which
+ // perform simple bitwise copying, should be fine.
+
+ bool operator==(const iterator& rhs) const {
+ return (_iter == rhs._iter);
+ }
+
+ bool operator!=(const iterator& rhs) const {
+ return (_iter != rhs._iter);
+ }
+
+ // Dereference this iterator to get a pair.
+ reference operator*() const {
+ return *_iter;
+ }
+
+ // Return the interval start.
+ offset_type get_start() const {
+ return _iter->first;
+ }
+
+ // Return the interval length.
+ length_type get_len() const {
+ return _iter->second;
+ }
+
+ offset_type get_end() const {
+ return _iter->first + _iter->second;
+ }
+
+ // Set the interval length.
+ void set_len(const length_type& len) {
+ _iter->second = len;
+ }
+
+ // Preincrement
+ iterator& operator++()
+ {
+ ++_iter;
+ return *this;
+ }
+
+ // Postincrement
+ iterator operator++(int)
+ {
+ iterator prev(_iter);
+ ++_iter;
+ return prev;
+ }
+
+ // Predecrement
+ iterator& operator--()
+ {
+ --_iter;
+ return *this;
+ }
+
+ // Postdecrement
+ iterator operator--(int)
+ {
+ iterator prev(_iter);
+ --_iter;
+ return prev;
+ }
+
+ friend class interval_set::const_iterator;
+
+ protected:
+ typename Map::iterator _iter;
+ friend class interval_set;
+ };
+
+ class const_iterator
+ {
+ public:
+ using difference_type = ssize_t;
+ using value_type = const typename Map::value_type;
+ using pointer = const typename Map::value_type*;
+ using reference = const typename Map::value_type&;
+ using iterator_category = std::forward_iterator_tag;
+
+ explicit const_iterator(typename Map::const_iterator iter)
+ : _iter(iter)
+ { }
+
+ const_iterator(const iterator &i)
+ : _iter(i._iter)
+ { }
+
+ // For the copy constructor and assignment operator, the compiler-generated functions, which
+ // perform simple bitwise copying, should be fine.
+
+ bool operator==(const const_iterator& rhs) const {
+ return (_iter == rhs._iter);
+ }
+
+ bool operator!=(const const_iterator& rhs) const {
+ return (_iter != rhs._iter);
+ }
+
+ // Dereference this iterator to get a pair.
+ reference operator*() const {
+ return *_iter;
+ }
+
+ // Return the interval start.
+ offset_type get_start() const {
+ return _iter->first;
+ }
+ offset_type get_end() const {
+ return _iter->first + _iter->second;
+ }
+
+ // Return the interval length.
+ length_type get_len() const {
+ return _iter->second;
+ }
+
+ // Preincrement
+ const_iterator& operator++()
+ {
+ ++_iter;
+ return *this;
+ }
+
+ // Postincrement
+ const_iterator operator++(int)
+ {
+ const_iterator prev(_iter);
+ ++_iter;
+ return prev;
+ }
+
+ // Predecrement
+ iterator& operator--()
+ {
+ --_iter;
+ return *this;
+ }
+
+ // Postdecrement
+ iterator operator--(int)
+ {
+ iterator prev(_iter);
+ --_iter;
+ return prev;
+ }
+
+ protected:
+ typename Map::const_iterator _iter;
+ };
+
+ interval_set() = default;
+ interval_set(Map&& other) {
+ m.swap(other);
+ for (const auto& p : m) {
+ _size += p.second;
+ }
+ }
+
+ size_type num_intervals() const
+ {
+ return m.size();
+ }
+
+ iterator begin() {
+ return iterator(m.begin());
+ }
+
+ iterator lower_bound(T start) {
+ return iterator(find_inc_m(start));
+ }
+
+ iterator end() {
+ return iterator(m.end());
+ }
+
+ const_iterator begin() const {
+ return const_iterator(m.begin());
+ }
+
+ const_iterator lower_bound(T start) const {
+ return const_iterator(find_inc(start));
+ }
+
+ const_iterator end() const {
+ return const_iterator(m.end());
+ }
+
+ // helpers
+ private:
+ auto find_inc(T start) const {
+ auto p = m.lower_bound(start); // p->first >= start
+ if (p != m.begin() &&
+ (p == m.end() || p->first > start)) {
+ --p; // might overlap?
+ if (p->first + p->second <= start)
+ ++p; // it doesn't.
+ }
+ return p;
+ }
+
+ auto find_inc_m(T start) {
+ auto p = m.lower_bound(start);
+ if (p != m.begin() &&
+ (p == m.end() || p->first > start)) {
+ --p; // might overlap?
+ if (p->first + p->second <= start)
+ ++p; // it doesn't.
+ }
+ return p;
+ }
+
+ auto find_adj(T start) const {
+ auto p = m.lower_bound(start);
+ if (p != m.begin() &&
+ (p == m.end() || p->first > start)) {
+ --p; // might touch?
+ if (p->first + p->second < start)
+ ++p; // it doesn't.
+ }
+ return p;
+ }
+
+ auto find_adj_m(T start) {
+ auto p = m.lower_bound(start);
+ if (p != m.begin() &&
+ (p == m.end() || p->first > start)) {
+ --p; // might touch?
+ if (p->first + p->second < start)
+ ++p; // it doesn't.
+ }
+ return p;
+ }
+
+ void intersection_size_asym(const interval_set &s, const interval_set &l) {
+ auto ps = s.m.begin();
+ ceph_assert(ps != s.m.end());
+ auto offset = ps->first;
+ bool first = true;
+ auto mi = m.begin();
+
+ while (1) {
+ if (first)
+ first = false;
+ auto pl = l.find_inc(offset);
+ if (pl == l.m.end())
+ break;
+ while (ps != s.m.end() && ps->first + ps->second <= pl->first)
+ ++ps;
+ if (ps == s.m.end())
+ break;
+ offset = pl->first + pl->second;
+ if (offset <= ps->first) {
+ offset = ps->first;
+ continue;
+ }
+
+ if (*ps == *pl) {
+ do {
+ mi = m.insert(mi, *ps);
+ _size += ps->second;
+ ++ps;
+ ++pl;
+ } while (ps != s.m.end() && pl != l.m.end() && *ps == *pl);
+ if (ps == s.m.end())
+ break;
+ offset = ps->first;
+ continue;
+ }
+
+ auto start = std::max<T>(ps->first, pl->first);
+ auto en = std::min<T>(ps->first + ps->second, offset);
+ ceph_assert(en > start);
+ mi = m.emplace_hint(mi, start, en - start);
+ _size += mi->second;
+ if (ps->first + ps->second <= offset) {
+ ++ps;
+ if (ps == s.m.end())
+ break;
+ offset = ps->first;
+ }
+ }
+ }
+
+ bool subset_size_sym(const interval_set &b) const {
+ auto pa = m.begin(), pb = b.m.begin();
+ const auto a_end = m.end(), b_end = b.m.end();
+
+ while (pa != a_end && pb != b_end) {
+ while (pb->first + pb->second <= pa->first) {
+ ++pb;
+ if (pb == b_end)
+ return false;
+ }
+
+ if (*pa == *pb) {
+ do {
+ ++pa;
+ ++pb;
+ } while (pa != a_end && pb != b_end && *pa == *pb);
+ continue;
+ }
+
+ // interval begins before other
+ if (pa->first < pb->first)
+ return false;
+ // interval is longer than other
+ if (pa->first + pa->second > pb->first + pb->second)
+ return false;
+
+ ++pa;
+ }
+
+ return pa == a_end;
+ }
+
+ public:
+ bool operator==(const interval_set& other) const {
+ return _size == other._size && m == other.m;
+ }
+
+ uint64_t size() const {
+ return _size;
+ }
+
+ void bound_encode(size_t& p) const {
+ denc_traits<Map>::bound_encode(m, p);
+ }
+ void encode(ceph::buffer::list::contiguous_appender& p) const {
+ denc(m, p);
+ }
+ void decode(ceph::buffer::ptr::const_iterator& p) {
+ denc(m, p);
+ _size = 0;
+ for (const auto& p : m) {
+ _size += p.second;
+ }
+ }
+ void decode(ceph::buffer::list::iterator& p) {
+ denc(m, p);
+ _size = 0;
+ for (const auto& p : m) {
+ _size += p.second;
+ }
+ }
+
+ void encode_nohead(ceph::buffer::list::contiguous_appender& p) const {
+ denc_traits<Map>::encode_nohead(m, p);
+ }
+ void decode_nohead(int n, ceph::buffer::ptr::const_iterator& p) {
+ denc_traits<Map>::decode_nohead(n, m, p);
+ _size = 0;
+ for (const auto& p : m) {
+ _size += p.second;
+ }
+ }
+
+ void clear() {
+ m.clear();
+ _size = 0;
+ }
+
+ bool contains(T i, T *pstart=0, T *plen=0) const {
+ auto p = find_inc(i);
+ if (p == m.end()) return false;
+ if (p->first > i) return false;
+ if (p->first+p->second <= i) return false;
+ ceph_assert(p->first <= i && p->first+p->second > i);
+ if (pstart)
+ *pstart = p->first;
+ if (plen)
+ *plen = p->second;
+ return true;
+ }
+ bool contains(T start, T len) const {
+ auto p = find_inc(start);
+ if (p == m.end()) return false;
+ if (p->first > start) return false;
+ if (p->first+p->second <= start) return false;
+ ceph_assert(p->first <= start && p->first+p->second > start);
+ if (p->first+p->second < start+len) return false;
+ return true;
+ }
+ bool intersects(T start, T len) const {
+ interval_set a;
+ a.insert(start, len);
+ interval_set i;
+ i.intersection_of( *this, a );
+ if (i.empty()) return false;
+ return true;
+ }
+
+ // outer range of set
+ bool empty() const {
+ return m.empty();
+ }
+ offset_type range_start() const {
+ ceph_assert(!empty());
+ auto p = m.begin();
+ return p->first;
+ }
+ offset_type range_end() const {
+ ceph_assert(!empty());
+ auto p = m.rbegin();
+ return p->first + p->second;
+ }
+
+ // interval start after p (where p not in set)
+ bool starts_after(T i) const {
+ ceph_assert(!contains(i));
+ auto p = find_inc(i);
+ if (p == m.end()) return false;
+ return true;
+ }
+ offset_type start_after(T i) const {
+ ceph_assert(!contains(i));
+ auto p = find_inc(i);
+ return p->first;
+ }
+
+ // interval end that contains start
+ offset_type end_after(T start) const {
+ ceph_assert(contains(start));
+ auto p = find_inc(start);
+ return p->first+p->second;
+ }
+
+ void insert(T val) {
+ insert(val, 1);
+ }
+
+ void insert(T start, T len, T *pstart=0, T *plen=0) {
+ //cout << "insert " << start << "~" << len << endl;
+ ceph_assert(len > 0);
+ _size += len;
+ auto p = find_adj_m(start);
+ if (p == m.end()) {
+ m[start] = len; // new interval
+ if (pstart)
+ *pstart = start;
+ if (plen)
+ *plen = len;
+ } else {
+ if (p->first < start) {
+
+ if (p->first + p->second != start) {
+ //cout << "p is " << p->first << "~" << p->second << ", start is " << start << ", len is " << len << endl;
+ ceph_abort();
+ }
+
+ p->second += len; // append to end
+
+ auto n = p;
+ ++n;
+ if (pstart)
+ *pstart = p->first;
+ if (n != m.end() &&
+ start+len == n->first) { // combine with next, too!
+ p->second += n->second;
+ if (plen)
+ *plen = p->second;
+ m.erase(n);
+ } else {
+ if (plen)
+ *plen = p->second;
+ }
+ } else {
+ if (start+len == p->first) {
+ if (pstart)
+ *pstart = start;
+ if (plen)
+ *plen = len + p->second;
+ T psecond = p->second;
+ m.erase(p);
+ m[start] = len + psecond; // append to front
+ } else {
+ ceph_assert(p->first > start+len);
+ if (pstart)
+ *pstart = start;
+ if (plen)
+ *plen = len;
+ m[start] = len; // new interval
+ }
+ }
+ }
+ }
+
+ void swap(interval_set& other) {
+ m.swap(other.m);
+ std::swap(_size, other._size);
+ }
+
+ void erase(const iterator &i) {
+ _size -= i.get_len();
+ m.erase(i._iter);
+ }
+
+ void erase(T val) {
+ erase(val, 1);
+ }
+
+ void erase(T start, T len,
+ std::function<bool(T, T)> claim = {}) {
+ auto p = find_inc_m(start);
+
+ _size -= len;
+
+ ceph_assert(p != m.end());
+ ceph_assert(p->first <= start);
+
+ T before = start - p->first;
+ ceph_assert(p->second >= before+len);
+ T after = p->second - before - len;
+ if (before) {
+ if (claim && claim(p->first, before)) {
+ _size -= before;
+ m.erase(p);
+ } else {
+ p->second = before; // shorten bit before
+ }
+ } else {
+ m.erase(p);
+ }
+ if (after) {
+ if (claim && claim(start + len, after)) {
+ _size -= after;
+ } else {
+ m[start + len] = after;
+ }
+ }
+ }
+
+ void subtract(const interval_set &a) {
+ for (const auto& [start, len] : a.m) {
+ erase(start, len);
+ }
+ }
+
+ void insert(const interval_set &a) {
+ for (const auto& [start, len] : a.m) {
+ insert(start, len);
+ }
+ }
+
+
+ void intersection_of(const interval_set &a, const interval_set &b) {
+ ceph_assert(&a != this);
+ ceph_assert(&b != this);
+ clear();
+
+ const interval_set *s, *l;
+
+ if (a.size() < b.size()) {
+ s = &a;
+ l = &b;
+ } else {
+ s = &b;
+ l = &a;
+ }
+
+ if (!s->size())
+ return;
+
+ /*
+ * Use the lower_bound algorithm for larger size ratios
+ * where it performs better, but not for smaller size
+ * ratios where sequential search performs better.
+ */
+ if (l->size() / s->size() >= 10) {
+ intersection_size_asym(*s, *l);
+ return;
+ }
+
+ auto pa = a.m.begin();
+ auto pb = b.m.begin();
+ auto mi = m.begin();
+
+ while (pa != a.m.end() && pb != b.m.end()) {
+ // passing?
+ if (pa->first + pa->second <= pb->first)
+ { pa++; continue; }
+ if (pb->first + pb->second <= pa->first)
+ { pb++; continue; }
+
+ if (*pa == *pb) {
+ do {
+ mi = m.insert(mi, *pa);
+ _size += pa->second;
+ ++pa;
+ ++pb;
+ } while (pa != a.m.end() && pb != b.m.end() && *pa == *pb);
+ continue;
+ }
+
+ T start = std::max(pa->first, pb->first);
+ T en = std::min(pa->first+pa->second, pb->first+pb->second);
+ ceph_assert(en > start);
+ mi = m.emplace_hint(mi, start, en - start);
+ _size += mi->second;
+ if (pa->first+pa->second > pb->first+pb->second)
+ pb++;
+ else
+ pa++;
+ }
+ }
+ void intersection_of(const interval_set& b) {
+ interval_set a;
+ swap(a);
+ intersection_of(a, b);
+ }
+
+ void union_of(const interval_set &a, const interval_set &b) {
+ ceph_assert(&a != this);
+ ceph_assert(&b != this);
+ clear();
+
+ //cout << "union_of" << endl;
+
+ // a
+ m = a.m;
+ _size = a._size;
+
+ // - (a*b)
+ interval_set ab;
+ ab.intersection_of(a, b);
+ subtract(ab);
+
+ // + b
+ insert(b);
+ return;
+ }
+ void union_of(const interval_set &b) {
+ interval_set a;
+ swap(a);
+ union_of(a, b);
+ }
+ void union_insert(T off, T len) {
+ interval_set a;
+ a.insert(off, len);
+ union_of(a);
+ }
+
+ bool subset_of(const interval_set &big) const {
+ if (!size())
+ return true;
+ if (size() > big.size())
+ return false;
+ if (range_end() > big.range_end())
+ return false;
+
+ /*
+ * Use the lower_bound algorithm for larger size ratios
+ * where it performs better, but not for smaller size
+ * ratios where sequential search performs better.
+ */
+ if (big.size() / size() < 10)
+ return subset_size_sym(big);
+
+ for (const auto& [start, len] : m) {
+ if (!big.contains(start, len)) return false;
+ }
+ return true;
+ }
+
+ /*
+ * build a subset of @other, starting at or after @start, and including
+ * @len worth of values, skipping holes. e.g.,
+ * span_of([5~10,20~5], 8, 5) -> [8~2,20~3]
+ */
+ void span_of(const interval_set &other, T start, T len) {
+ clear();
+ auto p = other.find_inc(start);
+ if (p == other.m.end())
+ return;
+ if (p->first < start) {
+ if (p->first + p->second < start)
+ return;
+ if (p->first + p->second < start + len) {
+ T howmuch = p->second - (start - p->first);
+ insert(start, howmuch);
+ len -= howmuch;
+ p++;
+ } else {
+ insert(start, len);
+ return;
+ }
+ }
+ while (p != other.m.end() && len > 0) {
+ if (p->second < len) {
+ insert(p->first, p->second);
+ len -= p->second;
+ p++;
+ } else {
+ insert(p->first, len);
+ return;
+ }
+ }
+ }
+
+ /*
+ * Move contents of m into another Map. Use that instead of
+ * encoding interval_set into bufferlist then decoding it back into Map.
+ */
+ Map detach() && {
+ return std::move(m);
+ }
+
+private:
+ // data
+ uint64_t _size = 0;
+ Map m; // map start -> len
+};
+
+// declare traits explicitly because (1) it's templatized, and (2) we
+// want to include _nohead variants.
+template<typename T, template<typename, typename, typename ...> class C>
+struct denc_traits<interval_set<T, C>> {
+private:
+ using container_t = interval_set<T, C>;
+public:
+ static constexpr bool supported = true;
+ static constexpr bool bounded = false;
+ static constexpr bool featured = false;
+ static constexpr bool need_contiguous = denc_traits<T, C<T,T>>::need_contiguous;
+ static void bound_encode(const container_t& v, size_t& p) {
+ v.bound_encode(p);
+ }
+ static void encode(const container_t& v,
+ ceph::buffer::list::contiguous_appender& p) {
+ v.encode(p);
+ }
+ static void decode(container_t& v, ceph::buffer::ptr::const_iterator& p) {
+ v.decode(p);
+ }
+ template<typename U=T>
+ static typename std::enable_if<sizeof(U) && !need_contiguous>::type
+ decode(container_t& v, ceph::buffer::list::iterator& p) {
+ v.decode(p);
+ }
+ static void encode_nohead(const container_t& v,
+ ceph::buffer::list::contiguous_appender& p) {
+ v.encode_nohead(p);
+ }
+ static void decode_nohead(size_t n, container_t& v,
+ ceph::buffer::ptr::const_iterator& p) {
+ v.decode_nohead(n, p);
+ }
+};
+
+
+template<typename T, template<typename, typename, typename ...> class C>
+inline std::ostream& operator<<(std::ostream& out, const interval_set<T,C> &s) {
+ out << "[";
+ bool first = true;
+ for (const auto& [start, len] : s) {
+ if (!first) out << ",";
+ out << start << "~" << len;
+ first = false;
+ }
+ out << "]";
+ return out;
+}
+
+
+#endif
diff --git a/src/include/ipaddr.h b/src/include/ipaddr.h
new file mode 100644
index 000000000..bf06cfc93
--- /dev/null
+++ b/src/include/ipaddr.h
@@ -0,0 +1,47 @@
+#ifndef CEPH_IPADDR_H
+#define CEPH_IPADDR_H
+
+class entity_addr_t;
+
+/*
+ * Check if an IP address that is in the wanted subnet.
+ */
+bool matches_ipv4_in_subnet(const struct ifaddrs& addrs,
+ const struct sockaddr_in* net,
+ unsigned int prefix_len);
+bool matches_ipv6_in_subnet(const struct ifaddrs& addrs,
+ const struct sockaddr_in6* net,
+ unsigned int prefix_len);
+
+/*
+ * Validate and parse IPv4 or IPv6 network
+ *
+ * Given a network (e.g. "192.168.0.0/24") and pointers to a sockaddr_storage
+ * struct and an unsigned int:
+ *
+ * if the network string is valid, return true and populate sockaddr_storage
+ * and prefix_len;
+ *
+ * if the network string is invalid, return false.
+ */
+bool parse_network(const char *s,
+ struct sockaddr_storage *network,
+ unsigned int *prefix_len);
+bool parse_network(const char *s,
+ entity_addr_t *network,
+ unsigned int *prefix_len);
+
+void netmask_ipv6(const struct in6_addr *addr,
+ unsigned int prefix_len,
+ struct in6_addr *out);
+
+void netmask_ipv4(const struct in_addr *addr,
+ unsigned int prefix_len,
+ struct in_addr *out);
+
+bool network_contains(
+ const struct entity_addr_t& network,
+ unsigned int prefix_len,
+ const struct entity_addr_t& addr);
+
+#endif
diff --git a/src/include/krbd.h b/src/include/krbd.h
new file mode 100644
index 000000000..977d45fe2
--- /dev/null
+++ b/src/include/krbd.h
@@ -0,0 +1,97 @@
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2014 Inktank Storage, Inc.
+ *
+ * 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_KRBD_H
+#define CEPH_KRBD_H
+
+#include "rados/librados.h"
+
+/*
+ * Don't wait for udev add uevents in krbd_map() and udev remove
+ * uevents in krbd_unmap*(). Instead, make do with the respective
+ * kernel uevents and return as soon as they are received.
+ *
+ * systemd-udevd sends out udev uevents after it finishes processing
+ * the respective kernel uevents, which mostly boils down to executing
+ * all matching udev rules. With this flag set, on return from
+ * krbd_map() systemd-udevd may still be poking at the device: it
+ * may still be open with tools such as blkid and various ioctls to
+ * be run against it, none of the persistent symlinks to the device
+ * node may be there, etc. udev used to be responsible for creating
+ * the device node as well, but that has been handled by devtmpfs in
+ * the kernel for many years now, so the device node (as returned
+ * through @pdevnode) is guaranteed to be there.
+ *
+ * If set, krbd_map() and krbd_unmap*() can be invoked from any
+ * network namespace that is owned by the initial user namespace
+ * (which is a formality because things like loading kernel modules
+ * and creating block devices are not namespaced and require global
+ * privileges, i.e. capabilities in the initial user namespace).
+ * Otherwise, krbd_map() and krbd_unmap*() must be invoked from
+ * the initial network namespace.
+ *
+ * If set, krbd_unmap*() doesn't attempt to settle the udev queue
+ * before retrying unmap for the last time. Some EBUSY errors due
+ * to systemd-udevd poking at the device at the time krbd_unmap*()
+ * is invoked that are otherwise covered by the retry logic may be
+ * returned.
+ */
+#define KRBD_CTX_F_NOUDEV (1U << 0)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct krbd_ctx;
+
+int krbd_create_from_context(rados_config_t cct, uint32_t flags,
+ struct krbd_ctx **pctx);
+void krbd_destroy(struct krbd_ctx *ctx);
+
+int krbd_map(struct krbd_ctx *ctx,
+ const char *pool_name,
+ const char *nspace_name,
+ const char *image_name,
+ const char *snap_name,
+ const char *options,
+ char **pdevnode);
+int krbd_is_mapped(struct krbd_ctx *ctx,
+ const char *pool_name,
+ const char *nspace_name,
+ const char *image_name,
+ const char *snap_name,
+ char **pdevnode);
+
+int krbd_unmap(struct krbd_ctx *ctx, const char *devnode,
+ const char *options);
+int krbd_unmap_by_spec(struct krbd_ctx *ctx,
+ const char *pool_name,
+ const char *nspace_name,
+ const char *image_name,
+ const char *snap_name,
+ const char *options);
+
+#ifdef __cplusplus
+}
+#endif
+
+#ifdef __cplusplus
+
+namespace ceph {
+ class Formatter;
+}
+
+int krbd_showmapped(struct krbd_ctx *ctx, ceph::Formatter *f);
+
+#endif /* __cplusplus */
+
+#endif /* CEPH_KRBD_H */
diff --git a/src/include/libcephsqlite.h b/src/include/libcephsqlite.h
new file mode 100644
index 000000000..d81cc55e8
--- /dev/null
+++ b/src/include/libcephsqlite.h
@@ -0,0 +1,73 @@
+// -*- 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) 2021 Red Hat, Inc.
+ *
+ * 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 LIBCEPHSQLITE_H
+#define LIBCEPHSQLITE_H
+
+/* This loadable extension does not generally require using this header. It is
+ * here to allow controlling which version of the library is linked in. See
+ * also sqlite3_cephsqlite_init below. Additionally, you may specify which
+ * CephContext to use rather than the library instantiating its own and using
+ * whatever the default credential is.
+ */
+
+#include <sqlite3.h>
+
+#ifdef _WIN32
+# define LIBCEPHSQLITE_API __declspec(dllexport)
+#else
+# define LIBCEPHSQLITE_API [[gnu::visibility("default")]]
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/* This is the SQLite entry point when loaded as a dynamic library. You also
+ * need to ensure SQLite calls this method when using libcephsqlite as a static
+ * library or a dynamic library linked at compile time. For the latter case,
+ * you can do this by:
+ *
+ * sqlite3_auto_extension((void (*)())sqlite3_cephsqlite_init);
+ * sqlite3* db = nullptr;
+ * int rc = sqlite3_open_v2(":memory:", &db, SQLITE_OPEN_READWRITE, nullptr);
+ * if (rc == SQLITE_DONE) {
+ * sqlite3_close(db);
+ * } else {
+ * // failure
+ * }
+ *
+ * The throwaway database created (name == "") is a memory database opened so
+ * that SQLite runs the libcephsqlite initialization routine to register the
+ * VFS. AFter that's done, the VFS is available for a future database open with
+ * the VFS set to "ceph":
+ *
+ * sqlite3_open_v2("foo:bar/baz.db", &db, SQLITE_OPEN_READWRITE, "ceph");
+ *
+ * You MUST do this before calling any other libcephsqlite routine so that
+ * sqlite3 can pass its API routines to the libcephsqlite extension.
+ */
+
+LIBCEPHSQLITE_API int sqlite3_cephsqlite_init(sqlite3* db, char** err, const sqlite3_api_routines* api);
+
+/* If you prefer to have libcephsqlite use a CephContext managed by your
+ * application, use this routine to set that. libcephsqlite can only have one
+ * context globally.
+ */
+
+LIBCEPHSQLITE_API int cephsqlite_setcct(class CephContext* cct, char** ident);
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/include/linux_fiemap.h b/src/include/linux_fiemap.h
new file mode 100644
index 000000000..36046b5cc
--- /dev/null
+++ b/src/include/linux_fiemap.h
@@ -0,0 +1,73 @@
+/*
+ * FS_IOC_FIEMAP ioctl infrastructure.
+ *
+ * Some portions copyright (C) 2007 Cluster File Systems, Inc
+ *
+ * Authors: Mark Fasheh <mfasheh@suse.com>
+ * Kalpak Shah <kalpak.shah@sun.com>
+ * Andreas Dilger <adilger@sun.com>
+ */
+#ifndef _LINUX_FIEMAP_H
+#define _LINUX_FIEMAP_H
+
+#if defined(__linux__)
+#include <linux/types.h>
+#elif defined(__FreeBSD_)
+#include <sys/types.h>
+#endif
+
+#include "include/int_types.h"
+
+struct fiemap_extent {
+ __u64 fe_logical; /* logical offset in bytes for the start of
+ * the extent from the beginning of the file */
+ __u64 fe_physical; /* physical offset in bytes for the start
+ * of the extent from the beginning of the disk */
+ __u64 fe_length; /* length in bytes for this extent */
+ __u64 fe_reserved64[2];
+ __u32 fe_flags; /* FIEMAP_EXTENT_* flags for this extent */
+ __u32 fe_reserved[3];
+};
+
+struct fiemap {
+ __u64 fm_start; /* logical offset (inclusive) at
+ * which to start mapping (in) */
+ __u64 fm_length; /* logical length of mapping which
+ * userspace wants (in) */
+ __u32 fm_flags; /* FIEMAP_FLAG_* flags for request (in/out) */
+ __u32 fm_mapped_extents;/* number of extents that were mapped (out) */
+ __u32 fm_extent_count; /* size of fm_extents array (in) */
+ __u32 fm_reserved;
+ struct fiemap_extent fm_extents[0]; /* array of mapped extents (out) */
+};
+
+#define FIEMAP_MAX_OFFSET (~0ULL)
+
+#define FIEMAP_FLAG_SYNC 0x00000001 /* sync file data before map */
+#define FIEMAP_FLAG_XATTR 0x00000002 /* map extended attribute tree */
+
+#define FIEMAP_FLAGS_COMPAT (FIEMAP_FLAG_SYNC | FIEMAP_FLAG_XATTR)
+
+#define FIEMAP_EXTENT_LAST 0x00000001 /* Last extent in file. */
+#define FIEMAP_EXTENT_UNKNOWN 0x00000002 /* Data location unknown. */
+#define FIEMAP_EXTENT_DELALLOC 0x00000004 /* Location still pending.
+ * Sets EXTENT_UNKNOWN. */
+#define FIEMAP_EXTENT_ENCODED 0x00000008 /* Data can not be read
+ * while fs is unmounted */
+#define FIEMAP_EXTENT_DATA_ENCRYPTED 0x00000080 /* Data is encrypted by fs.
+ * Sets EXTENT_NO_BYPASS. */
+#define FIEMAP_EXTENT_NOT_ALIGNED 0x00000100 /* Extent offsets may not be
+ * block aligned. */
+#define FIEMAP_EXTENT_DATA_INLINE 0x00000200 /* Data mixed with metadata.
+ * Sets EXTENT_NOT_ALIGNED.*/
+#define FIEMAP_EXTENT_DATA_TAIL 0x00000400 /* Multiple files in block.
+ * Sets EXTENT_NOT_ALIGNED.*/
+#define FIEMAP_EXTENT_UNWRITTEN 0x00000800 /* Space allocated, but
+ * no data (i.e. zero). */
+#define FIEMAP_EXTENT_MERGED 0x00001000 /* File does not natively
+ * support extents. Result
+ * merged for efficiency. */
+#define FIEMAP_EXTENT_SHARED 0x00002000 /* Space shared with other
+ * files. */
+
+#endif /* _LINUX_FIEMAP_H */
diff --git a/src/include/lru.h b/src/include/lru.h
new file mode 100644
index 000000000..3f5069ee3
--- /dev/null
+++ b/src/include/lru.h
@@ -0,0 +1,241 @@
+// -*- 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-2006 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_LRU_H
+#define CEPH_LRU_H
+
+#include <math.h>
+#include <stdint.h>
+
+#include "common/config.h"
+#include "xlist.h"
+
+class LRUObject {
+public:
+ LRUObject() : lru_link(this) {}
+ virtual ~LRUObject();
+
+ // pin/unpin item in cache
+ void lru_pin();
+ void lru_unpin();
+ bool lru_is_expireable() const { return !lru_pinned; }
+
+ friend class LRU;
+private:
+ class LRU *lru{};
+ xlist<LRUObject *>::item lru_link;
+ bool lru_pinned = false;
+};
+
+class LRU {
+public:
+ uint64_t lru_get_size() const { return lru_get_top()+lru_get_bot()+lru_get_pintail(); }
+ uint64_t lru_get_top() const { return top.size(); }
+ uint64_t lru_get_bot() const{ return bottom.size(); }
+ uint64_t lru_get_pintail() const { return pintail.size(); }
+ uint64_t lru_get_num_pinned() const { return num_pinned; }
+
+ void lru_set_midpoint(double f) { midpoint = fmin(1.0, fmax(0.0, f)); }
+
+ void lru_clear() {
+ while (!top.empty()) {
+ lru_remove(top.front());
+ }
+ while (!bottom.empty()) {
+ lru_remove(bottom.front());
+ }
+ while (!pintail.empty()) {
+ lru_remove(pintail.front());
+ }
+ ceph_assert(num_pinned == 0);
+ }
+
+ // insert at top of lru
+ void lru_insert_top(LRUObject *o) {
+ ceph_assert(!o->lru);
+ o->lru = this;
+ top.push_front(&o->lru_link);
+ if (o->lru_pinned) num_pinned++;
+ adjust();
+ }
+
+ // insert at mid point in lru
+ void lru_insert_mid(LRUObject *o) {
+ ceph_assert(!o->lru);
+ o->lru = this;
+ bottom.push_front(&o->lru_link);
+ if (o->lru_pinned) num_pinned++;
+ adjust();
+ }
+
+ // insert at bottom of lru
+ void lru_insert_bot(LRUObject *o) {
+ ceph_assert(!o->lru);
+ o->lru = this;
+ bottom.push_back(&o->lru_link);
+ if (o->lru_pinned) num_pinned++;
+ adjust();
+ }
+
+ // remove an item
+ LRUObject *lru_remove(LRUObject *o) {
+ if (!o->lru) return o;
+ auto list = o->lru_link.get_list();
+ ceph_assert(list == &top || list == &bottom || list == &pintail);
+ o->lru_link.remove_myself();
+ if (o->lru_pinned) num_pinned--;
+ o->lru = nullptr;
+ adjust();
+ return o;
+ }
+
+ // touch item -- move to head of lru
+ bool lru_touch(LRUObject *o) {
+ if (!o->lru) {
+ lru_insert_top(o);
+ } else {
+ ceph_assert(o->lru == this);
+ auto list = o->lru_link.get_list();
+ ceph_assert(list == &top || list == &bottom || list == &pintail);
+ top.push_front(&o->lru_link);
+ adjust();
+ }
+ return true;
+ }
+
+ // touch item -- move to midpoint (unless already higher)
+ bool lru_midtouch(LRUObject *o) {
+ if (!o->lru) {
+ lru_insert_mid(o);
+ } else {
+ ceph_assert(o->lru == this);
+ auto list = o->lru_link.get_list();
+ ceph_assert(list == &top || list == &bottom || list == &pintail);
+ if (list == &top) return false;
+ bottom.push_front(&o->lru_link);
+ adjust();
+ }
+ return true;
+ }
+
+ // touch item -- move to bottom
+ bool lru_bottouch(LRUObject *o) {
+ if (!o->lru) {
+ lru_insert_bot(o);
+ } else {
+ ceph_assert(o->lru == this);
+ auto list = o->lru_link.get_list();
+ ceph_assert(list == &top || list == &bottom || list == &pintail);
+ bottom.push_back(&o->lru_link);
+ adjust();
+ }
+ return true;
+ }
+
+ void lru_touch_entire_pintail() {
+ // promote entire pintail to the top lru
+ while (pintail.size() > 0) {
+ top.push_back(&pintail.front()->lru_link);
+ adjust();
+ }
+ }
+
+ // expire -- expire a single item
+ LRUObject *lru_get_next_expire() {
+ adjust();
+ // look through tail of bot
+ while (bottom.size()) {
+ LRUObject *p = bottom.back();
+ if (!p->lru_pinned) return p;
+
+ // move to pintail
+ pintail.push_front(&p->lru_link);
+ }
+
+ // ok, try head then
+ while (top.size()) {
+ LRUObject *p = top.back();
+ if (!p->lru_pinned) return p;
+
+ // move to pintail
+ pintail.push_front(&p->lru_link);
+ }
+
+ // no luck!
+ return NULL;
+ }
+
+ LRUObject *lru_expire() {
+ LRUObject *p = lru_get_next_expire();
+ if (p)
+ return lru_remove(p);
+ return NULL;
+ }
+
+ void lru_status() {
+ //generic_dout(10) << "lru: " << lru_get_size() << " items, " << top.size() << " top, " << bottom.size() << " bot, " << pintail.size() << " pintail" << dendl;
+ }
+
+protected:
+ // adjust top/bot balance, as necessary
+ void adjust() {
+ uint64_t toplen = top.size();
+ uint64_t topwant = (midpoint * (double)(lru_get_size() - num_pinned));
+ /* move items from below midpoint (bottom) to top: move midpoint forward */
+ for (uint64_t i = toplen; i < topwant; i++) {
+ top.push_back(&bottom.front()->lru_link);
+ }
+ /* or: move items from above midpoint (top) to bottom: move midpoint backwards */
+ for (uint64_t i = toplen; i > topwant; i--) {
+ bottom.push_front(&top.back()->lru_link);
+ }
+ }
+
+ uint64_t num_pinned = 0;
+ double midpoint = 0.6;
+
+ friend class LRUObject;
+private:
+ using LRUList = xlist<LRUObject*>;
+ LRUList top, bottom, pintail;
+};
+
+inline LRUObject::~LRUObject() {
+ if (lru) {
+ lru->lru_remove(this);
+ }
+}
+
+inline void LRUObject::lru_pin() {
+ if (lru && !lru_pinned) {
+ lru->num_pinned++;
+ }
+ lru_pinned = true;
+}
+
+inline void LRUObject::lru_unpin() {
+ if (lru && lru_pinned) {
+ lru->num_pinned--;
+
+ // move from pintail -> bot
+ if (lru_link.get_list() == &lru->pintail) {
+ lru->lru_bottouch(this);
+ }
+ }
+ lru_pinned = false;
+}
+
+#endif
diff --git a/src/include/mempool.h b/src/include/mempool.h
new file mode 100644
index 000000000..076c62afe
--- /dev/null
+++ b/src/include/mempool.h
@@ -0,0 +1,557 @@
+// -*- 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) 2016 Allen Samuels <allen.samuels@sandisk.com>
+ *
+ * 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_INCLUDE_MEMPOOL_H
+#define _CEPH_INCLUDE_MEMPOOL_H
+
+#include <cstddef>
+#include <map>
+#include <unordered_map>
+#include <set>
+#include <vector>
+#include <list>
+#include <mutex>
+#include <typeinfo>
+#include <boost/container/flat_set.hpp>
+#include <boost/container/flat_map.hpp>
+
+#include "common/Formatter.h"
+#include "common/ceph_atomic.h"
+#include "include/ceph_assert.h"
+#include "include/compact_map.h"
+#include "include/compact_set.h"
+#include "include/compat.h"
+
+
+/*
+
+Memory Pools
+============
+
+A memory pool is a method for accounting the consumption of memory of
+a set of containers.
+
+Memory pools are statically declared (see pool_index_t).
+
+Each memory pool tracks the number of bytes and items it contains.
+
+Allocators can be declared and associated with a type so that they are
+tracked independently of the pool total. This additional accounting
+is optional and only incurs an overhead if the debugging is enabled at
+runtime. This allows developers to see what types are consuming the
+pool resources.
+
+
+Declaring
+---------
+
+Using memory pools is very easy.
+
+To create a new memory pool, simply add a new name into the list of
+memory pools that's defined in "DEFINE_MEMORY_POOLS_HELPER". That's
+it. :)
+
+For each memory pool that's created a C++ namespace is also
+automatically created (name is same as in DEFINE_MEMORY_POOLS_HELPER).
+That namespace contains a set of common STL containers that are predefined
+with the appropriate allocators.
+
+Thus for mempool "osd" we have automatically available to us:
+
+ mempool::osd::map
+ mempool::osd::multimap
+ mempool::osd::set
+ mempool::osd::multiset
+ mempool::osd::list
+ mempool::osd::vector
+ mempool::osd::unordered_map
+
+
+Putting objects in a mempool
+----------------------------
+
+In order to use a memory pool with a particular type, a few additional
+declarations are needed.
+
+For a class:
+
+ struct Foo {
+ MEMPOOL_CLASS_HELPERS();
+ ...
+ };
+
+Then, in an appropriate .cc file,
+
+ MEMPOOL_DEFINE_OBJECT_FACTORY(Foo, foo, osd);
+
+The second argument can generally be identical to the first, except
+when the type contains a nested scope. For example, for
+BlueStore::Onode, we need to do
+
+ MEMPOOL_DEFINE_OBJECT_FACTORY(BlueStore::Onode, bluestore_onode,
+ bluestore_meta);
+
+(This is just because we need to name some static variables and we
+can't use :: in a variable name.)
+
+XXX Note: the new operator hard-codes the allocation size to the size of the
+object given in MEMPOOL_DEFINE_OBJECT_FACTORY. For this reason, you cannot
+incorporate mempools into a base class without also defining a helper/factory
+for the child class as well (as the base class is usually smaller than the
+child class).
+
+In order to use the STL containers, simply use the namespaced variant
+of the container type. For example,
+
+ mempool::osd::map<int> myvec;
+
+Introspection
+-------------
+
+The simplest way to interrogate the process is with
+
+ Formater *f = ...
+ mempool::dump(f);
+
+This will dump information about *all* memory pools. When debug mode
+is enabled, the runtime complexity of dump is O(num_shards *
+num_types). When debug name is disabled it is O(num_shards).
+
+You can also interrogate a specific pool programmatically with
+
+ size_t bytes = mempool::unittest_2::allocated_bytes();
+ size_t items = mempool::unittest_2::allocated_items();
+
+The runtime complexity is O(num_shards).
+
+Note that you cannot easily query per-type, primarily because debug
+mode is optional and you should not rely on that information being
+available.
+
+*/
+
+namespace mempool {
+
+// --------------------------------------------------------------
+// define memory pools
+
+#define DEFINE_MEMORY_POOLS_HELPER(f) \
+ f(bloom_filter) \
+ f(bluestore_alloc) \
+ f(bluestore_cache_data) \
+ f(bluestore_cache_onode) \
+ f(bluestore_cache_meta) \
+ f(bluestore_cache_other) \
+ f(bluestore_cache_buffer) \
+ f(bluestore_extent) \
+ f(bluestore_blob) \
+ f(bluestore_shared_blob) \
+ f(bluestore_inline_bl) \
+ f(bluestore_fsck) \
+ f(bluestore_txc) \
+ f(bluestore_writing_deferred) \
+ f(bluestore_writing) \
+ f(bluefs) \
+ f(bluefs_file_reader) \
+ f(bluefs_file_writer) \
+ f(buffer_anon) \
+ f(buffer_meta) \
+ f(osd) \
+ f(osd_mapbl) \
+ f(osd_pglog) \
+ f(osdmap) \
+ f(osdmap_mapping) \
+ f(pgmap) \
+ f(mds_co) \
+ f(unittest_1) \
+ f(unittest_2)
+
+
+// give them integer ids
+#define P(x) mempool_##x,
+enum pool_index_t {
+ DEFINE_MEMORY_POOLS_HELPER(P)
+ num_pools // Must be last.
+};
+#undef P
+
+extern bool debug_mode;
+extern void set_debug_mode(bool d);
+
+// --------------------------------------------------------------
+class pool_t;
+
+// we shard pool stats across many shard_t's to reduce the amount
+// of cacheline ping pong.
+enum {
+ num_shard_bits = 5
+};
+enum {
+ num_shards = 1 << num_shard_bits
+};
+
+//
+// Align shard to a cacheline.
+//
+// It would be possible to retrieve the value at runtime (for instance
+// with getconf LEVEL1_DCACHE_LINESIZE or grep -m1 cache_alignment
+// /proc/cpuinfo). It is easier to hard code the largest cache
+// linesize for all known processors (128 bytes). If the actual cache
+// linesize is smaller on a given processor, it will just waste a few
+// bytes.
+//
+struct shard_t {
+ ceph::atomic<size_t> bytes = {0};
+ ceph::atomic<size_t> items = {0};
+ char __padding[128 - sizeof(ceph::atomic<size_t>)*2];
+} __attribute__ ((aligned (128)));
+
+static_assert(sizeof(shard_t) == 128, "shard_t should be cacheline-sized");
+
+struct stats_t {
+ ssize_t items = 0;
+ ssize_t bytes = 0;
+ void dump(ceph::Formatter *f) const {
+ f->dump_int("items", items);
+ f->dump_int("bytes", bytes);
+ }
+
+ stats_t& operator+=(const stats_t& o) {
+ items += o.items;
+ bytes += o.bytes;
+ return *this;
+ }
+};
+
+pool_t& get_pool(pool_index_t ix);
+const char *get_pool_name(pool_index_t ix);
+
+struct type_t {
+ const char *type_name;
+ size_t item_size;
+ ceph::atomic<ssize_t> items = {0}; // signed
+};
+
+struct type_info_hash {
+ std::size_t operator()(const std::type_info& k) const {
+ return k.hash_code();
+ }
+};
+
+class pool_t {
+ shard_t shard[num_shards];
+
+ mutable std::mutex lock; // only used for types list
+ std::unordered_map<const char *, type_t> type_map;
+
+public:
+ //
+ // How much this pool consumes. O(<num_shards>)
+ //
+ size_t allocated_bytes() const;
+ size_t allocated_items() const;
+
+ void adjust_count(ssize_t items, ssize_t bytes);
+
+ static size_t pick_a_shard_int() {
+ // Dirt cheap, see:
+ // https://fossies.org/dox/glibc-2.32/pthread__self_8c_source.html
+ size_t me = (size_t)pthread_self();
+ size_t i = (me >> CEPH_PAGE_SHIFT) & ((1 << num_shard_bits) - 1);
+ return i;
+ }
+
+ shard_t* pick_a_shard() {
+ size_t i = pick_a_shard_int();
+ return &shard[i];
+ }
+
+ type_t *get_type(const std::type_info& ti, size_t size) {
+ std::lock_guard<std::mutex> l(lock);
+ auto p = type_map.find(ti.name());
+ if (p != type_map.end()) {
+ return &p->second;
+ }
+ type_t &t = type_map[ti.name()];
+ t.type_name = ti.name();
+ t.item_size = size;
+ return &t;
+ }
+
+ // get pool stats. by_type is not populated if !debug
+ void get_stats(stats_t *total,
+ std::map<std::string, stats_t> *by_type) const;
+
+ void dump(ceph::Formatter *f, stats_t *ptotal=0) const;
+};
+
+void dump(ceph::Formatter *f);
+
+
+// STL allocator for use with containers. All actual state
+// is stored in the static pool_allocator_base_t, which saves us from
+// passing the allocator to container constructors.
+
+template<pool_index_t pool_ix, typename T>
+class pool_allocator {
+ pool_t *pool;
+ type_t *type = nullptr;
+
+public:
+ typedef pool_allocator<pool_ix, T> allocator_type;
+ typedef T value_type;
+ typedef value_type *pointer;
+ typedef const value_type * const_pointer;
+ typedef value_type& reference;
+ typedef const value_type& const_reference;
+ typedef std::size_t size_type;
+ typedef std::ptrdiff_t difference_type;
+
+ template<typename U> struct rebind {
+ typedef pool_allocator<pool_ix,U> other;
+ };
+
+ void init(bool force_register) {
+ pool = &get_pool(pool_ix);
+ if (debug_mode || force_register) {
+ type = pool->get_type(typeid(T), sizeof(T));
+ }
+ }
+
+ pool_allocator(bool force_register=false) {
+ init(force_register);
+ }
+ template<typename U>
+ pool_allocator(const pool_allocator<pool_ix,U>&) {
+ init(false);
+ }
+
+ T* allocate(size_t n, void *p = nullptr) {
+ size_t total = sizeof(T) * n;
+ shard_t *shard = pool->pick_a_shard();
+ shard->bytes += total;
+ shard->items += n;
+ if (type) {
+ type->items += n;
+ }
+ T* r = reinterpret_cast<T*>(new char[total]);
+ return r;
+ }
+
+ void deallocate(T* p, size_t n) {
+ size_t total = sizeof(T) * n;
+ shard_t *shard = pool->pick_a_shard();
+ shard->bytes -= total;
+ shard->items -= n;
+ if (type) {
+ type->items -= n;
+ }
+ delete[] reinterpret_cast<char*>(p);
+ }
+
+ T* allocate_aligned(size_t n, size_t align, void *p = nullptr) {
+ size_t total = sizeof(T) * n;
+ shard_t *shard = pool->pick_a_shard();
+ shard->bytes += total;
+ shard->items += n;
+ if (type) {
+ type->items += n;
+ }
+ char *ptr;
+ int rc = ::posix_memalign((void**)(void*)&ptr, align, total);
+ if (rc)
+ throw std::bad_alloc();
+ T* r = reinterpret_cast<T*>(ptr);
+ return r;
+ }
+
+ void deallocate_aligned(T* p, size_t n) {
+ size_t total = sizeof(T) * n;
+ shard_t *shard = pool->pick_a_shard();
+ shard->bytes -= total;
+ shard->items -= n;
+ if (type) {
+ type->items -= n;
+ }
+ aligned_free(p);
+ }
+
+ void destroy(T* p) {
+ p->~T();
+ }
+
+ template<class U>
+ void destroy(U *p) {
+ p->~U();
+ }
+
+ void construct(T* p, const T& val) {
+ ::new ((void *)p) T(val);
+ }
+
+ template<class U, class... Args> void construct(U* p,Args&&... args) {
+ ::new((void *)p) U(std::forward<Args>(args)...);
+ }
+
+ bool operator==(const pool_allocator&) const { return true; }
+ bool operator!=(const pool_allocator&) const { return false; }
+};
+
+
+// Namespace mempool
+
+#define P(x) \
+ namespace x { \
+ static const mempool::pool_index_t id = mempool::mempool_##x; \
+ template<typename v> \
+ using pool_allocator = mempool::pool_allocator<id,v>; \
+ \
+ using string = std::basic_string<char,std::char_traits<char>, \
+ pool_allocator<char>>; \
+ \
+ template<typename k,typename v, typename cmp = std::less<k> > \
+ using map = std::map<k, v, cmp, \
+ pool_allocator<std::pair<const k,v>>>; \
+ \
+ template<typename k,typename v, typename cmp = std::less<k> > \
+ using compact_map = compact_map<k, v, cmp, \
+ pool_allocator<std::pair<const k,v>>>; \
+ \
+ template<typename k,typename v, typename cmp = std::less<k> > \
+ using compact_multimap = compact_multimap<k, v, cmp, \
+ pool_allocator<std::pair<const k,v>>>; \
+ \
+ template<typename k, typename cmp = std::less<k> > \
+ using compact_set = compact_set<k, cmp, pool_allocator<k>>; \
+ \
+ template<typename k,typename v, typename cmp = std::less<k> > \
+ using multimap = std::multimap<k,v,cmp, \
+ pool_allocator<std::pair<const k, \
+ v>>>; \
+ \
+ template<typename k, typename cmp = std::less<k> > \
+ using set = std::set<k,cmp,pool_allocator<k>>; \
+ \
+ template<typename k, typename cmp = std::less<k> > \
+ using flat_set = boost::container::flat_set<k,cmp,pool_allocator<k>>; \
+ \
+ template<typename k, typename v, typename cmp = std::less<k> > \
+ using flat_map = boost::container::flat_map<k,v,cmp, \
+ pool_allocator<std::pair<k,v>>>; \
+ \
+ template<typename v> \
+ using list = std::list<v,pool_allocator<v>>; \
+ \
+ template<typename v> \
+ using vector = std::vector<v,pool_allocator<v>>; \
+ \
+ template<typename k, typename v, \
+ typename h=std::hash<k>, \
+ typename eq = std::equal_to<k>> \
+ using unordered_map = \
+ std::unordered_map<k,v,h,eq,pool_allocator<std::pair<const k,v>>>;\
+ \
+ inline size_t allocated_bytes() { \
+ return mempool::get_pool(id).allocated_bytes(); \
+ } \
+ inline size_t allocated_items() { \
+ return mempool::get_pool(id).allocated_items(); \
+ } \
+ };
+
+DEFINE_MEMORY_POOLS_HELPER(P)
+
+#undef P
+
+};
+
+// the elements allocated by mempool is in the same memory space as the ones
+// allocated by the default allocator. so compare them in an efficient way:
+// libstdc++'s std::equal is specialized to use memcmp if T is integer or
+// pointer. this is good enough for our usecase. use
+// std::is_trivially_copyable<T> to expand the support to more types if
+// nececssary.
+template<typename T, mempool::pool_index_t pool_index>
+bool operator==(const std::vector<T, std::allocator<T>>& lhs,
+ const std::vector<T, mempool::pool_allocator<pool_index, T>>& rhs)
+{
+ return (lhs.size() == rhs.size() &&
+ std::equal(lhs.begin(), lhs.end(), rhs.begin()));
+}
+
+template<typename T, mempool::pool_index_t pool_index>
+bool operator!=(const std::vector<T, std::allocator<T>>& lhs,
+ const std::vector<T, mempool::pool_allocator<pool_index, T>>& rhs)
+{
+ return !(lhs == rhs);
+}
+
+template<typename T, mempool::pool_index_t pool_index>
+bool operator==(const std::vector<T, mempool::pool_allocator<pool_index, T>>& lhs,
+ const std::vector<T, std::allocator<T>>& rhs)
+{
+ return rhs == lhs;
+}
+
+template<typename T, mempool::pool_index_t pool_index>
+bool operator!=(const std::vector<T, mempool::pool_allocator<pool_index, T>>& lhs,
+ const std::vector<T, std::allocator<T>>& rhs)
+{
+ return !(lhs == rhs);
+}
+
+// Use this for any type that is contained by a container (unless it
+// is a class you defined; see below).
+#define MEMPOOL_DECLARE_FACTORY(obj, factoryname, pool) \
+ namespace mempool { \
+ namespace pool { \
+ extern pool_allocator<obj> alloc_##factoryname; \
+ } \
+ }
+
+#define MEMPOOL_DEFINE_FACTORY(obj, factoryname, pool) \
+ namespace mempool { \
+ namespace pool { \
+ pool_allocator<obj> alloc_##factoryname = {true}; \
+ } \
+ }
+
+// Use this for each class that belongs to a mempool. For example,
+//
+// class T {
+// MEMPOOL_CLASS_HELPERS();
+// ...
+// };
+//
+#define MEMPOOL_CLASS_HELPERS() \
+ void *operator new(size_t size); \
+ void *operator new[](size_t size) noexcept { \
+ ceph_abort_msg("no array new"); \
+ return nullptr; } \
+ void operator delete(void *); \
+ void operator delete[](void *) { ceph_abort_msg("no array delete"); }
+
+
+// Use this in some particular .cc file to match each class with a
+// MEMPOOL_CLASS_HELPERS().
+#define MEMPOOL_DEFINE_OBJECT_FACTORY(obj,factoryname,pool) \
+ MEMPOOL_DEFINE_FACTORY(obj, factoryname, pool) \
+ void *obj::operator new(size_t size) { \
+ return mempool::pool::alloc_##factoryname.allocate(1); \
+ } \
+ void obj::operator delete(void *p) { \
+ return mempool::pool::alloc_##factoryname.deallocate((obj*)p, 1); \
+ }
+
+#endif
diff --git a/src/include/msgr.h b/src/include/msgr.h
new file mode 100644
index 000000000..c8ad48ad1
--- /dev/null
+++ b/src/include/msgr.h
@@ -0,0 +1,255 @@
+#ifndef CEPH_MSGR_H
+#define CEPH_MSGR_H
+
+#ifndef __KERNEL__
+#include <sys/socket.h> // for struct sockaddr_storage
+#endif
+
+#include "include/int_types.h"
+
+/* See comment in ceph_fs.h. */
+#ifndef __KERNEL__
+#include "byteorder.h"
+#define __le16 ceph_le16
+#define __le32 ceph_le32
+#define __le64 ceph_le64
+#endif
+
+/*
+ * Data types for message passing layer used by Ceph.
+ */
+
+#define CEPH_MON_PORT_LEGACY 6789 /* legacy default monitor port */
+#define CEPH_MON_PORT_IANA 3300 /* IANA monitor port */
+
+/*
+ * tcp connection banner. include a protocol version. and adjust
+ * whenever the wire protocol changes. try to keep this string length
+ * constant.
+ */
+#define CEPH_BANNER "ceph v027"
+
+
+/*
+ * messenger V2 connection banner prefix.
+ * The full banner string should have the form: "ceph v2\n<le16>"
+ * the 2 bytes are the length of the remaining banner.
+ */
+#define CEPH_BANNER_V2_PREFIX "ceph v2\n"
+
+/*
+ * messenger V2 features
+ */
+#define CEPH_MSGR2_INCARNATION_1 (0ull)
+
+#define DEFINE_MSGR2_FEATURE(bit, incarnation, name) \
+ const static uint64_t CEPH_MSGR2_FEATURE_##name = (1ULL << bit); \
+ const static uint64_t CEPH_MSGR2_FEATUREMASK_##name = \
+ (1ULL << bit | CEPH_MSGR2_INCARNATION_##incarnation);
+
+#define HAVE_MSGR2_FEATURE(x, name) \
+ (((x) & (CEPH_MSGR2_FEATUREMASK_##name)) == (CEPH_MSGR2_FEATUREMASK_##name))
+
+DEFINE_MSGR2_FEATURE(0, 1, REVISION_1) // msgr2.1
+DEFINE_MSGR2_FEATURE(1, 1, COMPRESSION) // on-wire compression
+
+/*
+ * Features supported. Should be everything above.
+ */
+#define CEPH_MSGR2_SUPPORTED_FEATURES \
+ (CEPH_MSGR2_FEATURE_REVISION_1 | \
+ CEPH_MSGR2_FEATURE_COMPRESSION | \
+ 0ULL)
+
+#define CEPH_MSGR2_REQUIRED_FEATURES (0ULL)
+
+
+
+/*
+ * Rollover-safe type and comparator for 32-bit sequence numbers.
+ * Comparator returns -1, 0, or 1.
+ */
+typedef __u32 ceph_seq_t;
+
+static inline __s32 ceph_seq_cmp(__u32 a, __u32 b)
+{
+ return (__s32)a - (__s32)b;
+}
+
+
+/*
+ * entity_name -- logical name for a process participating in the
+ * network, e.g. 'mds0' or 'osd3'.
+ */
+struct ceph_entity_name {
+ __u8 type; /* CEPH_ENTITY_TYPE_* */
+ __le64 num;
+} __attribute__ ((packed));
+
+#define CEPH_ENTITY_TYPE_MON 0x01
+#define CEPH_ENTITY_TYPE_MDS 0x02
+#define CEPH_ENTITY_TYPE_OSD 0x04
+#define CEPH_ENTITY_TYPE_CLIENT 0x08
+#define CEPH_ENTITY_TYPE_MGR 0x10
+#define CEPH_ENTITY_TYPE_AUTH 0x20
+
+#define CEPH_ENTITY_TYPE_ANY 0xFF
+
+extern const char *ceph_entity_type_name(int type);
+
+/*
+ * entity_addr -- network address
+ */
+struct ceph_entity_addr {
+ __le32 type;
+ __le32 nonce; /* unique id for process (e.g. pid) */
+ struct sockaddr_storage in_addr;
+} __attribute__ ((packed));
+
+struct ceph_entity_inst {
+ struct ceph_entity_name name;
+ struct ceph_entity_addr addr;
+} __attribute__ ((packed));
+
+
+/* used by message exchange protocol */
+#define CEPH_MSGR_TAG_READY 1 /* server->client: ready for messages */
+#define CEPH_MSGR_TAG_RESETSESSION 2 /* server->client: reset, try again */
+#define CEPH_MSGR_TAG_WAIT 3 /* server->client: wait for racing
+ incoming connection */
+#define CEPH_MSGR_TAG_RETRY_SESSION 4 /* server->client + cseq: try again
+ with higher cseq */
+#define CEPH_MSGR_TAG_RETRY_GLOBAL 5 /* server->client + gseq: try again
+ with higher gseq */
+#define CEPH_MSGR_TAG_CLOSE 6 /* closing pipe */
+#define CEPH_MSGR_TAG_MSG 7 /* message */
+#define CEPH_MSGR_TAG_ACK 8 /* message ack */
+#define CEPH_MSGR_TAG_KEEPALIVE 9 /* just a keepalive byte! */
+#define CEPH_MSGR_TAG_BADPROTOVER 10 /* bad protocol version */
+#define CEPH_MSGR_TAG_BADAUTHORIZER 11 /* bad authorizer */
+#define CEPH_MSGR_TAG_FEATURES 12 /* insufficient features */
+#define CEPH_MSGR_TAG_SEQ 13 /* 64-bit int follows with seen seq number */
+#define CEPH_MSGR_TAG_KEEPALIVE2 14
+#define CEPH_MSGR_TAG_KEEPALIVE2_ACK 15 /* keepalive reply */
+#define CEPH_MSGR_TAG_CHALLENGE_AUTHORIZER 16 /* ceph v2 doing server challenge */
+
+/*
+ * connection negotiation
+ */
+struct ceph_msg_connect {
+ __le64 features; /* supported feature bits */
+ __le32 host_type; /* CEPH_ENTITY_TYPE_* */
+ __le32 global_seq; /* count connections initiated by this host */
+ __le32 connect_seq; /* count connections initiated in this session */
+ __le32 protocol_version;
+ __le32 authorizer_protocol;
+ __le32 authorizer_len;
+ __u8 flags; /* CEPH_MSG_CONNECT_* */
+} __attribute__ ((packed));
+
+struct ceph_msg_connect_reply {
+ __u8 tag;
+ __le64 features; /* feature bits for this session */
+ __le32 global_seq;
+ __le32 connect_seq;
+ __le32 protocol_version;
+ __le32 authorizer_len;
+ __u8 flags;
+} __attribute__ ((packed));
+
+#define CEPH_MSG_CONNECT_LOSSY 1 /* messages i send may be safely dropped */
+
+
+/*
+ * message header
+ */
+struct ceph_msg_header_old {
+ __le64 seq; /* message seq# for this session */
+ __le64 tid; /* transaction id */
+ __le16 type; /* message type */
+ __le16 priority; /* priority. higher value == higher priority */
+ __le16 version; /* version of message encoding */
+
+ __le32 front_len; /* bytes in main payload */
+ __le32 middle_len;/* bytes in middle payload */
+ __le32 data_len; /* bytes of data payload */
+ __le16 data_off; /* sender: include full offset;
+ receiver: mask against ~PAGE_MASK */
+
+ struct ceph_entity_inst src, orig_src;
+ __le32 reserved;
+ __le32 crc; /* header crc32c */
+} __attribute__ ((packed));
+
+struct ceph_msg_header {
+ __le64 seq; /* message seq# for this session */
+ __le64 tid; /* transaction id */
+ __le16 type; /* message type */
+ __le16 priority; /* priority. higher value == higher priority */
+ __le16 version; /* version of message encoding */
+
+ __le32 front_len; /* bytes in main payload */
+ __le32 middle_len;/* bytes in middle payload */
+ __le32 data_len; /* bytes of data payload */
+ __le16 data_off; /* sender: include full offset;
+ receiver: mask against ~PAGE_MASK */
+
+ struct ceph_entity_name src;
+
+ /* oldest code we think can decode this. unknown if zero. */
+ __le16 compat_version;
+ __le16 reserved;
+ __le32 crc; /* header crc32c */
+} __attribute__ ((packed));
+
+struct ceph_msg_header2 {
+ __le64 seq; /* message seq# for this session */
+ __le64 tid; /* transaction id */
+ __le16 type; /* message type */
+ __le16 priority; /* priority. higher value == higher priority */
+ __le16 version; /* version of message encoding */
+
+ __le32 data_pre_padding_len;
+ __le16 data_off; /* sender: include full offset;
+ receiver: mask against ~PAGE_MASK */
+
+ __le64 ack_seq;
+ __u8 flags;
+ /* oldest code we think can decode this. unknown if zero. */
+ __le16 compat_version;
+ __le16 reserved;
+} __attribute__ ((packed));
+
+#define CEPH_MSG_PRIO_LOW 64
+#define CEPH_MSG_PRIO_DEFAULT 127
+#define CEPH_MSG_PRIO_HIGH 196
+#define CEPH_MSG_PRIO_HIGHEST 255
+
+/*
+ * follows data payload
+ * ceph_msg_footer_old does not support digital signatures on messages PLR
+ */
+
+struct ceph_msg_footer_old {
+ __le32 front_crc, middle_crc, data_crc;
+ __u8 flags;
+} __attribute__ ((packed));
+
+struct ceph_msg_footer {
+ __le32 front_crc, middle_crc, data_crc;
+ // sig holds the 64 bits of the digital signature for the message PLR
+ __le64 sig;
+ __u8 flags;
+} __attribute__ ((packed));
+
+#define CEPH_MSG_FOOTER_COMPLETE (1<<0) /* msg wasn't aborted */
+#define CEPH_MSG_FOOTER_NOCRC (1<<1) /* no data crc */
+#define CEPH_MSG_FOOTER_SIGNED (1<<2) /* msg was signed */
+
+#ifndef __KERNEL__
+#undef __le16
+#undef __le32
+#undef __le64
+#endif
+
+#endif
diff --git a/src/include/neorados/RADOS.hpp b/src/include/neorados/RADOS.hpp
new file mode 100644
index 000000000..fa1ac92ae
--- /dev/null
+++ b/src/include/neorados/RADOS.hpp
@@ -0,0 +1,1150 @@
+// -*- 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) 2018 Red Hat <contact@redhat.com>
+ * Author: Adam C. Emerson
+ *
+ * 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 NEORADOS_RADOS_HPP
+#define NEORADOS_RADOS_HPP
+
+#include <cstddef>
+#include <memory>
+#include <tuple>
+#include <string>
+#include <string_view>
+#include <type_traits>
+#include <variant>
+
+#include <boost/asio.hpp>
+
+#include <boost/container/flat_map.hpp>
+#include <boost/container/flat_set.hpp>
+#include <boost/uuid/uuid.hpp>
+
+#include <boost/system/error_code.hpp>
+
+// Will be in C++20!
+
+#include "include/expected.hpp"
+
+// Had better be in C++20. Why is this not in Boost?
+
+#include "include/function2.hpp"
+
+// Things broken out so we can decode them in Objecter.
+
+#include "include/neorados/RADOS_Decodable.hpp"
+
+// Needed for type erasure and template support. We can't really avoid
+// it.
+
+#include "common/async/completion.h"
+
+// These are needed for RGW, but in general as a 'shiny new interface'
+// we should try to use forward declarations and provide standard alternatives.
+
+#include "include/common_fwd.h"
+
+#include "include/buffer.h"
+#include "include/rados/librados_fwd.hpp"
+
+#include "common/ceph_time.h"
+
+namespace neorados {
+class Object;
+class IOContext;
+}
+namespace std {
+template<>
+struct hash<neorados::Object>;
+template<>
+struct hash<neorados::IOContext>;
+}
+
+namespace neorados {
+namespace detail {
+class Client;
+}
+
+class RADOS;
+
+// Exists mostly so that repeated operations on the same object don't
+// have to pay for the string copy to construct an object_t.
+
+class Object final {
+ friend RADOS;
+ friend std::hash<Object>;
+
+public:
+ Object();
+ Object(const char* s);
+ Object(std::string_view s);
+ Object(std::string&& s);
+ Object(const std::string& s);
+ ~Object();
+
+ Object(const Object& o);
+ Object& operator =(const Object& o);
+
+ Object(Object&& o);
+ Object& operator =(Object&& o);
+
+ operator std::string_view() const;
+
+ friend std::ostream& operator <<(std::ostream& m, const Object& o);
+ friend bool operator <(const Object& lhs, const Object& rhs);
+ friend bool operator <=(const Object& lhs, const Object& rhs);
+ friend bool operator >=(const Object& lhs, const Object& rhs);
+ friend bool operator >(const Object& lhs, const Object& rhs);
+
+ friend bool operator ==(const Object& lhs, const Object& rhs);
+ friend bool operator !=(const Object& lhs, const Object& rhs);
+
+private:
+
+ static constexpr std::size_t impl_size = 4 * 8;
+ std::aligned_storage_t<impl_size> impl;
+};
+
+// Not the same as the librados::IoCtx, but it does gather together
+// some of the same metadata. Since we're likely to do multiple
+// operations in the same pool or namespace, it doesn't make sense to
+// redo a bunch of lookups and string copies.
+
+class IOContext final {
+ friend RADOS;
+ friend std::hash<IOContext>;
+
+public:
+
+ IOContext();
+ explicit IOContext(std::int64_t pool);
+ IOContext(std::int64_t _pool, std::string_view _ns);
+ IOContext(std::int64_t _pool, std::string&& _ns);
+ ~IOContext();
+
+ IOContext(const IOContext& rhs);
+ IOContext& operator =(const IOContext& rhs);
+
+ IOContext(IOContext&& rhs);
+ IOContext& operator =(IOContext&& rhs);
+
+ std::int64_t pool() const;
+ void pool(std::int64_t _pool);
+
+ std::string_view ns() const;
+ void ns(std::string_view _ns);
+ void ns(std::string&& _ns);
+
+ std::optional<std::string_view> key() const;
+ void key(std::string_view _key);
+ void key(std::string&& _key);
+ void clear_key();
+
+ std::optional<std::int64_t> hash() const;
+ void hash(std::int64_t _hash);
+ void clear_hash();
+
+ std::optional<std::uint64_t> read_snap() const;
+ void read_snap(std::optional<std::uint64_t> _snapid);
+
+ // I can't actually move-construct here since snapid_t is its own
+ // separate class type, not an alias.
+ std::optional<
+ std::pair<std::uint64_t,
+ std::vector<std::uint64_t>>> write_snap_context() const;
+ void write_snap_context(std::optional<
+ std::pair<std::uint64_t,
+ std::vector<std::uint64_t>>> snapc);
+
+ bool full_try() const;
+ void full_try(bool _full_try);
+
+ friend std::ostream& operator <<(std::ostream& m, const IOContext& o);
+ friend bool operator <(const IOContext& lhs, const IOContext& rhs);
+ friend bool operator <=(const IOContext& lhs, const IOContext& rhs);
+ friend bool operator >=(const IOContext& lhs, const IOContext& rhs);
+ friend bool operator >(const IOContext& lhs, const IOContext& rhs);
+
+ friend bool operator ==(const IOContext& lhs, const IOContext& rhs);
+ friend bool operator !=(const IOContext& lhs, const IOContext& rhs);
+
+private:
+
+ static constexpr std::size_t impl_size = 16 * 8;
+ std::aligned_storage_t<impl_size> impl;
+};
+
+inline constexpr std::string_view all_nspaces("\001");
+
+enum class cmpxattr_op : std::uint8_t {
+ eq = 1,
+ ne = 2,
+ gt = 3,
+ gte = 4,
+ lt = 5,
+ lte = 6
+};
+
+namespace alloc_hint {
+enum alloc_hint_t {
+ sequential_write = 1,
+ random_write = 2,
+ sequential_read = 4,
+ random_read = 8,
+ append_only = 16,
+ immutable = 32,
+ shortlived = 64,
+ longlived = 128,
+ compressible = 256,
+ incompressible = 512
+};
+}
+
+class Op {
+ friend RADOS;
+
+public:
+
+ Op(const Op&) = delete;
+ Op& operator =(const Op&) = delete;
+ Op(Op&&);
+ Op& operator =(Op&&);
+ ~Op();
+
+ void set_excl();
+ void set_failok();
+ void set_fadvise_random();
+ void set_fadvise_sequential();
+ void set_fadvise_willneed();
+ void set_fadvise_dontneed();
+ void set_fadvise_nocache();
+
+ void cmpext(uint64_t off, ceph::buffer::list&& cmp_bl, std::size_t* s);
+ void cmpxattr(std::string_view name, cmpxattr_op op,
+ const ceph::buffer::list& val);
+ void cmpxattr(std::string_view name, cmpxattr_op op, std::uint64_t val);
+ void assert_version(uint64_t ver);
+ void assert_exists();
+ void cmp_omap(const boost::container::flat_map<
+ std::string,
+ std::pair<ceph::buffer::list, int>>& assertions);
+
+ void exec(std::string_view cls, std::string_view method,
+ const ceph::buffer::list& inbl,
+ ceph::buffer::list* out,
+ boost::system::error_code* ec = nullptr);
+ void exec(std::string_view cls, std::string_view method,
+ const ceph::buffer::list& inbl,
+ fu2::unique_function<void(boost::system::error_code,
+ const ceph::buffer::list&) &&> f);
+ void exec(std::string_view cls, std::string_view method,
+ const ceph::buffer::list& inbl,
+ fu2::unique_function<void(boost::system::error_code, int,
+ const ceph::buffer::list&) &&> f);
+ void exec(std::string_view cls, std::string_view method,
+ const ceph::buffer::list& inbl,
+ boost::system::error_code* ec = nullptr);
+
+
+ // Flags that apply to all ops in the operation vector
+ void balance_reads();
+ void localize_reads();
+ void order_reads_writes();
+ void ignore_cache();
+ void skiprwlocks();
+ void ignore_overlay();
+ void full_try();
+ void full_force();
+ void ignore_redirect();
+ void ordersnap();
+ void returnvec();
+
+ std::size_t size() const;
+ using Signature = void(boost::system::error_code);
+ using Completion = ceph::async::Completion<Signature>;
+
+ friend std::ostream& operator <<(std::ostream& m, const Op& o);
+protected:
+ Op();
+ static constexpr std::size_t impl_size = 85 * 8;
+ std::aligned_storage_t<impl_size> impl;
+};
+
+// This class is /not/ thread-safe. If you want you can wrap it in
+// something that locks it.
+
+class ReadOp final : public Op {
+ friend RADOS;
+
+public:
+
+ ReadOp() = default;
+ ReadOp(const ReadOp&) = delete;
+ ReadOp(ReadOp&&) = default;
+
+ ReadOp& operator =(const ReadOp&) = delete;
+ ReadOp& operator =(ReadOp&&) = default;
+
+ void read(size_t off, uint64_t len, ceph::buffer::list* out,
+ boost::system::error_code* ec = nullptr);
+ void get_xattr(std::string_view name, ceph::buffer::list* out,
+ boost::system::error_code* ec = nullptr);
+ void get_omap_header(ceph::buffer::list*,
+ boost::system::error_code* ec = nullptr);
+
+ void sparse_read(uint64_t off, uint64_t len,
+ ceph::buffer::list* out,
+ std::vector<std::pair<std::uint64_t, std::uint64_t>>* extents,
+ boost::system::error_code* ec = nullptr);
+
+ void stat(std::uint64_t* size, ceph::real_time* mtime,
+ boost::system::error_code* ec = nullptr);
+
+ void get_omap_keys(std::optional<std::string_view> start_after,
+ std::uint64_t max_return,
+ boost::container::flat_set<std::string>* keys,
+ bool* truncated,
+ boost::system::error_code* ec = nullptr);
+
+
+ void get_xattrs(boost::container::flat_map<std::string,
+ ceph::buffer::list>* kv,
+ boost::system::error_code* ec = nullptr);
+
+ void get_omap_vals(std::optional<std::string_view> start_after,
+ std::optional<std::string_view> filter_prefix,
+ uint64_t max_return,
+ boost::container::flat_map<std::string,
+ ceph::buffer::list>* kv,
+ bool* truncated,
+ boost::system::error_code* ec = nullptr);
+
+
+ void get_omap_vals_by_keys(const boost::container::flat_set<std::string>& keys,
+ boost::container::flat_map<std::string,
+ ceph::buffer::list>* kv,
+ boost::system::error_code* ec = nullptr);
+
+ void list_watchers(std::vector<struct ObjWatcher>* watchers,
+ boost::system::error_code* ec = nullptr);
+
+ void list_snaps(struct SnapSet* snaps,
+ boost::system::error_code* ec = nullptr);
+};
+
+class WriteOp final : public Op {
+ friend RADOS;
+public:
+
+ WriteOp() = default;
+ WriteOp(const WriteOp&) = delete;
+ WriteOp(WriteOp&&) = default;
+
+ WriteOp& operator =(const WriteOp&) = delete;
+ WriteOp& operator =(WriteOp&&) = default;
+
+ void set_mtime(ceph::real_time t);
+ void create(bool exclusive);
+ void write(uint64_t off, ceph::buffer::list&& bl);
+ void write_full(ceph::buffer::list&& bl);
+ void writesame(std::uint64_t off, std::uint64_t write_len,
+ ceph::buffer::list&& bl);
+ void append(ceph::buffer::list&& bl);
+ void remove();
+ void truncate(uint64_t off);
+ void zero(uint64_t off, uint64_t len);
+ void rmxattr(std::string_view name);
+ void setxattr(std::string_view name,
+ ceph::buffer::list&& bl);
+ void rollback(uint64_t snapid);
+ void set_omap(const boost::container::flat_map<std::string,
+ ceph::buffer::list>& map);
+ void set_omap_header(ceph::buffer::list&& bl);
+ void clear_omap();
+ void rm_omap_keys(const boost::container::flat_set<std::string>& to_rm);
+ void set_alloc_hint(uint64_t expected_object_size,
+ uint64_t expected_write_size,
+ alloc_hint::alloc_hint_t flags);
+};
+
+
+struct FSStats {
+ uint64_t kb;
+ uint64_t kb_used;
+ uint64_t kb_avail;
+ uint64_t num_objects;
+};
+
+// From librados.h, maybe move into a common file. But I want to see
+// if we need/want to amend/add/remove anything first.
+struct PoolStats {
+ /// space used in bytes
+ uint64_t num_bytes;
+ /// space used in KB
+ uint64_t num_kb;
+ /// number of objects in the pool
+ uint64_t num_objects;
+ /// number of clones of objects
+ uint64_t num_object_clones;
+ /// num_objects * num_replicas
+ uint64_t num_object_copies;
+ /// number of objects missing on primary
+ uint64_t num_objects_missing_on_primary;
+ /// number of objects found on no OSDs
+ uint64_t num_objects_unfound;
+ /// number of objects replicated fewer times than they should be
+ /// (but found on at least one OSD)
+ uint64_t num_objects_degraded;
+ /// number of objects read
+ uint64_t num_rd;
+ /// objects read in KB
+ uint64_t num_rd_kb;
+ /// number of objects written
+ uint64_t num_wr;
+ /// objects written in KB
+ uint64_t num_wr_kb;
+ /// bytes originally provided by user
+ uint64_t num_user_bytes;
+ /// bytes passed compression
+ uint64_t compressed_bytes_orig;
+ /// bytes resulted after compression
+ uint64_t compressed_bytes;
+ /// bytes allocated at storage
+ uint64_t compressed_bytes_alloc;
+};
+
+// Placement group, for PG commands
+struct PG {
+ uint64_t pool;
+ uint32_t seed;
+};
+
+class Cursor final {
+public:
+ static Cursor begin();
+ static Cursor end();
+
+ Cursor();
+ Cursor(const Cursor&);
+ Cursor& operator =(const Cursor&);
+ Cursor(Cursor&&);
+ Cursor& operator =(Cursor&&);
+ ~Cursor();
+
+ friend bool operator ==(const Cursor& lhs,
+ const Cursor& rhs);
+ friend bool operator !=(const Cursor& lhs,
+ const Cursor& rhs);
+ friend bool operator <(const Cursor& lhs,
+ const Cursor& rhs);
+ friend bool operator <=(const Cursor& lhs,
+ const Cursor& rhs);
+ friend bool operator >=(const Cursor& lhs,
+ const Cursor& rhs);
+ friend bool operator >(const Cursor& lhs,
+ const Cursor& rhs);
+
+ std::string to_str() const;
+ static std::optional<Cursor> from_str(const std::string& s);
+
+private:
+ struct end_magic_t {};
+ Cursor(end_magic_t);
+ Cursor(void*);
+ friend RADOS;
+ static constexpr std::size_t impl_size = 16 * 8;
+ std::aligned_storage_t<impl_size> impl;
+};
+
+class RADOS final
+{
+public:
+ static constexpr std::tuple<uint32_t, uint32_t, uint32_t> version() {
+ return {0, 0, 1};
+ }
+
+ using BuildSig = void(boost::system::error_code, RADOS);
+ using BuildComp = ceph::async::Completion<BuildSig>;
+ class Builder {
+ std::optional<std::string> conf_files;
+ std::optional<std::string> cluster;
+ std::optional<std::string> name;
+ std::vector<std::pair<std::string, std::string>> configs;
+ bool no_default_conf = false;
+ bool no_mon_conf = false;
+
+ public:
+ Builder() = default;
+ Builder& add_conf_file(std::string_view v);
+ Builder& set_cluster(std::string_view c) {
+ cluster = std::string(c);
+ return *this;
+ }
+ Builder& set_name(std::string_view n) {
+ name = std::string(n);
+ return *this;
+ }
+ Builder& set_no_default_conf() {
+ no_default_conf = true;
+ return *this;
+ }
+ Builder& set_no_mon_conf() {
+ no_mon_conf = true;
+ return *this;
+ }
+ Builder& set_conf_option(std::string_view opt, std::string_view val) {
+ configs.emplace_back(std::string(opt), std::string(val));
+ return *this;
+ }
+
+ template<typename CompletionToken>
+ auto build(boost::asio::io_context& ioctx, CompletionToken&& token) {
+ boost::asio::async_completion<CompletionToken, BuildSig> init(token);
+ build(ioctx,
+ BuildComp::create(ioctx.get_executor(),
+ std::move(init.completion_handler)));
+ return init.result.get();
+ }
+
+ private:
+ void build(boost::asio::io_context& ioctx,
+ std::unique_ptr<BuildComp> c);
+ };
+
+
+ template<typename CompletionToken>
+ static auto make_with_cct(CephContext* cct,
+ boost::asio::io_context& ioctx,
+ CompletionToken&& token) {
+ boost::asio::async_completion<CompletionToken, BuildSig> init(token);
+ make_with_cct(cct, ioctx,
+ BuildComp::create(ioctx.get_executor(),
+ std::move(init.completion_handler)));
+ return init.result.get();
+ }
+
+ static RADOS make_with_librados(librados::Rados& rados);
+
+ RADOS(const RADOS&) = delete;
+ RADOS& operator =(const RADOS&) = delete;
+
+ RADOS(RADOS&&);
+ RADOS& operator =(RADOS&&);
+
+ ~RADOS();
+
+ CephContext* cct();
+
+ using executor_type = boost::asio::io_context::executor_type;
+ executor_type get_executor() const;
+ boost::asio::io_context& get_io_context();
+
+ template<typename CompletionToken>
+ auto execute(const Object& o, const IOContext& ioc, ReadOp&& op,
+ ceph::buffer::list* bl,
+ CompletionToken&& token, uint64_t* objver = nullptr,
+ const blkin_trace_info* trace_info = nullptr) {
+ boost::asio::async_completion<CompletionToken, Op::Signature> init(token);
+ execute(o, ioc, std::move(op), bl,
+ ReadOp::Completion::create(get_executor(),
+ std::move(init.completion_handler)),
+ objver, trace_info);
+ return init.result.get();
+ }
+
+ template<typename CompletionToken>
+ auto execute(const Object& o, const IOContext& ioc, WriteOp&& op,
+ CompletionToken&& token, uint64_t* objver = nullptr,
+ const blkin_trace_info* trace_info = nullptr) {
+ boost::asio::async_completion<CompletionToken, Op::Signature> init(token);
+ execute(o, ioc, std::move(op),
+ Op::Completion::create(get_executor(),
+ std::move(init.completion_handler)),
+ objver, trace_info);
+ return init.result.get();
+ }
+
+ template<typename CompletionToken>
+ auto execute(const Object& o, std::int64_t pool,
+ ReadOp&& op,
+ ceph::buffer::list* bl,
+ CompletionToken&& token,
+ std::optional<std::string_view> ns = {},
+ std::optional<std::string_view> key = {},
+ uint64_t* objver = nullptr) {
+ boost::asio::async_completion<CompletionToken, Op::Signature> init(token);
+ execute(o, pool, std::move(op), bl,
+ ReadOp::Completion::create(get_executor(),
+ std::move(init.completion_handler)),
+ ns, key, objver);
+ return init.result.get();
+ }
+
+ template<typename CompletionToken>
+ auto execute(const Object& o, std::int64_t pool, WriteOp&& op,
+ CompletionToken&& token,
+ std::optional<std::string_view> ns = {},
+ std::optional<std::string_view> key = {},
+ uint64_t* objver = nullptr) {
+ boost::asio::async_completion<CompletionToken, Op::Signature> init(token);
+ execute(o, pool, std::move(op),
+ Op::Completion::create(get_executor(),
+ std::move(init.completion_handler)),
+ ns, key, objver);
+ return init.result.get();
+ }
+
+ boost::uuids::uuid get_fsid() const noexcept;
+
+ using LookupPoolSig = void(boost::system::error_code,
+ std::int64_t);
+ using LookupPoolComp = ceph::async::Completion<LookupPoolSig>;
+ template<typename CompletionToken>
+ auto lookup_pool(std::string_view name,
+ CompletionToken&& token) {
+ boost::asio::async_completion<CompletionToken, LookupPoolSig> init(token);
+ lookup_pool(name,
+ LookupPoolComp::create(get_executor(),
+ std::move(init.completion_handler)));
+ return init.result.get();
+ }
+
+ std::optional<uint64_t> get_pool_alignment(int64_t pool_id);
+
+ using LSPoolsSig = void(std::vector<std::pair<std::int64_t, std::string>>);
+ using LSPoolsComp = ceph::async::Completion<LSPoolsSig>;
+ template<typename CompletionToken>
+ auto list_pools(CompletionToken&& token) {
+ boost::asio::async_completion<CompletionToken, LSPoolsSig> init(token);
+ list_pools(LSPoolsComp::create(get_executor(),
+ std::move(init.completion_handler)));
+ return init.result.get();
+ }
+
+
+
+ using SimpleOpSig = void(boost::system::error_code);
+ using SimpleOpComp = ceph::async::Completion<SimpleOpSig>;
+ template<typename CompletionToken>
+ auto create_pool_snap(int64_t pool, std::string_view snapName,
+ CompletionToken&& token) {
+ boost::asio::async_completion<CompletionToken, SimpleOpSig> init(token);
+ create_pool_snap(pool, snapName,
+ SimpleOpComp::create(get_executor(),
+ std::move(init.completion_handler)));
+ return init.result.get();
+ }
+
+ using SMSnapSig = void(boost::system::error_code, std::uint64_t);
+ using SMSnapComp = ceph::async::Completion<SMSnapSig>;
+ template<typename CompletionToken>
+ auto allocate_selfmanaged_snap(int64_t pool,
+ CompletionToken&& token) {
+ boost::asio::async_completion<CompletionToken, SMSnapSig> init(token);
+ allocate_selfmanaged_snap(pool,
+ SMSnapComp::create(
+ get_executor(),
+ std::move(init.completion_handler)));
+ return init.result.get();
+ }
+
+ template<typename CompletionToken>
+ auto delete_pool_snap(int64_t pool, std::string_view snapName,
+ CompletionToken&& token) {
+ boost::asio::async_completion<CompletionToken, SimpleOpSig> init(token);
+ delete_pool_snap(pool, snapName,
+ SimpleOpComp::create(get_executor(),
+ std::move(init.completion_handler)));
+ return init.result.get();
+ }
+
+ template<typename CompletionToken>
+ auto delete_selfmanaged_snap(int64_t pool, std::string_view snapName,
+ CompletionToken&& token) {
+ boost::asio::async_completion<CompletionToken, SimpleOpSig> init(token);
+ delete_selfmanaged_snap(pool, snapName,
+ SimpleOpComp::create(
+ get_executor(),
+ std::move(init.completion_handler)));
+ return init.result.get();
+ }
+
+ template<typename CompletionToken>
+ auto create_pool(std::string_view name, std::optional<int> crush_rule,
+ CompletionToken&& token) {
+ boost::asio::async_completion<CompletionToken, SimpleOpSig> init(token);
+ create_pool(name, crush_rule,
+ SimpleOpComp::create(get_executor(),
+ std::move(init.completion_handler)));
+ return init.result.get();
+ }
+
+ template<typename CompletionToken>
+ auto delete_pool(std::string_view name,
+ CompletionToken&& token) {
+ boost::asio::async_completion<CompletionToken, SimpleOpSig> init(token);
+ delete_pool(name,
+ SimpleOpComp::create(get_executor(),
+ std::move(init.completion_handler)));
+ return init.result.get();
+ }
+
+ template<typename CompletionToken>
+ auto delete_pool(int64_t pool,
+ CompletionToken&& token) {
+ boost::asio::async_completion<CompletionToken, SimpleOpSig> init(token);
+ delete_pool(pool,
+ SimpleOpComp::create(get_executor(),
+ std::move(init.completion_handler)));
+ return init.result.get();
+ }
+
+ using PoolStatSig = void(boost::system::error_code,
+ boost::container::flat_map<std::string,
+ PoolStats>, bool);
+ using PoolStatComp = ceph::async::Completion<PoolStatSig>;
+ template<typename CompletionToken>
+ auto stat_pools(const std::vector<std::string>& pools,
+ CompletionToken&& token) {
+ boost::asio::async_completion<CompletionToken, PoolStatSig> init(token);
+ stat_pools(pools,
+ PoolStatComp::create(get_executor(),
+ std::move(init.completion_handler)));
+ return init.result.get();
+ }
+
+ using StatFSSig = void(boost::system::error_code,
+ FSStats);
+ using StatFSComp = ceph::async::Completion<StatFSSig>;
+ template<typename CompletionToken>
+ auto statfs(std::optional<int64_t> pool,
+ CompletionToken&& token) {
+ boost::asio::async_completion<CompletionToken, StatFSSig> init(token);
+ ceph_statfs(pool, StatFSComp::create(get_executor(),
+ std::move(init.completion_handler)));
+ return init.result.get();
+ }
+
+ using WatchCB = fu2::unique_function<void(boost::system::error_code,
+ uint64_t notify_id,
+ uint64_t cookie,
+ uint64_t notifier_id,
+ ceph::buffer::list&& bl)>;
+
+ using WatchSig = void(boost::system::error_code ec,
+ uint64_t cookie);
+ using WatchComp = ceph::async::Completion<WatchSig>;
+ template<typename CompletionToken>
+ auto watch(const Object& o, const IOContext& ioc,
+ std::optional<std::chrono::seconds> timeout,
+ WatchCB&& cb, CompletionToken&& token) {
+ boost::asio::async_completion<CompletionToken, WatchSig> init(token);
+ watch(o, ioc, timeout, std::move(cb),
+ WatchComp::create(get_executor(),
+ std::move(init.completion_handler)));
+ return init.result.get();
+ }
+
+ template<typename CompletionToken>
+ auto watch(const Object& o, std::int64_t pool,
+ std::optional<std::chrono::seconds> timeout,
+ WatchCB&& cb, CompletionToken&& token,
+ std::optional<std::string_view> ns = {},
+ std::optional<std::string_view> key = {}) {
+ boost::asio::async_completion<CompletionToken, WatchSig> init(token);
+ watch(o, pool, timeout, std::move(cb),
+ WatchComp::create(get_executor(),
+ std::move(init.completion_handler)),
+ ns, key);
+ return init.result.get();
+ }
+
+ template<typename CompletionToken>
+ auto notify_ack(const Object& o,
+ const IOContext& ioc,
+ uint64_t notify_id,
+ uint64_t cookie,
+ ceph::buffer::list&& bl,
+ CompletionToken&& token) {
+ boost::asio::async_completion<CompletionToken, SimpleOpSig> init(token);
+ notify_ack(o, ioc, notify_id, cookie, std::move(bl),
+ SimpleOpComp::create(get_executor(),
+ std::move(init.completion_handler)));
+ return init.result.get();
+ }
+
+ template<typename CompletionToken>
+ auto notify_ack(const Object& o,
+ std::int64_t pool,
+ uint64_t notify_id,
+ uint64_t cookie,
+ ceph::buffer::list&& bl,
+ CompletionToken&& token,
+ std::optional<std::string_view> ns = {},
+ std::optional<std::string_view> key = {}) {
+ boost::asio::async_completion<CompletionToken, WatchSig> init(token);
+ notify_ack(o, pool, notify_id, cookie, std::move(bl),
+ SimpleOpComp::create(get_executor(),
+ std::move(init.completion_handler)),
+ ns, key);
+ return init.result.get();
+ }
+
+ template<typename CompletionToken>
+ auto unwatch(uint64_t cookie, const IOContext& ioc,
+ CompletionToken&& token) {
+ boost::asio::async_completion<CompletionToken, SimpleOpSig> init(token);
+ unwatch(cookie, ioc,
+ SimpleOpComp::create(get_executor(),
+ std::move(init.completion_handler)));
+ return init.result.get();
+ }
+
+ template<typename CompletionToken>
+ auto unwatch(uint64_t cookie, std::int64_t pool,
+ CompletionToken&& token,
+ std::optional<std::string_view> ns = {},
+ std::optional<std::string_view> key = {}) {
+ boost::asio::async_completion<CompletionToken, SimpleOpSig> init(token);
+ unwatch(cookie, pool,
+ SimpleOpComp::create(get_executor(),
+ std::move(init.completion_handler)),
+ ns, key);
+ return init.result.get();
+ }
+
+ // This is one of those places where having to force everything into
+ // a .cc file is really infuriating. If we had modules, that would
+ // let us separate out the implementation details without
+ // sacrificing all the benefits of templates.
+ using VoidOpSig = void();
+ using VoidOpComp = ceph::async::Completion<VoidOpSig>;
+ template<typename CompletionToken>
+ auto flush_watch(CompletionToken&& token) {
+ boost::asio::async_completion<CompletionToken, VoidOpSig> init(token);
+ flush_watch(VoidOpComp::create(get_executor(),
+ std::move(init.completion_handler)));
+ return init.result.get();
+ }
+
+ using NotifySig = void(boost::system::error_code, ceph::buffer::list);
+ using NotifyComp = ceph::async::Completion<NotifySig>;
+ template<typename CompletionToken>
+ auto notify(const Object& oid, const IOContext& ioc, ceph::buffer::list&& bl,
+ std::optional<std::chrono::milliseconds> timeout,
+ CompletionToken&& token) {
+ boost::asio::async_completion<CompletionToken, NotifySig> init(token);
+ notify(oid, ioc, std::move(bl), timeout,
+ NotifyComp::create(get_executor(),
+ std::move(init.completion_handler)));
+
+ return init.result.get();
+ }
+
+ template<typename CompletionToken>
+ auto notify(const Object& oid, std::int64_t pool, ceph::buffer::list&& bl,
+ std::optional<std::chrono::milliseconds> timeout,
+ CompletionToken&& token,
+ std::optional<std::string_view> ns = {},
+ std::optional<std::string_view> key = {}) {
+ boost::asio::async_completion<CompletionToken, NotifySig> init(token);
+ notify(oid, pool, bl, timeout,
+ NotifyComp::create(get_executor(),
+ std::move(init.completion_handler)),
+ ns, key);
+
+ return init.result.get();
+ }
+
+ // The versions with pointers are fine for coroutines, but
+ // extraordinarily unappealing for callback-oriented programming.
+ using EnumerateSig = void(boost::system::error_code,
+ std::vector<Entry>,
+ Cursor);
+ using EnumerateComp = ceph::async::Completion<EnumerateSig>;
+ template<typename CompletionToken>
+ auto enumerate_objects(const IOContext& ioc, const Cursor& begin,
+ const Cursor& end, const std::uint32_t max,
+ const ceph::buffer::list& filter,
+ CompletionToken&& token) {
+ boost::asio::async_completion<CompletionToken, EnumerateSig> init(token);
+ enumerate_objects(ioc, begin, end, max, filter,
+ EnumerateComp::create(get_executor(),
+ std::move(init.completion_handler)));
+ return init.result.get();
+ }
+
+ template<typename CompletionToken>
+ auto enumerate_objects(std::int64_t pool, const Cursor& begin,
+ const Cursor& end, const std::uint32_t max,
+ const ceph::buffer::list& filter,
+ CompletionToken&& token,
+ std::optional<std::string_view> ns = {},
+ std::optional<std::string_view> key = {}) {
+ boost::asio::async_completion<CompletionToken, EnumerateSig> init(token);
+ enumerate_objects(pool, begin, end, max, filter,
+ EnumerateComp::create(get_executor(),
+ std::move(init.completion_handler)),
+ ns, key);
+ return init.result.get();
+ }
+
+ using CommandSig = void(boost::system::error_code,
+ std::string, ceph::buffer::list);
+ using CommandComp = ceph::async::Completion<CommandSig>;
+ template<typename CompletionToken>
+ auto osd_command(int osd, std::vector<std::string>&& cmd,
+ ceph::buffer::list&& in, CompletionToken&& token) {
+ boost::asio::async_completion<CompletionToken, CommandSig> init(token);
+ osd_command(osd, std::move(cmd), std::move(in),
+ CommandComp::create(get_executor(),
+ std::move(init.completion_handler)));
+ return init.result.get();
+ }
+ template<typename CompletionToken>
+ auto pg_command(PG pg, std::vector<std::string>&& cmd,
+ ceph::buffer::list&& in, CompletionToken&& token) {
+ boost::asio::async_completion<CompletionToken, CommandSig> init(token);
+ pg_command(pg, std::move(cmd), std::move(in),
+ CommandComp::create(get_executor(),
+ std::move(init.completion_handler)));
+ return init.result.get();
+ }
+
+ template<typename CompletionToken>
+ auto mon_command(std::vector<std::string> command,
+ const ceph::buffer::list& bl,
+ std::string* outs, ceph::buffer::list* outbl,
+ CompletionToken&& token) {
+ boost::asio::async_completion<CompletionToken, SimpleOpSig> init(token);
+ mon_command(command, bl, outs, outbl,
+ SimpleOpComp::create(get_executor(),
+ std::move(init.completion_handler)));
+ return init.result.get();
+ }
+
+ template<typename CompletionToken>
+ auto enable_application(std::string_view pool, std::string_view app_name,
+ bool force, CompletionToken&& token) {
+ boost::asio::async_completion<CompletionToken, SimpleOpSig> init(token);
+ enable_application(pool, app_name, force,
+ SimpleOpComp::create(get_executor(),
+ std::move(init.completion_handler)));
+ return init.result.get();
+ }
+
+ template<typename CompletionToken>
+ auto blocklist_add(std::string_view client_address,
+ std::optional<std::chrono::seconds> expire,
+ CompletionToken&& token) {
+ boost::asio::async_completion<CompletionToken, SimpleOpSig> init(token);
+ blocklist_add(client_address, expire,
+ SimpleOpComp::create(get_executor(),
+ std::move(init.completion_handler)));
+ return init.result.get();
+ }
+
+ template<typename CompletionToken>
+ auto wait_for_latest_osd_map(CompletionToken&& token) {
+ boost::asio::async_completion<CompletionToken, SimpleOpSig> init(token);
+ wait_for_latest_osd_map(
+ SimpleOpComp::create(get_executor(), std::move(init.completion_handler)));
+ return init.result.get();
+ }
+
+ uint64_t instance_id() const;
+
+private:
+
+ RADOS();
+
+ friend Builder;
+
+ RADOS(std::unique_ptr<detail::Client> impl);
+ static void make_with_cct(CephContext* cct,
+ boost::asio::io_context& ioctx,
+ std::unique_ptr<BuildComp> c);
+
+ void execute(const Object& o, const IOContext& ioc, ReadOp&& op,
+ ceph::buffer::list* bl, std::unique_ptr<Op::Completion> c,
+ uint64_t* objver, const blkin_trace_info* trace_info);
+
+ void execute(const Object& o, const IOContext& ioc, WriteOp&& op,
+ std::unique_ptr<Op::Completion> c, uint64_t* objver,
+ const blkin_trace_info* trace_info);
+
+ void execute(const Object& o, std::int64_t pool, ReadOp&& op,
+ ceph::buffer::list* bl, std::unique_ptr<Op::Completion> c,
+ std::optional<std::string_view> ns,
+ std::optional<std::string_view> key,
+ uint64_t* objver);
+
+ void execute(const Object& o, std::int64_t pool, WriteOp&& op,
+ std::unique_ptr<Op::Completion> c,
+ std::optional<std::string_view> ns,
+ std::optional<std::string_view> key,
+ uint64_t* objver);
+
+ void lookup_pool(std::string_view name, std::unique_ptr<LookupPoolComp> c);
+ void list_pools(std::unique_ptr<LSPoolsComp> c);
+ void create_pool_snap(int64_t pool, std::string_view snapName,
+ std::unique_ptr<SimpleOpComp> c);
+ void allocate_selfmanaged_snap(int64_t pool, std::unique_ptr<SMSnapComp> c);
+ void delete_pool_snap(int64_t pool, std::string_view snapName,
+ std::unique_ptr<SimpleOpComp> c);
+ void delete_selfmanaged_snap(int64_t pool, std::uint64_t snap,
+ std::unique_ptr<SimpleOpComp> c);
+ void create_pool(std::string_view name, std::optional<int> crush_rule,
+ std::unique_ptr<SimpleOpComp> c);
+ void delete_pool(std::string_view name,
+ std::unique_ptr<SimpleOpComp> c);
+ void delete_pool(int64_t pool,
+ std::unique_ptr<SimpleOpComp> c);
+ void stat_pools(const std::vector<std::string>& pools,
+ std::unique_ptr<PoolStatComp> c);
+ void stat_fs(std::optional<std::int64_t> pool,
+ std::unique_ptr<StatFSComp> c);
+
+ void watch(const Object& o, const IOContext& ioc,
+ std::optional<std::chrono::seconds> timeout,
+ WatchCB&& cb, std::unique_ptr<WatchComp> c);
+ void watch(const Object& o, std::int64_t pool,
+ std::optional<std::chrono::seconds> timeout,
+ WatchCB&& cb, std::unique_ptr<WatchComp> c,
+ std::optional<std::string_view> ns,
+ std::optional<std::string_view> key);
+ tl::expected<ceph::timespan, boost::system::error_code>
+ watch_check(uint64_t cookie);
+ void notify_ack(const Object& o,
+ const IOContext& _ioc,
+ uint64_t notify_id,
+ uint64_t cookie,
+ ceph::buffer::list&& bl,
+ std::unique_ptr<SimpleOpComp>);
+ void notify_ack(const Object& o,
+ std::int64_t pool,
+ uint64_t notify_id,
+ uint64_t cookie,
+ ceph::buffer::list&& bl,
+ std::unique_ptr<SimpleOpComp>,
+ std::optional<std::string_view> ns,
+ std::optional<std::string_view> key);
+ void unwatch(uint64_t cookie, const IOContext& ioc,
+ std::unique_ptr<SimpleOpComp>);
+ void unwatch(uint64_t cookie, std::int64_t pool,
+ std::unique_ptr<SimpleOpComp>,
+ std::optional<std::string_view> ns,
+ std::optional<std::string_view> key);
+ void notify(const Object& oid, const IOContext& ioctx,
+ ceph::buffer::list&& bl,
+ std::optional<std::chrono::milliseconds> timeout,
+ std::unique_ptr<NotifyComp> c);
+ void notify(const Object& oid, std::int64_t pool,
+ ceph::buffer::list&& bl,
+ std::optional<std::chrono::milliseconds> timeout,
+ std::unique_ptr<NotifyComp> c,
+ std::optional<std::string_view> ns,
+ std::optional<std::string_view> key);
+ void flush_watch(std::unique_ptr<VoidOpComp>);
+
+ void enumerate_objects(const IOContext& ioc, const Cursor& begin,
+ const Cursor& end, const std::uint32_t max,
+ const ceph::buffer::list& filter,
+ std::vector<Entry>* ls,
+ Cursor* cursor,
+ std::unique_ptr<SimpleOpComp> c);
+ void enumerate_objects(std::int64_t pool, const Cursor& begin,
+ const Cursor& end, const std::uint32_t max,
+ const ceph::buffer::list& filter,
+ std::vector<Entry>* ls,
+ Cursor* cursor,
+ std::unique_ptr<SimpleOpComp> c,
+ std::optional<std::string_view> ns,
+ std::optional<std::string_view> key);
+ void enumerate_objects(const IOContext& ioc, const Cursor& begin,
+ const Cursor& end, const std::uint32_t max,
+ const ceph::buffer::list& filter,
+ std::unique_ptr<EnumerateComp> c);
+ void enumerate_objects(std::int64_t pool, const Cursor& begin,
+ const Cursor& end, const std::uint32_t max,
+ const ceph::buffer::list& filter,
+ std::unique_ptr<EnumerateComp> c,
+ std::optional<std::string_view> ns,
+ std::optional<std::string_view> key);
+ void osd_command(int osd, std::vector<std::string>&& cmd,
+ ceph::buffer::list&& in, std::unique_ptr<CommandComp> c);
+ void pg_command(PG pg, std::vector<std::string>&& cmd,
+ ceph::buffer::list&& in, std::unique_ptr<CommandComp> c);
+
+ void mon_command(std::vector<std::string> command,
+ const ceph::buffer::list& bl,
+ std::string* outs, ceph::buffer::list* outbl,
+ std::unique_ptr<SimpleOpComp> c);
+
+ void enable_application(std::string_view pool, std::string_view app_name,
+ bool force, std::unique_ptr<SimpleOpComp> c);
+
+ void blocklist_add(std::string_view client_address,
+ std::optional<std::chrono::seconds> expire,
+ std::unique_ptr<SimpleOpComp> c);
+
+ void wait_for_latest_osd_map(std::unique_ptr<SimpleOpComp> c);
+
+ // Proxy object to provide access to low-level RADOS messaging clients
+ std::unique_ptr<detail::Client> impl;
+};
+
+enum class errc {
+ pool_dne = 1,
+ invalid_snapcontext
+};
+
+const boost::system::error_category& error_category() noexcept;
+}
+
+namespace boost::system {
+template<>
+struct is_error_code_enum<::neorados::errc> {
+ static const bool value = true;
+};
+
+template<>
+struct is_error_condition_enum<::neorados::errc> {
+ static const bool value = false;
+};
+}
+
+namespace neorados {
+// explicit conversion:
+inline boost::system::error_code make_error_code(errc e) noexcept {
+ return { static_cast<int>(e), error_category() };
+}
+
+// implicit conversion:
+inline boost::system::error_condition make_error_condition(errc e) noexcept {
+ return { static_cast<int>(e), error_category() };
+}
+}
+
+namespace std {
+template<>
+struct hash<neorados::Object> {
+ size_t operator ()(const neorados::Object& r) const;
+};
+template<>
+struct hash<neorados::IOContext> {
+ size_t operator ()(const neorados::IOContext& r) const;
+};
+} // namespace std
+
+#endif // NEORADOS_RADOS_HPP
diff --git a/src/include/neorados/RADOS_Decodable.hpp b/src/include/neorados/RADOS_Decodable.hpp
new file mode 100644
index 000000000..83d065b3f
--- /dev/null
+++ b/src/include/neorados/RADOS_Decodable.hpp
@@ -0,0 +1,116 @@
+// -*- 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) 2020 Red Hat <contact@redhat.com>
+ * Author: Adam C. Emerson
+ *
+ * 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 NEORADOS_RADOS_DECODABLE_HPP
+#define NEORADOS_RADOS_DECODABLE_HPP
+
+#include <cstdint>
+#include <cstdlib>
+#include <string>
+#include <iostream>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+#include <fmt/core.h>
+#if FMT_VERSION >= 90000
+#include <fmt/ostream.h>
+#endif
+
+namespace neorados {
+struct Entry {
+ std::string nspace;
+ std::string oid;
+ std::string locator;
+
+ Entry() {}
+ Entry(std::string nspace, std::string oid, std::string locator) :
+ nspace(std::move(nspace)), oid(std::move(oid)), locator(locator) {}
+};
+inline bool operator ==(const Entry& l, const Entry r) {
+ return std::tie(l.nspace, l.oid, l.locator) ==
+ std::tie(r.nspace, r.oid, r.locator);
+}
+inline bool operator !=(const Entry& l, const Entry r) {
+ return std::tie(l.nspace, l.oid, l.locator) !=
+ std::tie(r.nspace, r.oid, r.locator);
+}
+inline bool operator <(const Entry& l, const Entry r) {
+ return std::tie(l.nspace, l.oid, l.locator) <
+ std::tie(r.nspace, r.oid, r.locator);
+}
+inline bool operator <=(const Entry& l, const Entry r) {
+ return std::tie(l.nspace, l.oid, l.locator) <=
+ std::tie(r.nspace, r.oid, r.locator);
+}
+inline bool operator >=(const Entry& l, const Entry r) {
+ return std::tie(l.nspace, l.oid, l.locator) >=
+ std::tie(r.nspace, r.oid, r.locator);
+}
+inline bool operator >(const Entry& l, const Entry r) {
+ return std::tie(l.nspace, l.oid, l.locator) >
+ std::tie(r.nspace, r.oid, r.locator);
+}
+
+inline std::ostream& operator <<(std::ostream& out, const Entry& entry) {
+ if (!entry.nspace.empty())
+ out << entry.nspace << '/';
+ out << entry.oid;
+ if (!entry.locator.empty())
+ out << '@' << entry.locator;
+ return out;
+}
+
+struct CloneInfo {
+ uint64_t cloneid = 0;
+ std::vector<uint64_t> snaps; // ascending
+ std::vector<std::pair<uint64_t, uint64_t>> overlap;// with next newest
+ uint64_t size = 0;
+ CloneInfo() = default;
+};
+
+struct SnapSet {
+ std::vector<CloneInfo> clones; // ascending
+ std::uint64_t seq = 0; // newest snapid seen by the object
+ SnapSet() = default;
+};
+
+struct ObjWatcher {
+ /// Address of the Watcher
+ std::string addr;
+ /// Watcher ID
+ std::int64_t watcher_id;
+ /// Cookie
+ std::uint64_t cookie;
+ /// Timeout in Seconds
+ std::uint32_t timeout_seconds;
+};
+}
+
+namespace std {
+template<>
+struct hash<::neorados::Entry> {
+ std::size_t operator ()(::neorados::Entry e) const {
+ hash<std::string> h;
+ return (h(e.nspace) << 2) ^ (h(e.oid) << 1) ^ h(e.locator);
+ }
+};
+}
+
+#if FMT_VERSION >= 90000
+template <> struct fmt::formatter<neorados::Entry> : ostream_formatter {};
+#endif
+
+#endif // RADOS_DECODABLE_HPP
diff --git a/src/include/neorados/buffer_fwd.h b/src/include/neorados/buffer_fwd.h
new file mode 120000
index 000000000..bd1f6f1b0
--- /dev/null
+++ b/src/include/neorados/buffer_fwd.h
@@ -0,0 +1 @@
+../buffer_fwd.h \ No newline at end of file
diff --git a/src/include/neorados/completion.h b/src/include/neorados/completion.h
new file mode 120000
index 000000000..100678fc2
--- /dev/null
+++ b/src/include/neorados/completion.h
@@ -0,0 +1 @@
+../../common/async/completion.h \ No newline at end of file
diff --git a/src/include/object.h b/src/include/object.h
new file mode 100644
index 000000000..4564af86e
--- /dev/null
+++ b/src/include/object.h
@@ -0,0 +1,189 @@
+// -*- 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-2006 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_OBJECT_H
+#define CEPH_OBJECT_H
+
+#include <cstdint>
+#include <cstdio>
+#include <iomanip>
+#include <iosfwd>
+#include <string>
+#include <string>
+#include <string_view>
+
+#include "include/rados.h"
+#include "include/unordered_map.h"
+
+#include "hash.h"
+#include "encoding.h"
+#include "ceph_hash.h"
+
+struct object_t {
+ std::string name;
+
+ object_t() {}
+ // cppcheck-suppress noExplicitConstructor
+ object_t(const char *s) : name(s) {}
+ // cppcheck-suppress noExplicitConstructor
+ object_t(const std::string& s) : name(s) {}
+ object_t(std::string&& s) : name(std::move(s)) {}
+ object_t(std::string_view s) : name(s) {}
+
+ auto operator<=>(const object_t&) const noexcept = default;
+
+ void swap(object_t& o) {
+ name.swap(o.name);
+ }
+ void clear() {
+ name.clear();
+ }
+
+ void encode(ceph::buffer::list &bl) const {
+ using ceph::encode;
+ encode(name, bl);
+ }
+ void decode(ceph::buffer::list::const_iterator &bl) {
+ using ceph::decode;
+ decode(name, bl);
+ }
+};
+WRITE_CLASS_ENCODER(object_t)
+
+inline std::ostream& operator<<(std::ostream& out, const object_t& o) {
+ return out << o.name;
+}
+
+namespace std {
+template<> struct hash<object_t> {
+ size_t operator()(const object_t& r) const {
+ //static hash<string> H;
+ //return H(r.name);
+ return ceph_str_hash_linux(r.name.c_str(), r.name.length());
+ }
+};
+} // namespace std
+
+
+struct file_object_t {
+ uint64_t ino, bno;
+ mutable char buf[34];
+
+ file_object_t(uint64_t i=0, uint64_t b=0) : ino(i), bno(b) {
+ buf[0] = 0;
+ }
+
+ const char *c_str() const {
+ if (!buf[0])
+ snprintf(buf, sizeof(buf), "%llx.%08llx", (long long unsigned)ino, (long long unsigned)bno);
+ return buf;
+ }
+
+ operator object_t() {
+ return object_t(c_str());
+ }
+};
+
+
+// ---------------------------
+// snaps
+
+struct snapid_t {
+ uint64_t val;
+ // cppcheck-suppress noExplicitConstructor
+ snapid_t(uint64_t v=0) : val(v) {}
+ snapid_t operator+=(snapid_t o) { val += o.val; return *this; }
+ snapid_t operator++() { ++val; return *this; }
+ operator uint64_t() const { return val; }
+};
+
+inline void encode(snapid_t i, ceph::buffer::list &bl) {
+ using ceph::encode;
+ encode(i.val, bl);
+}
+inline void decode(snapid_t &i, ceph::buffer::list::const_iterator &p) {
+ using ceph::decode;
+ decode(i.val, p);
+}
+
+template<>
+struct denc_traits<snapid_t> {
+ static constexpr bool supported = true;
+ static constexpr bool featured = false;
+ static constexpr bool bounded = true;
+ static constexpr bool need_contiguous = true;
+ static void bound_encode(const snapid_t& o, size_t& p) {
+ denc(o.val, p);
+ }
+ static void encode(const snapid_t &o, ceph::buffer::list::contiguous_appender& p) {
+ denc(o.val, p);
+ }
+ static void decode(snapid_t& o, ceph::buffer::ptr::const_iterator &p) {
+ denc(o.val, p);
+ }
+};
+
+inline std::ostream& operator<<(std::ostream& out, const snapid_t& s) {
+ if (s == CEPH_NOSNAP)
+ return out << "head";
+ else if (s == CEPH_SNAPDIR)
+ return out << "snapdir";
+ else
+ return out << std::hex << s.val << std::dec;
+}
+
+
+struct sobject_t {
+ object_t oid;
+ snapid_t snap;
+
+ sobject_t() : snap(0) {}
+ sobject_t(object_t o, snapid_t s) : oid(o), snap(s) {}
+
+ auto operator<=>(const sobject_t&) const noexcept = default;
+
+ void swap(sobject_t& o) {
+ oid.swap(o.oid);
+ snapid_t t = snap;
+ snap = o.snap;
+ o.snap = t;
+ }
+
+ void encode(ceph::buffer::list& bl) const {
+ using ceph::encode;
+ encode(oid, bl);
+ encode(snap, bl);
+ }
+ void decode(ceph::buffer::list::const_iterator& bl) {
+ using ceph::decode;
+ decode(oid, bl);
+ decode(snap, bl);
+ }
+};
+WRITE_CLASS_ENCODER(sobject_t)
+
+inline std::ostream& operator<<(std::ostream& out, const sobject_t &o) {
+ return out << o.oid << "/" << o.snap;
+}
+namespace std {
+template<> struct hash<sobject_t> {
+ size_t operator()(const sobject_t &r) const {
+ static hash<object_t> H;
+ static rjhash<uint64_t> I;
+ return H(r.oid) ^ I(r.snap);
+ }
+};
+} // namespace std
+
+#endif
diff --git a/src/include/object_fmt.h b/src/include/object_fmt.h
new file mode 100644
index 000000000..33df5e3fb
--- /dev/null
+++ b/src/include/object_fmt.h
@@ -0,0 +1,29 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+#pragma once
+
+/**
+ * \file fmtlib formatters for some object.h structs
+ */
+#include <fmt/format.h>
+
+#include "object.h"
+
+
+template <>
+struct fmt::formatter<snapid_t> {
+
+ constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }
+
+ template <typename FormatContext>
+ auto format(const snapid_t& snp, FormatContext& ctx) const
+ {
+ if (snp == CEPH_NOSNAP) {
+ return fmt::format_to(ctx.out(), "head");
+ }
+ if (snp == CEPH_SNAPDIR) {
+ return fmt::format_to(ctx.out(), "snapdir");
+ }
+ return fmt::format_to(ctx.out(), "{:x}", snp.val);
+ }
+};
diff --git a/src/include/on_exit.h b/src/include/on_exit.h
new file mode 100644
index 000000000..c412ab33e
--- /dev/null
+++ b/src/include/on_exit.h
@@ -0,0 +1,49 @@
+#ifndef CEPH_ON_EXIT_H
+#define CEPH_ON_EXIT_H
+
+#include <pthread.h>
+#include <vector>
+
+#include "include/ceph_assert.h"
+/*
+ * Create a static instance at the file level to get callbacks called when the
+ * process exits via main() or exit().
+ */
+
+class OnExitManager {
+ public:
+ typedef void (*callback_t)(void *arg);
+
+ OnExitManager() {
+ int ret = pthread_mutex_init(&lock_, NULL);
+ ceph_assert(ret == 0);
+ }
+
+ ~OnExitManager() {
+ pthread_mutex_lock(&lock_);
+ std::vector<struct cb>::iterator it;
+ for (it = funcs_.begin(); it != funcs_.end(); it++) {
+ it->func(it->arg);
+ }
+ funcs_.clear();
+ pthread_mutex_unlock(&lock_);
+ }
+
+ void add_callback(callback_t func, void *arg) {
+ pthread_mutex_lock(&lock_);
+ struct cb callback = { func, arg };
+ funcs_.push_back(callback);
+ pthread_mutex_unlock(&lock_);
+ }
+
+ private:
+ struct cb {
+ callback_t func;
+ void *arg;
+ };
+
+ std::vector<struct cb> funcs_;
+ pthread_mutex_t lock_;
+};
+
+#endif
diff --git a/src/include/page.h b/src/include/page.h
new file mode 100644
index 000000000..db6e20585
--- /dev/null
+++ b/src/include/page.h
@@ -0,0 +1,18 @@
+#ifndef CEPH_PAGE_H
+#define CEPH_PAGE_H
+
+namespace ceph {
+ // these are in common/page.cc
+ extern unsigned _page_size;
+ extern unsigned long _page_mask;
+ extern unsigned _page_shift;
+}
+
+#endif
+
+
+#define CEPH_PAGE_SIZE ceph::_page_size
+#define CEPH_PAGE_MASK ceph::_page_mask
+#define CEPH_PAGE_SHIFT ceph::_page_shift
+
+
diff --git a/src/include/rados.h b/src/include/rados.h
new file mode 100644
index 000000000..eac3a2159
--- /dev/null
+++ b/src/include/rados.h
@@ -0,0 +1,700 @@
+#ifndef CEPH_RADOS_H
+#define CEPH_RADOS_H
+
+/*
+ * Data types for the Ceph distributed object storage layer RADOS
+ * (Reliable Autonomic Distributed Object Store).
+ */
+
+#include <string.h>
+#include <stdbool.h>
+#include "msgr.h"
+
+/* See comment in ceph_fs.h. */
+#ifndef __KERNEL__
+#include "byteorder.h"
+#define __le16 ceph_le16
+#define __le32 ceph_le32
+#define __le64 ceph_le64
+#endif
+
+/*
+ * fs id
+ */
+struct ceph_fsid {
+ unsigned char fsid[16];
+};
+
+static inline int ceph_fsid_compare(const struct ceph_fsid *a,
+ const struct ceph_fsid *b)
+{
+ return memcmp(a, b, sizeof(*a));
+}
+
+/*
+ * ino, object, etc.
+ */
+typedef __le64 ceph_snapid_t;
+#define CEPH_SNAPDIR ((__u64)(-1)) /* reserved for hidden .snap dir */
+#define CEPH_NOSNAP ((__u64)(-2)) /* "head", "live" revision */
+#define CEPH_MAXSNAP ((__u64)(-3)) /* largest valid snapid */
+
+struct ceph_timespec {
+ __le32 tv_sec;
+ __le32 tv_nsec;
+} __attribute__ ((packed));
+
+
+/*
+ * object layout - how objects are mapped into PGs
+ */
+#define CEPH_OBJECT_LAYOUT_HASH 1
+#define CEPH_OBJECT_LAYOUT_LINEAR 2
+#define CEPH_OBJECT_LAYOUT_HASHINO 3
+
+/*
+ * pg layout -- how PGs are mapped onto (sets of) OSDs
+ */
+#define CEPH_PG_LAYOUT_CRUSH 0
+#define CEPH_PG_LAYOUT_HASH 1
+#define CEPH_PG_LAYOUT_LINEAR 2
+#define CEPH_PG_LAYOUT_HYBRID 3
+
+#define CEPH_PG_MAX_SIZE 16 /* max # osds in a single pg */
+
+/*
+ * placement group.
+ * we encode this into one __le64.
+ */
+struct ceph_pg {
+ __le16 preferred; /* preferred primary osd */
+ __le16 ps; /* placement seed */
+ __le32 pool; /* object pool */
+} __attribute__ ((packed));
+
+/*
+ * pg pool types
+ *
+ * NOTE: These map 1:1 on to the pg_pool_t::TYPE_* values. They are
+ * duplicated here only for CrushCompiler's benefit.
+ */
+#define CEPH_PG_TYPE_REPLICATED 1
+/* #define CEPH_PG_TYPE_RAID4 2 never implemented */
+#define CEPH_PG_TYPE_ERASURE 3
+
+/*
+ * stable_mod func is used to control number of placement groups.
+ * similar to straight-up modulo, but produces a stable mapping as b
+ * increases over time. b is the number of bins, and bmask is the
+ * containing power of 2 minus 1.
+ *
+ * b <= bmask and bmask=(2**n)-1
+ * e.g., b=12 -> bmask=15, b=123 -> bmask=127
+ *
+ * ** This function is released to the public domain by the author. **
+ */
+static inline int ceph_stable_mod(int x, int b, int bmask)
+{
+ if ((x & bmask) < b)
+ return x & bmask;
+ else
+ return x & (bmask >> 1);
+}
+
+/*
+ * object layout - how a given object should be stored.
+ */
+struct ceph_object_layout {
+ struct ceph_pg ol_pgid; /* raw pg, with _full_ ps precision. */
+ __le32 ol_stripe_unit; /* for per-object parity, if any */
+} __attribute__ ((packed));
+
+/*
+ * compound epoch+version, used by storage layer to serialize mutations
+ */
+struct ceph_eversion {
+ __le32 epoch;
+ __le64 version;
+} __attribute__ ((packed));
+
+/*
+ * osd map bits
+ */
+
+/* status bits */
+#define CEPH_OSD_EXISTS (1<<0)
+#define CEPH_OSD_UP (1<<1)
+#define CEPH_OSD_AUTOOUT (1<<2) /* osd was automatically marked out */
+#define CEPH_OSD_NEW (1<<3) /* osd is new, never marked in */
+#define CEPH_OSD_FULL (1<<4) /* osd is at or above full threshold */
+#define CEPH_OSD_NEARFULL (1<<5) /* osd is at or above nearfull threshold */
+#define CEPH_OSD_BACKFILLFULL (1<<6) /* osd is at or above backfillfull threshold */
+#define CEPH_OSD_DESTROYED (1<<7) /* osd has been destroyed */
+#define CEPH_OSD_NOUP (1<<8) /* osd can not be marked up */
+#define CEPH_OSD_NODOWN (1<<9) /* osd can not be marked down */
+#define CEPH_OSD_NOIN (1<<10) /* osd can not be marked in */
+#define CEPH_OSD_NOOUT (1<<11) /* osd can not be marked out */
+#define CEPH_OSD_STOP (1<<12) /* osd has been stopped by admin */
+
+extern const char *ceph_osd_state_name(int s);
+
+/* osd weights. fixed point value: 0x10000 == 1.0 ("in"), 0 == "out" */
+#define CEPH_OSD_IN 0x10000
+#define CEPH_OSD_OUT 0
+
+#define CEPH_OSD_MAX_PRIMARY_AFFINITY 0x10000
+#define CEPH_OSD_DEFAULT_PRIMARY_AFFINITY 0x10000
+
+
+/*
+ * osd map flag bits
+ */
+#define CEPH_OSDMAP_NEARFULL (1<<0) /* sync writes (near ENOSPC), deprecated since mimic*/
+#define CEPH_OSDMAP_FULL (1<<1) /* no data writes (ENOSPC), deprecated since mimic */
+#define CEPH_OSDMAP_PAUSERD (1<<2) /* pause all reads */
+#define CEPH_OSDMAP_PAUSEWR (1<<3) /* pause all writes */
+#define CEPH_OSDMAP_PAUSEREC (1<<4) /* pause recovery */
+#define CEPH_OSDMAP_NOUP (1<<5) /* block osd boot */
+#define CEPH_OSDMAP_NODOWN (1<<6) /* block osd mark-down/failure */
+#define CEPH_OSDMAP_NOOUT (1<<7) /* block osd auto mark-out */
+#define CEPH_OSDMAP_NOIN (1<<8) /* block osd auto mark-in */
+#define CEPH_OSDMAP_NOBACKFILL (1<<9) /* block osd backfill */
+#define CEPH_OSDMAP_NORECOVER (1<<10) /* block osd recovery and backfill */
+#define CEPH_OSDMAP_NOSCRUB (1<<11) /* block periodic scrub */
+#define CEPH_OSDMAP_NODEEP_SCRUB (1<<12) /* block periodic deep-scrub */
+#define CEPH_OSDMAP_NOTIERAGENT (1<<13) /* disable tiering agent */
+#define CEPH_OSDMAP_NOREBALANCE (1<<14) /* block osd backfill unless pg is degraded */
+#define CEPH_OSDMAP_SORTBITWISE (1<<15) /* use bitwise hobject_t sort */
+#define CEPH_OSDMAP_REQUIRE_JEWEL (1<<16) /* require jewel for booting osds */
+#define CEPH_OSDMAP_REQUIRE_KRAKEN (1<<17) /* require kraken for booting osds */
+#define CEPH_OSDMAP_REQUIRE_LUMINOUS (1<<18) /* require l for booting osds */
+#define CEPH_OSDMAP_RECOVERY_DELETES (1<<19) /* deletes performed during recovery instead of peering */
+#define CEPH_OSDMAP_PURGED_SNAPDIRS (1<<20) /* osds have converted snapsets */
+#define CEPH_OSDMAP_NOSNAPTRIM (1<<21) /* disable snap trimming */
+#define CEPH_OSDMAP_PGLOG_HARDLIMIT (1<<22) /* put a hard limit on pg log length */
+#define CEPH_OSDMAP_NOAUTOSCALE (1<<23) /* block pg autoscale */
+
+/* these are hidden in 'ceph status' view */
+#define CEPH_OSDMAP_SEMIHIDDEN_FLAGS (CEPH_OSDMAP_REQUIRE_JEWEL| \
+ CEPH_OSDMAP_REQUIRE_KRAKEN | \
+ CEPH_OSDMAP_REQUIRE_LUMINOUS | \
+ CEPH_OSDMAP_RECOVERY_DELETES | \
+ CEPH_OSDMAP_SORTBITWISE | \
+ CEPH_OSDMAP_PURGED_SNAPDIRS | \
+ CEPH_OSDMAP_PGLOG_HARDLIMIT)
+#define CEPH_OSDMAP_LEGACY_REQUIRE_FLAGS (CEPH_OSDMAP_REQUIRE_JEWEL | \
+ CEPH_OSDMAP_REQUIRE_KRAKEN | \
+ CEPH_OSDMAP_REQUIRE_LUMINOUS)
+
+/*
+ * major ceph release numbers
+ */
+#define CEPH_RELEASE_ARGONAUT 1
+#define CEPH_RELEASE_BOBTAIL 2
+#define CEPH_RELEASE_CUTTLEFISH 3
+#define CEPH_RELEASE_DUMPLING 4
+#define CEPH_RELEASE_EMPEROR 5
+#define CEPH_RELEASE_FIREFLY 6
+#define CEPH_RELEASE_GIANT 7
+#define CEPH_RELEASE_HAMMER 8
+#define CEPH_RELEASE_INFERNALIS 9
+#define CEPH_RELEASE_JEWEL 10
+#define CEPH_RELEASE_KRAKEN 11
+#define CEPH_RELEASE_LUMINOUS 12
+#define CEPH_RELEASE_MIMIC 13
+#define CEPH_RELEASE_NAUTILUS 14
+#define CEPH_RELEASE_OCTOPUS 15
+#define CEPH_RELEASE_PACIFIC 16
+#define CEPH_RELEASE_QUINCY 17
+#define CEPH_RELEASE_REEF 18
+#define CEPH_RELEASE_MAX 19 /* highest + 1 */
+
+/*
+ * The error code to return when an OSD can't handle a write
+ * because it is too large.
+ */
+#define OSD_WRITETOOBIG EMSGSIZE
+
+/*
+ * osd ops
+ *
+ * WARNING: do not use these op codes directly. Use the helpers
+ * defined below instead. In certain cases, op code behavior was
+ * redefined, resulting in special-cases in the helpers.
+ */
+#define CEPH_OSD_OP_MODE 0xf000
+#define CEPH_OSD_OP_MODE_RD 0x1000
+#define CEPH_OSD_OP_MODE_WR 0x2000
+#define CEPH_OSD_OP_MODE_RMW 0x3000
+#define CEPH_OSD_OP_MODE_SUB 0x4000
+#define CEPH_OSD_OP_MODE_CACHE 0x8000
+
+#define CEPH_OSD_OP_TYPE 0x0f00
+#define CEPH_OSD_OP_TYPE_DATA 0x0200
+#define CEPH_OSD_OP_TYPE_ATTR 0x0300
+#define CEPH_OSD_OP_TYPE_EXEC 0x0400
+#define CEPH_OSD_OP_TYPE_PG 0x0500
+// LEAVE UNUSED 0x0600 used to be multiobject ops
+
+#define __CEPH_OSD_OP1(mode, nr) \
+ (CEPH_OSD_OP_MODE_##mode | (nr))
+
+#define __CEPH_OSD_OP(mode, type, nr) \
+ (CEPH_OSD_OP_MODE_##mode | CEPH_OSD_OP_TYPE_##type | (nr))
+
+#define __CEPH_FORALL_OSD_OPS(f) \
+ /** data **/ \
+ /* read */ \
+ f(READ, __CEPH_OSD_OP(RD, DATA, 1), "read") \
+ f(STAT, __CEPH_OSD_OP(RD, DATA, 2), "stat") \
+ f(MAPEXT, __CEPH_OSD_OP(RD, DATA, 3), "mapext") \
+ f(CHECKSUM, __CEPH_OSD_OP(RD, DATA, 31), "checksum") \
+ \
+ /* fancy read */ \
+ f(MASKTRUNC, __CEPH_OSD_OP(RD, DATA, 4), "masktrunc") \
+ f(SPARSE_READ, __CEPH_OSD_OP(RD, DATA, 5), "sparse-read") \
+ \
+ f(NOTIFY, __CEPH_OSD_OP(RD, DATA, 6), "notify") \
+ f(NOTIFY_ACK, __CEPH_OSD_OP(RD, DATA, 7), "notify-ack") \
+ \
+ /* versioning */ \
+ f(ASSERT_VER, __CEPH_OSD_OP(RD, DATA, 8), "assert-version") \
+ \
+ f(LIST_WATCHERS, __CEPH_OSD_OP(RD, DATA, 9), "list-watchers") \
+ \
+ f(LIST_SNAPS, __CEPH_OSD_OP(RD, DATA, 10), "list-snaps") \
+ \
+ /* sync */ \
+ f(SYNC_READ, __CEPH_OSD_OP(RD, DATA, 11), "sync_read") \
+ \
+ /* write */ \
+ f(WRITE, __CEPH_OSD_OP(WR, DATA, 1), "write") \
+ f(WRITEFULL, __CEPH_OSD_OP(WR, DATA, 2), "writefull") \
+ f(TRUNCATE, __CEPH_OSD_OP(WR, DATA, 3), "truncate") \
+ f(ZERO, __CEPH_OSD_OP(WR, DATA, 4), "zero") \
+ f(DELETE, __CEPH_OSD_OP(WR, DATA, 5), "delete") \
+ \
+ /* fancy write */ \
+ f(APPEND, __CEPH_OSD_OP(WR, DATA, 6), "append") \
+ f(STARTSYNC, __CEPH_OSD_OP(WR, DATA, 7), "startsync") \
+ f(SETTRUNC, __CEPH_OSD_OP(WR, DATA, 8), "settrunc") \
+ f(TRIMTRUNC, __CEPH_OSD_OP(WR, DATA, 9), "trimtrunc") \
+ \
+ f(TMAPUP, __CEPH_OSD_OP(RMW, DATA, 10), "tmapup") \
+ f(TMAPPUT, __CEPH_OSD_OP(WR, DATA, 11), "tmapput") \
+ f(TMAPGET, __CEPH_OSD_OP(RD, DATA, 12), "tmapget") \
+ \
+ f(CREATE, __CEPH_OSD_OP(WR, DATA, 13), "create") \
+ f(ROLLBACK, __CEPH_OSD_OP(WR, DATA, 14), "rollback") \
+ \
+ f(WATCH, __CEPH_OSD_OP(WR, DATA, 15), "watch") \
+ \
+ /* omap */ \
+ f(OMAPGETKEYS, __CEPH_OSD_OP(RD, DATA, 17), "omap-get-keys") \
+ f(OMAPGETVALS, __CEPH_OSD_OP(RD, DATA, 18), "omap-get-vals") \
+ f(OMAPGETHEADER, __CEPH_OSD_OP(RD, DATA, 19), "omap-get-header") \
+ f(OMAPGETVALSBYKEYS, __CEPH_OSD_OP(RD, DATA, 20), "omap-get-vals-by-keys") \
+ f(OMAPSETVALS, __CEPH_OSD_OP(WR, DATA, 21), "omap-set-vals") \
+ f(OMAPSETHEADER, __CEPH_OSD_OP(WR, DATA, 22), "omap-set-header") \
+ f(OMAPCLEAR, __CEPH_OSD_OP(WR, DATA, 23), "omap-clear") \
+ f(OMAPRMKEYS, __CEPH_OSD_OP(WR, DATA, 24), "omap-rm-keys") \
+ f(OMAPRMKEYRANGE, __CEPH_OSD_OP(WR, DATA, 44), "omap-rm-key-range") \
+ f(OMAP_CMP, __CEPH_OSD_OP(RD, DATA, 25), "omap-cmp") \
+ \
+ /* tiering */ \
+ f(COPY_FROM, __CEPH_OSD_OP(WR, DATA, 26), "copy-from") \
+ f(COPY_FROM2, __CEPH_OSD_OP(WR, DATA, 45), "copy-from2") \
+ /* was copy-get-classic */ \
+ f(UNDIRTY, __CEPH_OSD_OP(WR, DATA, 28), "undirty") \
+ f(ISDIRTY, __CEPH_OSD_OP(RD, DATA, 29), "isdirty") \
+ f(COPY_GET, __CEPH_OSD_OP(RD, DATA, 30), "copy-get") \
+ f(CACHE_FLUSH, __CEPH_OSD_OP(CACHE, DATA, 31), "cache-flush") \
+ f(CACHE_EVICT, __CEPH_OSD_OP(CACHE, DATA, 32), "cache-evict") \
+ f(CACHE_TRY_FLUSH, __CEPH_OSD_OP(CACHE, DATA, 33), "cache-try-flush") \
+ \
+ /* convert tmap to omap */ \
+ f(TMAP2OMAP, __CEPH_OSD_OP(RMW, DATA, 34), "tmap2omap") \
+ \
+ /* hints */ \
+ f(SETALLOCHINT, __CEPH_OSD_OP(WR, DATA, 35), "set-alloc-hint") \
+ \
+ /* cache pin/unpin */ \
+ f(CACHE_PIN, __CEPH_OSD_OP(WR, DATA, 36), "cache-pin") \
+ f(CACHE_UNPIN, __CEPH_OSD_OP(WR, DATA, 37), "cache-unpin") \
+ \
+ /* ESX/SCSI */ \
+ f(WRITESAME, __CEPH_OSD_OP(WR, DATA, 38), "write-same") \
+ f(CMPEXT, __CEPH_OSD_OP(RD, DATA, 32), "cmpext") \
+ \
+ /* Extensible */ \
+ f(SET_REDIRECT, __CEPH_OSD_OP(WR, DATA, 39), "set-redirect") \
+ f(SET_CHUNK, __CEPH_OSD_OP(CACHE, DATA, 40), "set-chunk") \
+ f(TIER_PROMOTE, __CEPH_OSD_OP(WR, DATA, 41), "tier-promote") \
+ f(UNSET_MANIFEST, __CEPH_OSD_OP(WR, DATA, 42), "unset-manifest") \
+ f(TIER_FLUSH, __CEPH_OSD_OP(CACHE, DATA, 43), "tier-flush") \
+ f(TIER_EVICT, __CEPH_OSD_OP(CACHE, DATA, 44), "tier-evict") \
+ \
+ /** attrs **/ \
+ /* read */ \
+ f(GETXATTR, __CEPH_OSD_OP(RD, ATTR, 1), "getxattr") \
+ f(GETXATTRS, __CEPH_OSD_OP(RD, ATTR, 2), "getxattrs") \
+ f(CMPXATTR, __CEPH_OSD_OP(RD, ATTR, 3), "cmpxattr") \
+ \
+ /* write */ \
+ f(SETXATTR, __CEPH_OSD_OP(WR, ATTR, 1), "setxattr") \
+ f(SETXATTRS, __CEPH_OSD_OP(WR, ATTR, 2), "setxattrs") \
+ f(RESETXATTRS, __CEPH_OSD_OP(WR, ATTR, 3), "resetxattrs") \
+ f(RMXATTR, __CEPH_OSD_OP(WR, ATTR, 4), "rmxattr") \
+ \
+ /** subop **/ \
+ f(PULL, __CEPH_OSD_OP1(SUB, 1), "pull") \
+ f(PUSH, __CEPH_OSD_OP1(SUB, 2), "push") \
+ f(BALANCEREADS, __CEPH_OSD_OP1(SUB, 3), "balance-reads") \
+ f(UNBALANCEREADS, __CEPH_OSD_OP1(SUB, 4), "unbalance-reads") \
+ f(SCRUB, __CEPH_OSD_OP1(SUB, 5), "scrub") \
+ f(SCRUB_RESERVE, __CEPH_OSD_OP1(SUB, 6), "scrub-reserve") \
+ f(SCRUB_UNRESERVE, __CEPH_OSD_OP1(SUB, 7), "scrub-unreserve") \
+ /* 8 used to be scrub-stop */ \
+ f(SCRUB_MAP, __CEPH_OSD_OP1(SUB, 9), "scrub-map") \
+ \
+ /** exec **/ \
+ /* note: the RD bit here is wrong; see special-case below in helper */ \
+ f(CALL, __CEPH_OSD_OP(RD, EXEC, 1), "call") \
+ \
+ /** pg **/ \
+ f(PGLS, __CEPH_OSD_OP(RD, PG, 1), "pgls") \
+ f(PGLS_FILTER, __CEPH_OSD_OP(RD, PG, 2), "pgls-filter") \
+ f(PG_HITSET_LS, __CEPH_OSD_OP(RD, PG, 3), "pg-hitset-ls") \
+ f(PG_HITSET_GET, __CEPH_OSD_OP(RD, PG, 4), "pg-hitset-get") \
+ f(PGNLS, __CEPH_OSD_OP(RD, PG, 5), "pgnls") \
+ f(PGNLS_FILTER, __CEPH_OSD_OP(RD, PG, 6), "pgnls-filter") \
+ f(SCRUBLS, __CEPH_OSD_OP(RD, PG, 7), "scrubls")
+
+enum {
+#define GENERATE_ENUM_ENTRY(op, opcode, str) CEPH_OSD_OP_##op = (opcode),
+__CEPH_FORALL_OSD_OPS(GENERATE_ENUM_ENTRY)
+#undef GENERATE_ENUM_ENTRY
+};
+
+static inline int ceph_osd_op_type_data(int op)
+{
+ return (op & CEPH_OSD_OP_TYPE) == CEPH_OSD_OP_TYPE_DATA;
+}
+static inline int ceph_osd_op_type_attr(int op)
+{
+ return (op & CEPH_OSD_OP_TYPE) == CEPH_OSD_OP_TYPE_ATTR;
+}
+static inline int ceph_osd_op_type_exec(int op)
+{
+ return (op & CEPH_OSD_OP_TYPE) == CEPH_OSD_OP_TYPE_EXEC;
+}
+static inline int ceph_osd_op_type_pg(int op)
+{
+ return (op & CEPH_OSD_OP_TYPE) == CEPH_OSD_OP_TYPE_PG;
+}
+
+static inline int ceph_osd_op_mode_subop(int op)
+{
+ return (op & CEPH_OSD_OP_MODE) == CEPH_OSD_OP_MODE_SUB;
+}
+static inline int ceph_osd_op_mode_read(int op)
+{
+ return (op & CEPH_OSD_OP_MODE_RD) &&
+ op != CEPH_OSD_OP_CALL;
+}
+static inline int ceph_osd_op_mode_modify(int op)
+{
+ return op & CEPH_OSD_OP_MODE_WR;
+}
+static inline int ceph_osd_op_mode_cache(int op)
+{
+ return op & CEPH_OSD_OP_MODE_CACHE;
+}
+static inline bool ceph_osd_op_uses_extent(int op)
+{
+ switch(op) {
+ case CEPH_OSD_OP_READ:
+ case CEPH_OSD_OP_MAPEXT:
+ case CEPH_OSD_OP_MASKTRUNC:
+ case CEPH_OSD_OP_SPARSE_READ:
+ case CEPH_OSD_OP_SYNC_READ:
+ case CEPH_OSD_OP_WRITE:
+ case CEPH_OSD_OP_WRITEFULL:
+ case CEPH_OSD_OP_TRUNCATE:
+ case CEPH_OSD_OP_ZERO:
+ case CEPH_OSD_OP_APPEND:
+ case CEPH_OSD_OP_TRIMTRUNC:
+ case CEPH_OSD_OP_CMPEXT:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/*
+ * note that the following tmap stuff is also defined in the ceph librados.h
+ * and objclass.h. Any modification here needs to be updated there
+ */
+#define CEPH_OSD_TMAP_HDR 'h'
+#define CEPH_OSD_TMAP_SET 's'
+#define CEPH_OSD_TMAP_CREATE 'c' /* create key */
+#define CEPH_OSD_TMAP_RM 'r'
+#define CEPH_OSD_TMAP_RMSLOPPY 'R'
+
+extern const char *ceph_osd_op_name(int op);
+
+/*
+ * osd op flags
+ *
+ * An op may be READ, WRITE, or READ|WRITE.
+ */
+enum {
+ CEPH_OSD_FLAG_ACK = 0x0001, /* want (or is) "ack" ack */
+ CEPH_OSD_FLAG_ONNVRAM = 0x0002, /* want (or is) "onnvram" ack */
+ CEPH_OSD_FLAG_ONDISK = 0x0004, /* want (or is) "ondisk" ack */
+ CEPH_OSD_FLAG_RETRY = 0x0008, /* resend attempt */
+ CEPH_OSD_FLAG_READ = 0x0010, /* op may read */
+ CEPH_OSD_FLAG_WRITE = 0x0020, /* op may write */
+ CEPH_OSD_FLAG_ORDERSNAP = 0x0040, /* EOLDSNAP if snapc is out of order */
+ CEPH_OSD_FLAG_PEERSTAT_OLD = 0x0080, /* DEPRECATED msg includes osd_peer_stat */
+ CEPH_OSD_FLAG_BALANCE_READS = 0x0100,
+ CEPH_OSD_FLAG_PARALLELEXEC = 0x0200, /* execute op in parallel */
+ CEPH_OSD_FLAG_PGOP = 0x0400, /* pg op, no object */
+ CEPH_OSD_FLAG_EXEC = 0x0800, /* op may exec */
+ CEPH_OSD_FLAG_EXEC_PUBLIC = 0x1000, /* DEPRECATED op may exec (public) */
+ CEPH_OSD_FLAG_LOCALIZE_READS = 0x2000, /* read from nearby replica, if any */
+ CEPH_OSD_FLAG_RWORDERED = 0x4000, /* order wrt concurrent reads */
+ CEPH_OSD_FLAG_IGNORE_CACHE = 0x8000, /* ignore cache logic */
+ CEPH_OSD_FLAG_SKIPRWLOCKS = 0x10000, /* skip rw locks */
+ CEPH_OSD_FLAG_IGNORE_OVERLAY =0x20000, /* ignore pool overlay */
+ CEPH_OSD_FLAG_FLUSH = 0x40000, /* this is part of flush */
+ CEPH_OSD_FLAG_MAP_SNAP_CLONE =0x80000, /* map snap direct to clone id
+ */
+ CEPH_OSD_FLAG_ENFORCE_SNAPC =0x100000, /* use snapc provided even if
+ pool uses pool snaps */
+ CEPH_OSD_FLAG_REDIRECTED = 0x200000, /* op has been redirected */
+ CEPH_OSD_FLAG_KNOWN_REDIR = 0x400000, /* redirect bit is authoritative */
+ CEPH_OSD_FLAG_FULL_TRY = 0x800000, /* try op despite full flag */
+ CEPH_OSD_FLAG_FULL_FORCE = 0x1000000, /* force op despite full flag */
+ CEPH_OSD_FLAG_IGNORE_REDIRECT = 0x2000000, /* ignore redirection */
+ CEPH_OSD_FLAG_RETURNVEC = 0x4000000, /* allow overall result >= 0, and return >= 0 and buffer for each op in opvec */
+ CEPH_OSD_FLAG_SUPPORTSPOOLEIO = 0x8000000, /* client understands pool EIO flag */
+};
+
+enum {
+ CEPH_OSD_OP_FLAG_EXCL = 0x1, /* EXCL object create */
+ CEPH_OSD_OP_FLAG_FAILOK = 0x2, /* continue despite failure */
+ CEPH_OSD_OP_FLAG_FADVISE_RANDOM = 0x4, /* the op is random */
+ CEPH_OSD_OP_FLAG_FADVISE_SEQUENTIAL = 0x8, /* the op is sequential */
+ CEPH_OSD_OP_FLAG_FADVISE_WILLNEED = 0x10,/* data will be accessed in the near future */
+ CEPH_OSD_OP_FLAG_FADVISE_DONTNEED = 0x20,/* data will not be accessed in the near future */
+ CEPH_OSD_OP_FLAG_FADVISE_NOCACHE = 0x40, /* data will be accessed only once by this client */
+ CEPH_OSD_OP_FLAG_WITH_REFERENCE = 0x80, /* need reference couting */
+ CEPH_OSD_OP_FLAG_BYPASS_CLEAN_CACHE = 0x100, /* bypass ObjectStore cache, mainly for deep-scrub */
+};
+
+#define EOLDSNAPC 85 /* ORDERSNAP flag set; writer has old snapc*/
+#define EBLOCKLISTED 108 /* blocklisted */
+#define EBLACKLISTED 108 /* deprecated */
+
+/* xattr comparison */
+enum {
+ CEPH_OSD_CMPXATTR_OP_EQ = 1,
+ CEPH_OSD_CMPXATTR_OP_NE = 2,
+ CEPH_OSD_CMPXATTR_OP_GT = 3,
+ CEPH_OSD_CMPXATTR_OP_GTE = 4,
+ CEPH_OSD_CMPXATTR_OP_LT = 5,
+ CEPH_OSD_CMPXATTR_OP_LTE = 6
+};
+
+enum {
+ CEPH_OSD_CMPXATTR_MODE_STRING = 1,
+ CEPH_OSD_CMPXATTR_MODE_U64 = 2
+};
+
+enum {
+ CEPH_OSD_COPY_FROM_FLAG_FLUSH = 1, /* part of a flush operation */
+ CEPH_OSD_COPY_FROM_FLAG_IGNORE_OVERLAY = 2, /* ignore pool overlay */
+ CEPH_OSD_COPY_FROM_FLAG_IGNORE_CACHE = 4, /* ignore osd cache logic */
+ CEPH_OSD_COPY_FROM_FLAG_MAP_SNAP_CLONE = 8, /* map snap direct to
+ * cloneid */
+ CEPH_OSD_COPY_FROM_FLAG_RWORDERED = 16, /* order with write */
+ CEPH_OSD_COPY_FROM_FLAG_TRUNCATE_SEQ = 32, /* use provided truncate_{seq,size} (copy-from2 only) */
+};
+
+#define CEPH_OSD_COPY_FROM_FLAGS \
+ (CEPH_OSD_COPY_FROM_FLAG_FLUSH | \
+ CEPH_OSD_COPY_FROM_FLAG_IGNORE_OVERLAY | \
+ CEPH_OSD_COPY_FROM_FLAG_IGNORE_CACHE | \
+ CEPH_OSD_COPY_FROM_FLAG_MAP_SNAP_CLONE | \
+ CEPH_OSD_COPY_FROM_FLAG_RWORDERED | \
+ CEPH_OSD_COPY_FROM_FLAG_TRUNCATE_SEQ)
+
+enum {
+ CEPH_OSD_TMAP2OMAP_NULLOK = 1,
+};
+
+enum {
+ CEPH_OSD_WATCH_OP_UNWATCH = 0,
+ CEPH_OSD_WATCH_OP_LEGACY_WATCH = 1,
+ /* note: use only ODD ids to prevent pre-giant code from
+ interpreting the op as UNWATCH */
+ CEPH_OSD_WATCH_OP_WATCH = 3,
+ CEPH_OSD_WATCH_OP_RECONNECT = 5,
+ CEPH_OSD_WATCH_OP_PING = 7,
+};
+
+enum {
+ CEPH_OSD_CHECKSUM_OP_TYPE_XXHASH32 = 0,
+ CEPH_OSD_CHECKSUM_OP_TYPE_XXHASH64 = 1,
+ CEPH_OSD_CHECKSUM_OP_TYPE_CRC32C = 2
+};
+
+const char *ceph_osd_watch_op_name(int o);
+
+enum {
+ CEPH_OSD_ALLOC_HINT_FLAG_SEQUENTIAL_WRITE = 1,
+ CEPH_OSD_ALLOC_HINT_FLAG_RANDOM_WRITE = 2,
+ CEPH_OSD_ALLOC_HINT_FLAG_SEQUENTIAL_READ = 4,
+ CEPH_OSD_ALLOC_HINT_FLAG_RANDOM_READ = 8,
+ CEPH_OSD_ALLOC_HINT_FLAG_APPEND_ONLY = 16,
+ CEPH_OSD_ALLOC_HINT_FLAG_IMMUTABLE = 32,
+ CEPH_OSD_ALLOC_HINT_FLAG_SHORTLIVED = 64,
+ CEPH_OSD_ALLOC_HINT_FLAG_LONGLIVED = 128,
+ CEPH_OSD_ALLOC_HINT_FLAG_COMPRESSIBLE = 256,
+ CEPH_OSD_ALLOC_HINT_FLAG_INCOMPRESSIBLE = 512,
+};
+
+const char *ceph_osd_alloc_hint_flag_name(int f);
+
+enum {
+ CEPH_OSD_BACKOFF_OP_BLOCK = 1,
+ CEPH_OSD_BACKOFF_OP_ACK_BLOCK = 2,
+ CEPH_OSD_BACKOFF_OP_UNBLOCK = 3,
+};
+
+const char *ceph_osd_backoff_op_name(int op);
+
+/*
+ * an individual object operation. each may be accompanied by some data
+ * payload
+ */
+struct ceph_osd_op {
+ __le16 op; /* CEPH_OSD_OP_* */
+ __le32 flags; /* CEPH_OSD_OP_FLAG_* */
+ union {
+ struct {
+ __le64 offset, length;
+ __le64 truncate_size;
+ __le32 truncate_seq;
+ } __attribute__ ((packed)) extent;
+ struct {
+ __le32 name_len;
+ __le32 value_len;
+ __u8 cmp_op; /* CEPH_OSD_CMPXATTR_OP_* */
+ __u8 cmp_mode; /* CEPH_OSD_CMPXATTR_MODE_* */
+ } __attribute__ ((packed)) xattr;
+ struct {
+ __u8 class_len;
+ __u8 method_len;
+ __u8 argc;
+ __le32 indata_len;
+ } __attribute__ ((packed)) cls;
+ struct {
+ __le64 count;
+ __le32 start_epoch; /* for the pgls sequence */
+ } __attribute__ ((packed)) pgls;
+ struct {
+ __le64 snapid;
+ } __attribute__ ((packed)) snap;
+ struct {
+ __le64 cookie;
+ __le64 ver; /* no longer used */
+ __u8 op; /* CEPH_OSD_WATCH_OP_* */
+ __u32 gen; /* registration generation */
+ __u32 timeout; /* connection timeout */
+ } __attribute__ ((packed)) watch;
+ struct {
+ __le64 cookie;
+ } __attribute__ ((packed)) notify;
+ struct {
+ __le64 unused;
+ __le64 ver;
+ } __attribute__ ((packed)) assert_ver;
+ struct {
+ __le64 offset, length;
+ __le64 src_offset;
+ } __attribute__ ((packed)) clonerange;
+ struct {
+ __le64 max; /* max data in reply */
+ } __attribute__ ((packed)) copy_get;
+ struct {
+ __le64 snapid;
+ __le64 src_version;
+ __u8 flags; /* CEPH_OSD_COPY_FROM_FLAG_* */
+ /*
+ * CEPH_OSD_OP_FLAG_FADVISE_*: fadvise flags
+ * for src object, flags for dest object are in
+ * ceph_osd_op::flags.
+ */
+ __le32 src_fadvise_flags;
+ } __attribute__ ((packed)) copy_from;
+ struct {
+ struct ceph_timespec stamp;
+ } __attribute__ ((packed)) hit_set_get;
+ struct {
+ __u8 flags;
+ } __attribute__ ((packed)) tmap2omap;
+ struct {
+ __le64 expected_object_size;
+ __le64 expected_write_size;
+ __le32 flags; /* CEPH_OSD_OP_ALLOC_HINT_FLAG_* */
+ } __attribute__ ((packed)) alloc_hint;
+ struct {
+ __le64 offset;
+ __le64 length;
+ __le64 data_length;
+ } __attribute__ ((packed)) writesame;
+ struct {
+ __le64 offset;
+ __le64 length;
+ __le32 chunk_size;
+ __u8 type; /* CEPH_OSD_CHECKSUM_OP_TYPE_* */
+ } __attribute__ ((packed)) checksum;
+ } __attribute__ ((packed));
+ __le32 payload_len;
+} __attribute__ ((packed));
+
+/*
+ * Check the compatibility of struct ceph_osd_op
+ * (2+4+(2*8+8+4)+4) = (sizeof(ceph_osd_op::op) +
+ * sizeof(ceph_osd_op::flags) +
+ * sizeof(ceph_osd_op::extent) +
+ * sizeof(ceph_osd_op::payload_len))
+ */
+#ifdef __cplusplus
+static_assert(sizeof(ceph_osd_op) == (2+4+(2*8+8+4)+4),
+ "sizeof(ceph_osd_op) breaks the compatibility");
+#endif
+
+struct ceph_osd_reply_head {
+ __le32 client_inc; /* client incarnation */
+ __le32 flags;
+ struct ceph_object_layout layout;
+ __le32 osdmap_epoch;
+ struct ceph_eversion reassert_version; /* for replaying uncommitted */
+
+ __le32 result; /* result code */
+
+ __le32 object_len; /* length of object name */
+ __le32 num_ops;
+ struct ceph_osd_op ops[0]; /* ops[], object */
+} __attribute__ ((packed));
+
+#ifndef __KERNEL__
+#undef __le16
+#undef __le32
+#undef __le64
+#endif
+
+#endif
diff --git a/src/include/rados/buffer.h b/src/include/rados/buffer.h
new file mode 120000
index 000000000..51fc03be1
--- /dev/null
+++ b/src/include/rados/buffer.h
@@ -0,0 +1 @@
+../buffer.h \ No newline at end of file
diff --git a/src/include/rados/buffer_fwd.h b/src/include/rados/buffer_fwd.h
new file mode 120000
index 000000000..bd1f6f1b0
--- /dev/null
+++ b/src/include/rados/buffer_fwd.h
@@ -0,0 +1 @@
+../buffer_fwd.h \ No newline at end of file
diff --git a/src/include/rados/crc32c.h b/src/include/rados/crc32c.h
new file mode 120000
index 000000000..19ef4317e
--- /dev/null
+++ b/src/include/rados/crc32c.h
@@ -0,0 +1 @@
+../crc32c.h \ No newline at end of file
diff --git a/src/include/rados/inline_memory.h b/src/include/rados/inline_memory.h
new file mode 120000
index 000000000..48f0d4436
--- /dev/null
+++ b/src/include/rados/inline_memory.h
@@ -0,0 +1 @@
+../inline_memory.h \ No newline at end of file
diff --git a/src/include/rados/librados.h b/src/include/rados/librados.h
new file mode 100644
index 000000000..858804c3a
--- /dev/null
+++ b/src/include/rados/librados.h
@@ -0,0 +1,4156 @@
+// -*- 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
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <netinet/in.h>
+#if defined(__linux__)
+#include <linux/types.h>
+#elif defined(__FreeBSD__)
+#include <sys/types.h>
+#endif
+#include <unistd.h>
+#include <string.h>
+#include "rados_types.h"
+
+#include <sys/time.h>
+
+#ifndef CEPH_OSD_TMAP_SET
+/* These are also defined in rados.h and objclass.h. Keep them in sync! */
+#define CEPH_OSD_TMAP_HDR 'h'
+#define CEPH_OSD_TMAP_SET 's'
+#define CEPH_OSD_TMAP_CREATE 'c'
+#define CEPH_OSD_TMAP_RM 'r'
+#endif
+
+#define LIBRADOS_VER_MAJOR 3
+#define LIBRADOS_VER_MINOR 0
+#define LIBRADOS_VER_EXTRA 0
+
+#define LIBRADOS_VERSION(maj, min, extra) ((maj << 16) + (min << 8) + extra)
+
+#define LIBRADOS_VERSION_CODE LIBRADOS_VERSION(LIBRADOS_VER_MAJOR, LIBRADOS_VER_MINOR, LIBRADOS_VER_EXTRA)
+
+#define LIBRADOS_SUPPORTS_WATCH 1
+#define LIBRADOS_SUPPORTS_SERVICES 1
+#define LIBRADOS_SUPPORTS_GETADDRS 1
+#define LIBRADOS_SUPPORTS_APP_METADATA 1
+
+/* RADOS lock flags
+ * They are also defined in cls_lock_types.h. Keep them in sync!
+ */
+#define LIBRADOS_LOCK_FLAG_RENEW (1u<<0)
+#define LIBRADOS_LOCK_FLAG_MAY_RENEW LIBRADOS_LOCK_FLAG_RENEW
+#define LIBRADOS_LOCK_FLAG_MUST_RENEW (1u<<1)
+
+/*
+ * Constants for rados_write_op_create().
+ */
+#define LIBRADOS_CREATE_EXCLUSIVE 1
+#define LIBRADOS_CREATE_IDEMPOTENT 0
+
+/*
+ * Flags that can be set on a per-op basis via
+ * rados_read_op_set_flags() and rados_write_op_set_flags().
+ */
+enum {
+ // fail a create operation if the object already exists
+ LIBRADOS_OP_FLAG_EXCL = 0x1,
+ // allow the transaction to succeed even if the flagged op fails
+ LIBRADOS_OP_FLAG_FAILOK = 0x2,
+ // indicate read/write op random
+ LIBRADOS_OP_FLAG_FADVISE_RANDOM = 0x4,
+ // indicate read/write op sequential
+ LIBRADOS_OP_FLAG_FADVISE_SEQUENTIAL = 0x8,
+ // indicate read/write data will be accessed in the near future (by someone)
+ LIBRADOS_OP_FLAG_FADVISE_WILLNEED = 0x10,
+ // indicate read/write data will not accessed in the near future (by anyone)
+ LIBRADOS_OP_FLAG_FADVISE_DONTNEED = 0x20,
+ // indicate read/write data will not accessed again (by *this* client)
+ LIBRADOS_OP_FLAG_FADVISE_NOCACHE = 0x40,
+ // optionally support FUA (force unit access) on write requests
+ LIBRADOS_OP_FLAG_FADVISE_FUA = 0x80,
+};
+
+#define CEPH_RADOS_API
+
+/**
+ * @name xattr comparison operations
+ * Operators for comparing xattrs on objects, and aborting the
+ * rados_read_op or rados_write_op transaction if the comparison
+ * fails.
+ *
+ * @{
+ */
+enum {
+ LIBRADOS_CMPXATTR_OP_EQ = 1,
+ LIBRADOS_CMPXATTR_OP_NE = 2,
+ LIBRADOS_CMPXATTR_OP_GT = 3,
+ LIBRADOS_CMPXATTR_OP_GTE = 4,
+ LIBRADOS_CMPXATTR_OP_LT = 5,
+ LIBRADOS_CMPXATTR_OP_LTE = 6
+};
+/** @} */
+
+/**
+ * @name Operation Flags
+ * Flags for rados_read_op_operate(), rados_write_op_operate(),
+ * rados_aio_read_op_operate(), and rados_aio_write_op_operate().
+ * See librados.hpp for details.
+ * @{
+ */
+enum {
+ LIBRADOS_OPERATION_NOFLAG = 0,
+ LIBRADOS_OPERATION_BALANCE_READS = 1,
+ LIBRADOS_OPERATION_LOCALIZE_READS = 2,
+ LIBRADOS_OPERATION_ORDER_READS_WRITES = 4,
+ LIBRADOS_OPERATION_IGNORE_CACHE = 8,
+ LIBRADOS_OPERATION_SKIPRWLOCKS = 16,
+ LIBRADOS_OPERATION_IGNORE_OVERLAY = 32,
+ /* send requests to cluster despite the cluster or pool being marked
+ full; ops will either succeed (e.g., delete) or return EDQUOT or
+ ENOSPC. */
+ LIBRADOS_OPERATION_FULL_TRY = 64,
+ /*
+ * Mainly for delete op
+ */
+ LIBRADOS_OPERATION_FULL_FORCE = 128,
+ LIBRADOS_OPERATION_IGNORE_REDIRECT = 256,
+ LIBRADOS_OPERATION_ORDERSNAP = 512,
+ /* enable/allow >0 return values and payloads on write/update */
+ LIBRADOS_OPERATION_RETURNVEC = 1024,
+};
+/** @} */
+
+/**
+ * @name Alloc hint flags
+ * Flags for rados_write_op_alloc_hint2() and rados_set_alloc_hint2()
+ * indicating future IO patterns.
+ * @{
+ */
+enum {
+ LIBRADOS_ALLOC_HINT_FLAG_SEQUENTIAL_WRITE = 1,
+ LIBRADOS_ALLOC_HINT_FLAG_RANDOM_WRITE = 2,
+ LIBRADOS_ALLOC_HINT_FLAG_SEQUENTIAL_READ = 4,
+ LIBRADOS_ALLOC_HINT_FLAG_RANDOM_READ = 8,
+ LIBRADOS_ALLOC_HINT_FLAG_APPEND_ONLY = 16,
+ LIBRADOS_ALLOC_HINT_FLAG_IMMUTABLE = 32,
+ LIBRADOS_ALLOC_HINT_FLAG_SHORTLIVED = 64,
+ LIBRADOS_ALLOC_HINT_FLAG_LONGLIVED = 128,
+ LIBRADOS_ALLOC_HINT_FLAG_COMPRESSIBLE = 256,
+ LIBRADOS_ALLOC_HINT_FLAG_INCOMPRESSIBLE = 512,
+};
+/** @} */
+
+typedef enum {
+ LIBRADOS_CHECKSUM_TYPE_XXHASH32 = 0,
+ LIBRADOS_CHECKSUM_TYPE_XXHASH64 = 1,
+ LIBRADOS_CHECKSUM_TYPE_CRC32C = 2
+} rados_checksum_type_t;
+
+/*
+ * snap id contants
+ */
+#define LIBRADOS_SNAP_HEAD UINT64_C(-2)
+#define LIBRADOS_SNAP_DIR UINT64_C(-1)
+
+/**
+ * @typedef rados_t
+ *
+ * A handle for interacting with a RADOS cluster. It encapsulates all
+ * RADOS client configuration, including username, key for
+ * authentication, logging, and debugging. Talking to different clusters
+ * -- or to the same cluster with different users -- requires
+ * different cluster handles.
+ */
+#ifndef VOIDPTR_RADOS_T
+#define VOIDPTR_RADOS_T
+typedef void *rados_t;
+#endif //VOIDPTR_RADOS_T
+
+/**
+ * @typedef rados_config_t
+ *
+ * A handle for the ceph configuration context for the rados_t cluster
+ * instance. This can be used to share configuration context/state
+ * (e.g., logging configuration) between librados instance.
+ *
+ * @warning The config context does not have independent reference
+ * counting. As such, a rados_config_t handle retrieved from a given
+ * rados_t is only valid as long as that rados_t.
+ */
+typedef void *rados_config_t;
+
+/**
+ * @typedef rados_ioctx_t
+ *
+ * An io context encapsulates a few settings for all I/O operations
+ * done on it:
+ * - pool - set when the io context is created (see rados_ioctx_create())
+ * - snapshot context for writes (see
+ * rados_ioctx_selfmanaged_snap_set_write_ctx())
+ * - snapshot id to read from (see rados_ioctx_snap_set_read())
+ * - object locator for all single-object operations (see
+ * rados_ioctx_locator_set_key())
+ * - namespace for all single-object operations (see
+ * rados_ioctx_set_namespace()). Set to LIBRADOS_ALL_NSPACES
+ * before rados_nobjects_list_open() will list all objects in all
+ * namespaces.
+ *
+ * @warning Changing any of these settings is not thread-safe -
+ * librados users must synchronize any of these changes on their own,
+ * or use separate io contexts for each thread
+ */
+typedef void *rados_ioctx_t;
+
+/**
+ * @typedef rados_list_ctx_t
+ *
+ * An iterator for listing the objects in a pool.
+ * Used with rados_nobjects_list_open(),
+ * rados_nobjects_list_next(), rados_nobjects_list_next2(), and
+ * rados_nobjects_list_close().
+ */
+typedef void *rados_list_ctx_t;
+
+/**
+ * @typedef rados_object_list_cursor
+ *
+ * The cursor used with rados_enumerate_objects
+ * and accompanying methods.
+ */
+typedef void * rados_object_list_cursor;
+
+/**
+ * @struct rados_object_list_item
+ *
+ * The item populated by rados_object_list in
+ * the results array.
+ */
+typedef struct {
+
+ /// oid length
+ size_t oid_length;
+ /// name of the object
+ char *oid;
+ /// namespace length
+ size_t nspace_length;
+ /// the object namespace
+ char *nspace;
+ /// locator length
+ size_t locator_length;
+ /// object locator
+ char *locator;
+} rados_object_list_item;
+
+/**
+ * @typedef rados_snap_t
+ * The id of a snapshot.
+ */
+typedef uint64_t rados_snap_t;
+
+/**
+ * @typedef rados_xattrs_iter_t
+ * An iterator for listing extended attrbutes on an object.
+ * Used with rados_getxattrs(), rados_getxattrs_next(), and
+ * rados_getxattrs_end().
+ */
+typedef void *rados_xattrs_iter_t;
+
+/**
+ * @typedef rados_omap_iter_t
+ * An iterator for listing omap key/value pairs on an object.
+ * Used with rados_read_op_omap_get_keys(), rados_read_op_omap_get_vals(),
+ * rados_read_op_omap_get_vals_by_keys(), rados_omap_get_next(), and
+ * rados_omap_get_end().
+ */
+typedef void *rados_omap_iter_t;
+
+/**
+ * @struct rados_pool_stat_t
+ * Usage information for a pool.
+ */
+struct rados_pool_stat_t {
+ /// space used in bytes
+ uint64_t num_bytes;
+ /// space used in KB
+ uint64_t num_kb;
+ /// number of objects in the pool
+ uint64_t num_objects;
+ /// number of clones of objects
+ uint64_t num_object_clones;
+ /// num_objects * num_replicas
+ uint64_t num_object_copies;
+ /// number of objects missing on primary
+ uint64_t num_objects_missing_on_primary;
+ /// number of objects found on no OSDs
+ uint64_t num_objects_unfound;
+ /// number of objects replicated fewer times than they should be
+ /// (but found on at least one OSD)
+ uint64_t num_objects_degraded;
+ /// number of objects read
+ uint64_t num_rd;
+ /// objects read in KB
+ uint64_t num_rd_kb;
+ /// number of objects written
+ uint64_t num_wr;
+ /// objects written in KB
+ uint64_t num_wr_kb;
+ /// bytes originally provided by user
+ uint64_t num_user_bytes;
+ /// bytes passed compression
+ uint64_t compressed_bytes_orig;
+ /// bytes resulted after compression
+ uint64_t compressed_bytes;
+ /// bytes allocated at storage
+ uint64_t compressed_bytes_alloc;
+};
+
+/**
+ * @struct rados_cluster_stat_t
+ * Cluster-wide usage information
+ */
+struct rados_cluster_stat_t {
+ /// total device size
+ uint64_t kb;
+ /// total used
+ uint64_t kb_used;
+ /// total available/free
+ uint64_t kb_avail;
+ /// number of objects
+ 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()
+ * - Object map key/value pairs: rados_write_op_omap_set(),
+ * rados_write_op_omap_rm_keys(), rados_write_op_omap_clear(),
+ * rados_write_op_omap_cmp()
+ * - Object properties: rados_write_op_assert_exists(),
+ * rados_write_op_assert_version()
+ * - 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_writesame(), rados_write_op_remove,
+ * rados_write_op_truncate(), rados_write_op_zero(), rados_write_op_cmpext()
+ * - Hints: rados_write_op_set_alloc_hint()
+ * - Performing the operation: rados_write_op_operate(), rados_aio_write_op_operate()
+ */
+typedef void *rados_write_op_t;
+
+/**
+ * @typedef rados_read_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 map key/value pairs: rados_read_op_omap_get_vals(),
+ * rados_read_op_omap_get_keys(), rados_read_op_omap_get_vals_by_keys(),
+ * rados_read_op_omap_cmp()
+ * - Object properties: rados_read_op_stat(), rados_read_op_assert_exists(),
+ * rados_read_op_assert_version()
+ * - IO on objects: rados_read_op_read(), rados_read_op_checksum(),
+ * rados_read_op_cmpext()
+ * - Custom operations: rados_read_op_exec(), rados_read_op_exec_user_buf()
+ * - Request properties: rados_read_op_set_flags()
+ * - Performing the operation: rados_read_op_operate(),
+ * rados_aio_read_op_operate()
+ */
+typedef void *rados_read_op_t;
+
+/**
+ * @typedef rados_completion_t
+ * Represents the state of an asynchronous operation - it contains the
+ * return value once the operation completes, and can be used to block
+ * until the operation is complete or safe.
+ */
+typedef void *rados_completion_t;
+
+/**
+ * @struct blkin_trace_info
+ * blkin trace information for Zipkin tracing
+ */
+struct blkin_trace_info;
+
+/**
+ * Get the version of librados.
+ *
+ * The version number is major.minor.extra. Note that this is
+ * unrelated to the Ceph version number.
+ *
+ * TODO: define version semantics, i.e.:
+ * - incrementing major is for backwards-incompatible changes
+ * - incrementing minor is for backwards-compatible changes
+ * - incrementing extra is for bug fixes
+ *
+ * @param major where to store the major version number
+ * @param minor where to store the minor version number
+ * @param extra where to store the extra version number
+ */
+CEPH_RADOS_API void rados_version(int *major, int *minor, int *extra);
+
+/**
+ * @name Setup and Teardown
+ * These are the first and last functions to that should be called
+ * when using librados.
+ *
+ * @{
+ */
+
+/**
+ * Create a handle for communicating with a RADOS cluster.
+ *
+ * Ceph environment variables are read when this is called, so if
+ * $CEPH_ARGS specifies everything you need to connect, no further
+ * configuration is necessary.
+ *
+ * @param cluster where to store the handle
+ * @param id the user to connect as (i.e. admin, not client.admin)
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_create(rados_t *cluster, const char * const id);
+
+/**
+ * Extended version of rados_create.
+ *
+ * Like rados_create, but
+ * 1) don't assume 'client\.'+id; allow full specification of name
+ * 2) allow specification of cluster name
+ * 3) flags for future expansion
+ */
+CEPH_RADOS_API int rados_create2(rados_t *pcluster,
+ const char *const clustername,
+ const char * const name, uint64_t flags);
+
+/**
+ * Initialize a cluster handle from an existing configuration.
+ *
+ * Share configuration state with another rados_t instance.
+ *
+ * @param cluster where to store the handle
+ * @param cct the existing configuration to use
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_create_with_context(rados_t *cluster,
+ rados_config_t cct);
+
+/**
+ * Ping the monitor with ID mon_id, storing the resulting reply in
+ * buf (if specified) with a maximum size of len.
+ *
+ * The result buffer is allocated on the heap; the caller is
+ * expected to release that memory with rados_buffer_free(). The
+ * buffer and length pointers can be NULL, in which case they are
+ * not filled in.
+ *
+ * @param cluster cluster handle
+ * @param mon_id [in] ID of the monitor to ping
+ * @param outstr [out] double pointer with the resulting reply
+ * @param outstrlen [out] pointer with the size of the reply in outstr
+ */
+CEPH_RADOS_API int rados_ping_monitor(rados_t cluster, const char *mon_id,
+ char **outstr, size_t *outstrlen);
+
+/**
+ * Connect to the cluster.
+ *
+ * @note BUG: Before calling this, calling a function that communicates with the
+ * cluster will crash.
+ *
+ * @pre The cluster handle is configured with at least a monitor
+ * address. If cephx is enabled, a client name and secret must also be
+ * set.
+ *
+ * @post If this succeeds, any function in librados may be used
+ *
+ * @param cluster The cluster to connect to.
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_connect(rados_t cluster);
+
+/**
+ * Disconnects from the cluster.
+ *
+ * For clean up, this is only necessary after rados_connect() has
+ * succeeded.
+ *
+ * @warning This does not guarantee any asynchronous writes have
+ * completed. To do that, you must call rados_aio_flush() on all open
+ * io contexts.
+ *
+ * @warning We implicitly call rados_watch_flush() on shutdown. If
+ * there are watches being used, this should be done explicitly before
+ * destroying the relevant IoCtx. We do it here as a safety measure.
+ *
+ * @post the cluster handle cannot be used again
+ *
+ * @param cluster the cluster to shutdown
+ */
+CEPH_RADOS_API void rados_shutdown(rados_t cluster);
+
+/** @} init */
+
+/**
+ * @name Configuration
+ * These functions read and update Ceph configuration for a cluster
+ * handle. Any configuration changes must be done before connecting to
+ * the cluster.
+ *
+ * Options that librados users might want to set include:
+ * - mon_host
+ * - auth_supported
+ * - key, keyfile, or keyring when using cephx
+ * - log_file, log_to_stderr, err_to_stderr, and log_to_syslog
+ * - debug_rados, debug_objecter, debug_monc, debug_auth, or debug_ms
+ *
+ * See docs.ceph.com for information about available configuration options`
+ *
+ * @{
+ */
+
+/**
+ * Configure the cluster handle using a Ceph config file
+ *
+ * If path is NULL, the default locations are searched, and the first
+ * found is used. The locations are:
+ * - $CEPH_CONF (environment variable)
+ * - /etc/ceph/ceph.conf
+ * - ~/.ceph/config
+ * - ceph.conf (in the current working directory)
+ *
+ * @pre rados_connect() has not been called on the cluster handle
+ *
+ * @param cluster cluster handle to configure
+ * @param path path to a Ceph configuration file
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_conf_read_file(rados_t cluster, const char *path);
+
+/**
+ * Configure the cluster handle with command line arguments
+ *
+ * argv can contain any common Ceph command line option, including any
+ * configuration parameter prefixed by '--' and replacing spaces with
+ * dashes or underscores. For example, the following options are equivalent:
+ * - --mon-host 10.0.0.1:6789
+ * - --mon_host 10.0.0.1:6789
+ * - -m 10.0.0.1:6789
+ *
+ * @pre rados_connect() has not been called on the cluster handle
+ *
+ * @param cluster cluster handle to configure
+ * @param argc number of arguments in argv
+ * @param argv arguments to parse
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_conf_parse_argv(rados_t cluster, int argc,
+ const char **argv);
+
+
+/**
+ * Configure the cluster handle with command line arguments, returning
+ * any remainders. Same rados_conf_parse_argv, except for extra
+ * remargv argument to hold returns unrecognized arguments.
+ *
+ * @pre rados_connect() has not been called on the cluster handle
+ *
+ * @param cluster cluster handle to configure
+ * @param argc number of arguments in argv
+ * @param argv arguments to parse
+ * @param remargv char* array for returned unrecognized arguments
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_conf_parse_argv_remainder(rados_t cluster, int argc,
+ const char **argv,
+ const char **remargv);
+/**
+ * Configure the cluster handle based on an environment variable
+ *
+ * The contents of the environment variable are parsed as if they were
+ * Ceph command line options. If var is NULL, the CEPH_ARGS
+ * environment variable is used.
+ *
+ * @pre rados_connect() has not been called on the cluster handle
+ *
+ * @note BUG: this is not threadsafe - it uses a static buffer
+ *
+ * @param cluster cluster handle to configure
+ * @param var name of the environment variable to read
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_conf_parse_env(rados_t cluster, const char *var);
+
+/**
+ * Set a configuration option
+ *
+ * @pre rados_connect() has not been called on the cluster handle
+ *
+ * @param cluster cluster handle to configure
+ * @param option option to set
+ * @param value value of the option
+ * @returns 0 on success, negative error code on failure
+ * @returns -ENOENT when the option is not a Ceph configuration option
+ */
+CEPH_RADOS_API int rados_conf_set(rados_t cluster, const char *option,
+ const char *value);
+
+/**
+ * Get the value of a configuration option
+ *
+ * @param cluster configuration to read
+ * @param option which option to read
+ * @param buf where to write the configuration value
+ * @param len the size of buf in bytes
+ * @returns 0 on success, negative error code on failure
+ * @returns -ENAMETOOLONG if the buffer is too short to contain the
+ * requested value
+ */
+CEPH_RADOS_API int rados_conf_get(rados_t cluster, const char *option,
+ char *buf, size_t len);
+
+/** @} config */
+
+/**
+ * Read usage info about the cluster
+ *
+ * This tells you total space, space used, space available, and number
+ * of objects. These are not updated immediately when data is written,
+ * they are eventually consistent.
+ *
+ * @param cluster cluster to query
+ * @param result where to store the results
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_cluster_stat(rados_t cluster,
+ struct rados_cluster_stat_t *result);
+
+/**
+ * Get the fsid of the cluster as a hexadecimal string.
+ *
+ * The fsid is a unique id of an entire Ceph cluster.
+ *
+ * @param cluster where to get the fsid
+ * @param buf where to write the fsid
+ * @param len the size of buf in bytes (should be 37)
+ * @returns 0 on success, negative error code on failure
+ * @returns -ERANGE if the buffer is too short to contain the
+ * fsid
+ */
+CEPH_RADOS_API int rados_cluster_fsid(rados_t cluster, char *buf, size_t len);
+
+/**
+ * Get/wait for the most recent osdmap
+ *
+ * @param cluster the cluster to shutdown
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_wait_for_latest_osdmap(rados_t cluster);
+
+/**
+ * @name Pools
+ *
+ * RADOS pools are separate namespaces for objects. Pools may have
+ * different crush rules associated with them, so they could have
+ * differing replication levels or placement strategies. RADOS
+ * permissions are also tied to pools - users can have different read,
+ * write, and execute permissions on a per-pool basis.
+ *
+ * @{
+ */
+
+/**
+ * List pools
+ *
+ * Gets a list of pool names as NULL-terminated strings. The pool
+ * names will be placed in the supplied buffer one after another.
+ * After the last pool name, there will be two 0 bytes in a row.
+ *
+ * If len is too short to fit all the pool name entries we need, we will fill
+ * as much as we can.
+ *
+ * Buf may be null to determine the buffer size needed to list all pools.
+ *
+ * @param cluster cluster handle
+ * @param buf output buffer
+ * @param len output buffer length
+ * @returns length of the buffer we would need to list all pools
+ */
+CEPH_RADOS_API int rados_pool_list(rados_t cluster, char *buf, size_t len);
+
+/**
+ * List inconsistent placement groups of the given pool
+ *
+ * Gets a list of inconsistent placement groups as NULL-terminated strings.
+ * The placement group names will be placed in the supplied buffer one after
+ * another. After the last name, there will be two 0 types in a row.
+ *
+ * If len is too short to fit all the placement group entries we need, we will
+ * fill as much as we can.
+ *
+ * @param cluster cluster handle
+ * @param pool pool ID
+ * @param buf output buffer
+ * @param len output buffer length
+ * @returns length of the buffer we would need to list all pools
+ */
+CEPH_RADOS_API int rados_inconsistent_pg_list(rados_t cluster, int64_t pool,
+ char *buf, size_t len);
+
+/**
+ * Get a configuration handle for a rados cluster handle
+ *
+ * This handle is valid only as long as the cluster handle is valid.
+ *
+ * @param cluster cluster handle
+ * @returns config handle for this cluster
+ */
+CEPH_RADOS_API rados_config_t rados_cct(rados_t cluster);
+
+/**
+ * Get a global id for current instance
+ *
+ * This id is a unique representation of current connection to the cluster
+ *
+ * @param cluster cluster handle
+ * @returns instance global id
+ */
+CEPH_RADOS_API uint64_t rados_get_instance_id(rados_t cluster);
+
+/**
+ * Gets the minimum compatible OSD version
+ *
+ * @param cluster cluster handle
+ * @param require_osd_release [out] minimum compatible OSD version
+ * based upon the current features
+ * @returns 0 on sucess, negative error code on failure
+ */
+CEPH_RADOS_API int rados_get_min_compatible_osd(rados_t cluster,
+ int8_t* require_osd_release);
+
+/**
+ * Gets the minimum compatible client version
+ *
+ * @param cluster cluster handle
+ * @param min_compat_client [out] minimum compatible client version
+ * based upon the current features
+ * @param require_min_compat_client [out] required minimum client version
+ * based upon explicit setting
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_get_min_compatible_client(rados_t cluster,
+ int8_t* min_compat_client,
+ int8_t* require_min_compat_client);
+
+/**
+ * Create an io context
+ *
+ * The io context allows you to perform operations within a particular
+ * pool. For more details see rados_ioctx_t.
+ *
+ * @param cluster which cluster the pool is in
+ * @param pool_name name of the pool
+ * @param ioctx where to store the io context
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_ioctx_create(rados_t cluster, const char *pool_name,
+ rados_ioctx_t *ioctx);
+CEPH_RADOS_API int rados_ioctx_create2(rados_t cluster, int64_t pool_id,
+ rados_ioctx_t *ioctx);
+
+/**
+ * The opposite of rados_ioctx_create
+ *
+ * This just tells librados that you no longer need to use the io context.
+ * It may not be freed immediately if there are pending asynchronous
+ * requests on it, but you should not use an io context again after
+ * calling this function on it.
+ *
+ * @warning This does not guarantee any asynchronous
+ * writes have completed. You must call rados_aio_flush()
+ * on the io context before destroying it to do that.
+ *
+ * @warning If this ioctx is used by rados_watch, the caller needs to
+ * be sure that all registered watches are disconnected via
+ * rados_unwatch() and that rados_watch_flush() is called. This
+ * ensures that a racing watch callback does not make use of a
+ * destroyed ioctx.
+ *
+ * @param io the io context to dispose of
+ */
+CEPH_RADOS_API void rados_ioctx_destroy(rados_ioctx_t io);
+
+/**
+ * Get configuration handle for a pool handle
+ *
+ * @param io pool handle
+ * @returns rados_config_t for this cluster
+ */
+CEPH_RADOS_API rados_config_t rados_ioctx_cct(rados_ioctx_t io);
+
+/**
+ * Get the cluster handle used by this rados_ioctx_t
+ * Note that this is a weak reference, and should not
+ * be destroyed via rados_shutdown().
+ *
+ * @param io the io context
+ * @returns the cluster handle for this io context
+ */
+CEPH_RADOS_API rados_t rados_ioctx_get_cluster(rados_ioctx_t io);
+
+/**
+ * Get pool usage statistics
+ *
+ * Fills in a rados_pool_stat_t after querying the cluster.
+ *
+ * @param io determines which pool to query
+ * @param stats where to store the results
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_ioctx_pool_stat(rados_ioctx_t io,
+ struct rados_pool_stat_t *stats);
+
+/**
+ * Get the id of a pool
+ *
+ * @param cluster which cluster the pool is in
+ * @param pool_name which pool to look up
+ * @returns id of the pool
+ * @returns -ENOENT if the pool is not found
+ */
+CEPH_RADOS_API int64_t rados_pool_lookup(rados_t cluster,
+ const char *pool_name);
+
+/**
+ * Get the name of a pool
+ *
+ * @param cluster which cluster the pool is in
+ * @param id the id of the pool
+ * @param buf where to store the pool name
+ * @param maxlen size of buffer where name will be stored
+ * @returns length of string stored, or -ERANGE if buffer too small
+ */
+CEPH_RADOS_API int rados_pool_reverse_lookup(rados_t cluster, int64_t id,
+ char *buf, size_t maxlen);
+
+/**
+ * Create a pool with default settings
+ *
+ * The default crush rule is rule 0.
+ *
+ * @param cluster the cluster in which the pool will be created
+ * @param pool_name the name of the new pool
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_pool_create(rados_t cluster, const char *pool_name);
+
+/**
+ * Create a pool owned by a specific auid.
+ *
+ * DEPRECATED: auid support has been removed, and this call will be removed in a future
+ * release.
+ *
+ * @param cluster the cluster in which the pool will be created
+ * @param pool_name the name of the new pool
+ * @param auid the id of the owner of the new pool
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_pool_create_with_auid(rados_t cluster,
+ const char *pool_name,
+ uint64_t auid)
+ __attribute__((deprecated));
+
+/**
+ * Create a pool with a specific CRUSH rule
+ *
+ * @param cluster the cluster in which the pool will be created
+ * @param pool_name the name of the new pool
+ * @param crush_rule_num which rule to use for placement in the new pool1
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_pool_create_with_crush_rule(rados_t cluster,
+ const char *pool_name,
+ uint8_t crush_rule_num);
+
+/**
+ * Create a pool with a specific CRUSH rule and auid
+ *
+ * DEPRECATED: auid support has been removed and this call will be removed
+ * in a future release.
+ *
+ * This is a combination of rados_pool_create_with_crush_rule() and
+ * rados_pool_create_with_auid().
+ *
+ * @param cluster the cluster in which the pool will be created
+ * @param pool_name the name of the new pool
+ * @param crush_rule_num which rule to use for placement in the new pool2
+ * @param auid the id of the owner of the new pool
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_pool_create_with_all(rados_t cluster,
+ const char *pool_name,
+ uint64_t auid,
+ uint8_t crush_rule_num)
+ __attribute__((deprecated));
+
+/**
+ * Returns the pool that is the base tier for this pool.
+ *
+ * The return value is the ID of the pool that should be used to read from/write to.
+ * If tiering is not set up for the pool, returns \c pool.
+ *
+ * @param cluster the cluster the pool is in
+ * @param pool ID of the pool to query
+ * @param base_tier [out] base tier, or \c pool if tiering is not configured
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_pool_get_base_tier(rados_t cluster, int64_t pool,
+ int64_t* base_tier);
+
+/**
+ * Delete a pool and all data inside it
+ *
+ * The pool is removed from the cluster immediately,
+ * but the actual data is deleted in the background.
+ *
+ * @param cluster the cluster the pool is in
+ * @param pool_name which pool to delete
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_pool_delete(rados_t cluster, const char *pool_name);
+
+/**
+ * Attempt to change an io context's associated auid "owner"
+ *
+ * DEPRECATED: auid support has been removed and this call has no effect.
+ *
+ * Requires that you have write permission on both the current and new
+ * auid.
+ *
+ * @param io reference to the pool to change.
+ * @param auid the auid you wish the io to have.
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_ioctx_pool_set_auid(rados_ioctx_t io, uint64_t auid)
+ __attribute__((deprecated));
+
+
+/**
+ * Get the auid of a pool
+ *
+ * DEPRECATED: auid support has been removed and this call always reports
+ * CEPH_AUTH_UID_DEFAULT (-1).
+
+ * @param io pool to query
+ * @param auid where to store the auid
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_ioctx_pool_get_auid(rados_ioctx_t io, uint64_t *auid)
+ __attribute__((deprecated));
+
+/* deprecated, use rados_ioctx_pool_requires_alignment2 instead */
+CEPH_RADOS_API int rados_ioctx_pool_requires_alignment(rados_ioctx_t io)
+ __attribute__((deprecated));
+
+/**
+ * Test whether the specified pool requires alignment or not.
+ *
+ * @param io pool to query
+ * @param req 1 if alignment is supported, 0 if not.
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_ioctx_pool_requires_alignment2(rados_ioctx_t io,
+ int *req);
+
+/* deprecated, use rados_ioctx_pool_required_alignment2 instead */
+CEPH_RADOS_API uint64_t rados_ioctx_pool_required_alignment(rados_ioctx_t io)
+ __attribute__((deprecated));
+
+/**
+ * Get the alignment flavor of a pool
+ *
+ * @param io pool to query
+ * @param alignment where to store the alignment flavor
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_ioctx_pool_required_alignment2(rados_ioctx_t io,
+ uint64_t *alignment);
+
+/**
+ * Get the pool id of the io context
+ *
+ * @param io the io context to query
+ * @returns the id of the pool the io context uses
+ */
+CEPH_RADOS_API int64_t rados_ioctx_get_id(rados_ioctx_t io);
+
+/**
+ * Get the pool name of the io context
+ *
+ * @param io the io context to query
+ * @param buf pointer to buffer where name will be stored
+ * @param maxlen size of buffer where name will be stored
+ * @returns length of string stored, or -ERANGE if buffer too small
+ */
+CEPH_RADOS_API int rados_ioctx_get_pool_name(rados_ioctx_t io, char *buf,
+ unsigned maxlen);
+
+/** @} pools */
+
+/**
+ * @name Object Locators
+ *
+ * @{
+ */
+
+/**
+ * Set the key for mapping objects to pgs within an io context.
+ *
+ * The key is used instead of the object name to determine which
+ * placement groups an object is put in. This affects all subsequent
+ * operations of the io context - until a different locator key is
+ * set, all objects in this io context will be placed in the same pg.
+ *
+ * @param io the io context to change
+ * @param key the key to use as the object locator, or NULL to discard
+ * any previously set key
+ */
+CEPH_RADOS_API void rados_ioctx_locator_set_key(rados_ioctx_t io,
+ const char *key);
+
+/**
+ * Set the namespace for objects within an io context
+ *
+ * The namespace specification further refines a pool into different
+ * domains. The mapping of objects to pgs is also based on this
+ * value.
+ *
+ * @param io the io context to change
+ * @param nspace the name to use as the namespace, or NULL use the
+ * default namespace
+ */
+CEPH_RADOS_API void rados_ioctx_set_namespace(rados_ioctx_t io,
+ const char *nspace);
+
+/**
+ * Get the namespace for objects within the io context
+ *
+ * @param io the io context to query
+ * @param buf pointer to buffer where name will be stored
+ * @param maxlen size of buffer where name will be stored
+ * @returns length of string stored, or -ERANGE if buffer too small
+ */
+CEPH_RADOS_API int rados_ioctx_get_namespace(rados_ioctx_t io, char *buf,
+ unsigned maxlen);
+
+/** @} obj_loc */
+
+/**
+ * @name Listing Objects
+ * @{
+ */
+/**
+ * Start listing objects in a pool
+ *
+ * @param io the pool to list from
+ * @param ctx the handle to store list context in
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_nobjects_list_open(rados_ioctx_t io,
+ rados_list_ctx_t *ctx);
+
+/**
+ * Return hash position of iterator, rounded to the current PG
+ *
+ * @param ctx iterator marking where you are in the listing
+ * @returns current hash position, rounded to the current pg
+ */
+CEPH_RADOS_API uint32_t rados_nobjects_list_get_pg_hash_position(rados_list_ctx_t ctx);
+
+/**
+ * Reposition object iterator to a different hash position
+ *
+ * @param ctx iterator marking where you are in the listing
+ * @param pos hash position to move to
+ * @returns actual (rounded) position we moved to
+ */
+CEPH_RADOS_API uint32_t rados_nobjects_list_seek(rados_list_ctx_t ctx,
+ uint32_t pos);
+
+/**
+ * Reposition object iterator to a different position
+ *
+ * @param ctx iterator marking where you are in the listing
+ * @param cursor position to move to
+ * @returns rounded position we moved to
+ */
+CEPH_RADOS_API uint32_t rados_nobjects_list_seek_cursor(rados_list_ctx_t ctx,
+ rados_object_list_cursor cursor);
+
+/**
+ * Reposition object iterator to a different position
+ *
+ * The returned handle must be released with rados_object_list_cursor_free().
+ *
+ * @param ctx iterator marking where you are in the listing
+ * @param cursor where to store cursor
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_nobjects_list_get_cursor(rados_list_ctx_t ctx,
+ rados_object_list_cursor *cursor);
+
+/**
+ * Get the next object name and locator in the pool
+ *
+ * *entry and *key are valid until next call to rados_nobjects_list_*
+ *
+ * @param ctx iterator marking where you are in the listing
+ * @param entry where to store the name of the entry
+ * @param key where to store the object locator (set to NULL to ignore)
+ * @param nspace where to store the object namespace (set to NULL to ignore)
+ * @returns 0 on success, negative error code on failure
+ * @returns -ENOENT when there are no more objects to list
+ */
+CEPH_RADOS_API int rados_nobjects_list_next(rados_list_ctx_t ctx,
+ const char **entry,
+ const char **key,
+ const char **nspace);
+
+/**
+ * Get the next object name, locator and their sizes in the pool
+ *
+ * The sizes allow to list objects with \0 (the NUL character)
+ * in .e.g *entry. Is is unusual see such object names but a bug
+ * in a client has risen the need to handle them as well.
+ * *entry and *key are valid until next call to rados_nobjects_list_*
+ *
+ * @param ctx iterator marking where you are in the listing
+ * @param entry where to store the name of the entry
+ * @param key where to store the object locator (set to NULL to ignore)
+ * @param nspace where to store the object namespace (set to NULL to ignore)
+ * @param entry_size where to store the size of name of the entry
+ * @param key_size where to store the size of object locator (set to NULL to ignore)
+ * @param nspace_size where to store the size of object namespace (set to NULL to ignore)
+ * @returns 0 on success, negative error code on failure
+ * @returns -ENOENT when there are no more objects to list
+ */
+CEPH_RADOS_API int rados_nobjects_list_next2(rados_list_ctx_t ctx,
+ const char **entry,
+ const char **key,
+ const char **nspace,
+ size_t *entry_size,
+ size_t *key_size,
+ size_t *nspace_size);
+
+/**
+ * Close the object listing handle.
+ *
+ * This should be called when the handle is no longer needed.
+ * The handle should not be used after it has been closed.
+ *
+ * @param ctx the handle to close
+ */
+CEPH_RADOS_API void rados_nobjects_list_close(rados_list_ctx_t ctx);
+
+/**
+ * Get cursor handle pointing to the *beginning* of a pool.
+ *
+ * This is an opaque handle pointing to the start of a pool. It must
+ * be released with rados_object_list_cursor_free().
+ *
+ * @param io ioctx for the pool
+ * @returns handle for the pool, NULL on error (pool does not exist)
+ */
+CEPH_RADOS_API rados_object_list_cursor rados_object_list_begin(
+ rados_ioctx_t io);
+
+/**
+ * Get cursor handle pointing to the *end* of a pool.
+ *
+ * This is an opaque handle pointing to the start of a pool. It must
+ * be released with rados_object_list_cursor_free().
+ *
+ * @param io ioctx for the pool
+ * @returns handle for the pool, NULL on error (pool does not exist)
+ */
+CEPH_RADOS_API rados_object_list_cursor rados_object_list_end(rados_ioctx_t io);
+
+/**
+ * Check if a cursor has reached the end of a pool
+ *
+ * @param io ioctx
+ * @param cur cursor
+ * @returns 1 if the cursor has reached the end of the pool, 0 otherwise
+ */
+CEPH_RADOS_API int rados_object_list_is_end(rados_ioctx_t io,
+ rados_object_list_cursor cur);
+
+/**
+ * Release a cursor
+ *
+ * Release a cursor. The handle may not be used after this point.
+ *
+ * @param io ioctx
+ * @param cur cursor
+ */
+CEPH_RADOS_API void rados_object_list_cursor_free(rados_ioctx_t io,
+ rados_object_list_cursor cur);
+
+/**
+ * Compare two cursor positions
+ *
+ * Compare two cursors, and indicate whether the first cursor precedes,
+ * matches, or follows the second.
+ *
+ * @param io ioctx
+ * @param lhs first cursor
+ * @param rhs second cursor
+ * @returns -1, 0, or 1 for lhs < rhs, lhs == rhs, or lhs > rhs
+ */
+CEPH_RADOS_API int rados_object_list_cursor_cmp(rados_ioctx_t io,
+ rados_object_list_cursor lhs, rados_object_list_cursor rhs);
+
+/**
+ * @return the number of items set in the results array
+ */
+CEPH_RADOS_API int rados_object_list(rados_ioctx_t io,
+ const rados_object_list_cursor start,
+ const rados_object_list_cursor finish,
+ const size_t result_size,
+ const char *filter_buf,
+ const size_t filter_buf_len,
+ rados_object_list_item *results,
+ rados_object_list_cursor *next);
+
+CEPH_RADOS_API void rados_object_list_free(
+ const size_t result_size,
+ rados_object_list_item *results);
+
+/**
+ * Obtain cursors delineating a subset of a range. Use this
+ * when you want to split up the work of iterating over the
+ * global namespace. Expected use case is when you are iterating
+ * in parallel, with `m` workers, and each worker taking an id `n`.
+ *
+ * @param io ioctx
+ * @param start start of the range to be sliced up (inclusive)
+ * @param finish end of the range to be sliced up (exclusive)
+ * @param n which of the m chunks you would like to get cursors for
+ * @param m how many chunks to divide start-finish into
+ * @param split_start cursor populated with start of the subrange (inclusive)
+ * @param split_finish cursor populated with end of the subrange (exclusive)
+ */
+CEPH_RADOS_API void rados_object_list_slice(rados_ioctx_t io,
+ const rados_object_list_cursor start,
+ const rados_object_list_cursor finish,
+ const size_t n,
+ const size_t m,
+ rados_object_list_cursor *split_start,
+ rados_object_list_cursor *split_finish);
+
+
+/** @} Listing Objects */
+
+/**
+ * @name Snapshots
+ *
+ * RADOS snapshots are based upon sequence numbers that form a
+ * snapshot context. They are pool-specific. The snapshot context
+ * consists of the current snapshot sequence number for a pool, and an
+ * array of sequence numbers at which snapshots were taken, in
+ * descending order. Whenever a snapshot is created or deleted, the
+ * snapshot sequence number for the pool is increased. To add a new
+ * snapshot, the new snapshot sequence number must be increased and
+ * added to the snapshot context.
+ *
+ * There are two ways to manage these snapshot contexts:
+ * -# within the RADOS cluster
+ * These are called pool snapshots, and store the snapshot context
+ * in the OSDMap. These represent a snapshot of all the objects in
+ * a pool.
+ * -# within the RADOS clients
+ * These are called self-managed snapshots, and push the
+ * responsibility for keeping track of the snapshot context to the
+ * clients. For every write, the client must send the snapshot
+ * context. In librados, this is accomplished with
+ * rados_selfmanaged_snap_set_write_ctx(). These are more
+ * difficult to manage, but are restricted to specific objects
+ * instead of applying to an entire pool.
+ *
+ * @{
+ */
+
+/**
+ * Create a pool-wide snapshot
+ *
+ * @param io the pool to snapshot
+ * @param snapname the name of the snapshot
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_ioctx_snap_create(rados_ioctx_t io,
+ const char *snapname);
+
+/**
+ * Delete a pool snapshot
+ *
+ * @param io the pool to delete the snapshot from
+ * @param snapname which snapshot to delete
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_ioctx_snap_remove(rados_ioctx_t io,
+ const char *snapname);
+
+/**
+ * Rollback an object to a pool snapshot
+ *
+ * The contents of the object will be the same as
+ * when the snapshot was taken.
+ *
+ * @param io the pool in which the object is stored
+ * @param oid the name of the object to rollback
+ * @param snapname which snapshot to rollback to
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_ioctx_snap_rollback(rados_ioctx_t io, const char *oid,
+ const char *snapname);
+
+/**
+ * @warning Deprecated: Use rados_ioctx_snap_rollback() instead
+ */
+CEPH_RADOS_API int rados_rollback(rados_ioctx_t io, const char *oid,
+ const char *snapname)
+ __attribute__((deprecated));
+
+/**
+ * Set the snapshot from which reads are performed.
+ *
+ * Subsequent reads will return data as it was at the time of that
+ * snapshot.
+ *
+ * @param io the io context to change
+ * @param snap the id of the snapshot to set, or LIBRADOS_SNAP_HEAD for no
+ * snapshot (i.e. normal operation)
+ */
+CEPH_RADOS_API void rados_ioctx_snap_set_read(rados_ioctx_t io,
+ rados_snap_t snap);
+
+/**
+ * Allocate an ID for a self-managed snapshot
+ *
+ * Get a unique ID to put in the snaphot context to create a
+ * snapshot. A clone of an object is not created until a write with
+ * the new snapshot context is completed.
+ *
+ * @param io the pool in which the snapshot will exist
+ * @param snapid where to store the newly allocated snapshot ID
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_ioctx_selfmanaged_snap_create(rados_ioctx_t io,
+ rados_snap_t *snapid);
+CEPH_RADOS_API void
+rados_aio_ioctx_selfmanaged_snap_create(rados_ioctx_t io,
+ rados_snap_t *snapid,
+ rados_completion_t completion);
+
+/**
+ * Remove a self-managed snapshot
+ *
+ * This increases the snapshot sequence number, which will cause
+ * snapshots to be removed lazily.
+ *
+ * @param io the pool in which the snapshot will exist
+ * @param snapid where to store the newly allocated snapshot ID
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_ioctx_selfmanaged_snap_remove(rados_ioctx_t io,
+ rados_snap_t snapid);
+CEPH_RADOS_API void
+rados_aio_ioctx_selfmanaged_snap_remove(rados_ioctx_t io,
+ rados_snap_t snapid,
+ rados_completion_t completion);
+
+/**
+ * Rollback an object to a self-managed snapshot
+ *
+ * The contents of the object will be the same as
+ * when the snapshot was taken.
+ *
+ * @param io the pool in which the object is stored
+ * @param oid the name of the object to rollback
+ * @param snapid which snapshot to rollback to
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_ioctx_selfmanaged_snap_rollback(rados_ioctx_t io,
+ const char *oid,
+ rados_snap_t snapid);
+
+/**
+ * Set the snapshot context for use when writing to objects
+ *
+ * This is stored in the io context, and applies to all future writes.
+ *
+ * @param io the io context to change
+ * @param seq the newest snapshot sequence number for the pool
+ * @param snaps array of snapshots in sorted by descending id
+ * @param num_snaps how many snaphosts are in the snaps array
+ * @returns 0 on success, negative error code on failure
+ * @returns -EINVAL if snaps are not in descending order
+ */
+CEPH_RADOS_API int rados_ioctx_selfmanaged_snap_set_write_ctx(rados_ioctx_t io,
+ rados_snap_t seq,
+ rados_snap_t *snaps,
+ int num_snaps);
+
+/**
+ * List all the ids of pool snapshots
+ *
+ * If the output array does not have enough space to fit all the
+ * snapshots, -ERANGE is returned and the caller should retry with a
+ * larger array.
+ *
+ * @param io the pool to read from
+ * @param snaps where to store the results
+ * @param maxlen the number of rados_snap_t that fit in the snaps array
+ * @returns number of snapshots on success, negative error code on failure
+ * @returns -ERANGE is returned if the snaps array is too short
+ */
+CEPH_RADOS_API int rados_ioctx_snap_list(rados_ioctx_t io, rados_snap_t *snaps,
+ int maxlen);
+
+/**
+ * Get the id of a pool snapshot
+ *
+ * @param io the pool to read from
+ * @param name the snapshot to find
+ * @param id where to store the result
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_ioctx_snap_lookup(rados_ioctx_t io, const char *name,
+ rados_snap_t *id);
+
+/**
+ * Get the name of a pool snapshot
+ *
+ * @param io the pool to read from
+ * @param id the snapshot to find
+ * @param name where to store the result
+ * @param maxlen the size of the name array
+ * @returns 0 on success, negative error code on failure
+ * @returns -ERANGE if the name array is too small
+ */
+CEPH_RADOS_API int rados_ioctx_snap_get_name(rados_ioctx_t io, rados_snap_t id,
+ char *name, int maxlen);
+
+/**
+ * Find when a pool snapshot occurred
+ *
+ * @param io the pool the snapshot was taken in
+ * @param id the snapshot to lookup
+ * @param t where to store the result
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_ioctx_snap_get_stamp(rados_ioctx_t io, rados_snap_t id,
+ time_t *t);
+
+/** @} Snapshots */
+
+/**
+ * @name Synchronous I/O
+ * Writes are replicated to a number of OSDs based on the
+ * configuration of the pool they are in. These write functions block
+ * until data is in memory on all replicas of the object they're
+ * writing to - they are equivalent to doing the corresponding
+ * asynchronous write, and the calling
+ * rados_ioctx_wait_for_complete(). For greater data safety, use the
+ * asynchronous functions and rados_aio_wait_for_safe().
+ *
+ * @{
+ */
+
+/**
+ * Return the version of the last object read or written to.
+ *
+ * This exposes the internal version number of the last object read or
+ * written via this io context
+ *
+ * @param io the io context to check
+ * @returns last read or written object version
+ */
+CEPH_RADOS_API uint64_t rados_get_last_version(rados_ioctx_t io);
+
+/**
+ * Write *len* bytes from *buf* into the *oid* object, starting at
+ * offset *off*. The value of *len* must be <= UINT_MAX/2.
+ *
+ * @note This will never return a positive value not equal to len.
+ * @param io the io context in which the write will occur
+ * @param oid name of the object
+ * @param buf data to write
+ * @param len length of the data, in bytes
+ * @param off byte offset in the object to begin writing at
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_write(rados_ioctx_t io, const char *oid,
+ const char *buf, size_t len, uint64_t off);
+
+/**
+ * Write *len* bytes from *buf* into the *oid* object. The value of
+ * *len* must be <= UINT_MAX/2.
+ *
+ * The object is filled with the provided data. If the object exists,
+ * it is atomically truncated and then written.
+ *
+ * @param io the io context in which the write will occur
+ * @param oid name of the object
+ * @param buf data to write
+ * @param len length of the data, in bytes
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_write_full(rados_ioctx_t io, const char *oid,
+ const char *buf, size_t len);
+
+/**
+ * Write the same *data_len* bytes from *buf* multiple times into the
+ * *oid* object. *write_len* bytes are written in total, which must be
+ * a multiple of *data_len*. The value of *write_len* and *data_len*
+ * must be <= UINT_MAX/2.
+ *
+ * @param io the io context in which the write will occur
+ * @param oid name of the object
+ * @param buf data to write
+ * @param data_len length of the data, in bytes
+ * @param write_len the total number of bytes to write
+ * @param off byte offset in the object to begin writing at
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_writesame(rados_ioctx_t io, const char *oid,
+ const char *buf, size_t data_len,
+ size_t write_len, uint64_t off);
+
+/**
+ * Append *len* bytes from *buf* into the *oid* object. The value of
+ * *len* must be <= UINT_MAX/2.
+ *
+ * @param io the context to operate in
+ * @param oid the name of the object
+ * @param buf the data to append
+ * @param len length of buf (in bytes)
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_append(rados_ioctx_t io, const char *oid,
+ const char *buf, size_t len);
+
+/**
+ * Read data from an object
+ *
+ * The io context determines the snapshot to read from, if any was set
+ * by rados_ioctx_snap_set_read().
+ *
+ * @param io the context in which to perform the read
+ * @param oid the name of the object to read from
+ * @param buf where to store the results
+ * @param len the number of bytes to read
+ * @param off the offset to start reading from in the object
+ * @returns number of bytes read on success, negative error code on
+ * failure
+ */
+CEPH_RADOS_API int rados_read(rados_ioctx_t io, const char *oid, char *buf,
+ size_t len, uint64_t off);
+
+/**
+ * Compute checksum from object data
+ *
+ * The io context determines the snapshot to checksum, if any was set
+ * by rados_ioctx_snap_set_read(). The length of the init_value and
+ * resulting checksum are dependent upon the checksum type:
+ *
+ * XXHASH64: le64
+ * XXHASH32: le32
+ * CRC32C: le32
+ *
+ * The checksum result is encoded the following manner:
+ *
+ * le32 num_checksum_chunks
+ * {
+ * leXX checksum for chunk (where XX = appropriate size for the checksum type)
+ * } * num_checksum_chunks
+ *
+ * @param io the context in which to perform the checksum
+ * @param oid the name of the object to checksum
+ * @param type the checksum algorithm to utilize
+ * @param init_value the init value for the algorithm
+ * @param init_value_len the length of the init value
+ * @param len the number of bytes to checksum
+ * @param off the offset to start checksumming in the object
+ * @param chunk_size optional length-aligned chunk size for checksums
+ * @param pchecksum where to store the checksum result
+ * @param checksum_len the number of bytes available for the result
+ * @return negative error code on failure
+ */
+CEPH_RADOS_API int rados_checksum(rados_ioctx_t io, const char *oid,
+ rados_checksum_type_t type,
+ const char *init_value, size_t init_value_len,
+ size_t len, uint64_t off, size_t chunk_size,
+ char *pchecksum, size_t checksum_len);
+
+/**
+ * Delete an object
+ *
+ * @note This does not delete any snapshots of the object.
+ *
+ * @param io the pool to delete the object from
+ * @param oid the name of the object to delete
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_remove(rados_ioctx_t io, const char *oid);
+
+/**
+ * Resize an object
+ *
+ * If this enlarges the object, the new area is logically filled with
+ * zeroes. If this shrinks the object, the excess data is removed.
+ *
+ * @param io the context in which to truncate
+ * @param oid the name of the object
+ * @param size the new size of the object in bytes
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_trunc(rados_ioctx_t io, const char *oid,
+ uint64_t size);
+
+/**
+ * Compare an on-disk object range with a buffer
+ *
+ * @param io the context in which to perform the comparison
+ * @param o name of the object
+ * @param cmp_buf buffer containing bytes to be compared with object contents
+ * @param cmp_len length to compare and size of @c cmp_buf in bytes
+ * @param off object byte offset at which to start the comparison
+ * @returns 0 on success, negative error code on failure,
+ * (-MAX_ERRNO - mismatch_off) on mismatch
+ */
+CEPH_RADOS_API int rados_cmpext(rados_ioctx_t io, const char *o,
+ const char *cmp_buf, size_t cmp_len,
+ uint64_t off);
+
+/**
+ * @name Xattrs
+ * Extended attributes are stored as extended attributes on the files
+ * representing an object on the OSDs. Thus, they have the same
+ * limitations as the underlying filesystem. On ext4, this means that
+ * the total data stored in xattrs cannot exceed 4KB.
+ *
+ * @{
+ */
+
+/**
+ * Get the value of an extended attribute on an object.
+ *
+ * @param io the context in which the attribute is read
+ * @param o name of the object
+ * @param name which extended attribute to read
+ * @param buf where to store the result
+ * @param len size of buf in bytes
+ * @returns length of xattr value on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_getxattr(rados_ioctx_t io, const char *o,
+ const char *name, char *buf, size_t len);
+
+/**
+ * Set an extended attribute on an object.
+ *
+ * @param io the context in which xattr is set
+ * @param o name of the object
+ * @param name which extended attribute to set
+ * @param buf what to store in the xattr
+ * @param len the number of bytes in buf
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_setxattr(rados_ioctx_t io, const char *o,
+ const char *name, const char *buf,
+ size_t len);
+
+/**
+ * Delete an extended attribute from an object.
+ *
+ * @param io the context in which to delete the xattr
+ * @param o the name of the object
+ * @param name which xattr to delete
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_rmxattr(rados_ioctx_t io, const char *o,
+ const char *name);
+
+/**
+ * Start iterating over xattrs on an object.
+ *
+ * @post iter is a valid iterator
+ *
+ * @param io the context in which to list xattrs
+ * @param oid name of the object
+ * @param iter where to store the iterator
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_getxattrs(rados_ioctx_t io, const char *oid,
+ rados_xattrs_iter_t *iter);
+
+/**
+ * Get the next xattr on the object
+ *
+ * @pre iter is a valid iterator
+ *
+ * @post name is the NULL-terminated name of the next xattr, and val
+ * contains the value of the xattr, which is of length len. If the end
+ * of the list has been reached, name and val are NULL, and len is 0.
+ *
+ * @param iter iterator to advance
+ * @param name where to store the name of the next xattr
+ * @param val where to store the value of the next xattr
+ * @param len the number of bytes in val
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_getxattrs_next(rados_xattrs_iter_t iter,
+ const char **name, const char **val,
+ size_t *len);
+
+/**
+ * Close the xattr iterator.
+ *
+ * iter should not be used after this is called.
+ *
+ * @param iter the iterator to close
+ */
+CEPH_RADOS_API void rados_getxattrs_end(rados_xattrs_iter_t iter);
+
+/** @} Xattrs */
+
+/**
+ * Get the next omap key/value pair on the object
+ *
+ * @pre iter is a valid iterator
+ *
+ * @post key and val are the next key/value pair. key is
+ * null-terminated, and val has length len. If the end of the list has
+ * been reached, key and val are NULL, and len is 0. key and val will
+ * not be accessible after rados_omap_get_end() is called on iter, so
+ * if they are needed after that they should be copied.
+ *
+ * @param iter iterator to advance
+ * @param key where to store the key of the next omap entry
+ * @param val where to store the value of the next omap entry
+ * @param len where to store the number of bytes in val
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_omap_get_next(rados_omap_iter_t iter,
+ char **key,
+ char **val,
+ size_t *len);
+
+/**
+ * Get the next omap key/value pair on the object. Note that it's
+ * perfectly safe to mix calls to rados_omap_get_next and
+ * rados_omap_get_next2.
+ *
+ * @pre iter is a valid iterator
+ *
+ * @post key and val are the next key/value pair. key has length
+ * keylen and val has length vallen. If the end of the list has
+ * been reached, key and val are NULL, and keylen and vallen is 0.
+ * key and val will not be accessible after rados_omap_get_end()
+ * is called on iter, so if they are needed after that they
+ * should be copied.
+ *
+ * @param iter iterator to advance
+ * @param key where to store the key of the next omap entry
+ * @param val where to store the value of the next omap entry
+ * @param key_len where to store the number of bytes in key
+ * @param val_len where to store the number of bytes in val
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_omap_get_next2(rados_omap_iter_t iter,
+ char **key,
+ char **val,
+ size_t *key_len,
+ size_t *val_len);
+
+/**
+ * Return number of elements in the iterator
+ *
+ * @param iter the iterator of which to return the size
+ */
+CEPH_RADOS_API unsigned int rados_omap_iter_size(rados_omap_iter_t iter);
+
+/**
+ * Close the omap iterator.
+ *
+ * iter should not be used after this is called.
+ *
+ * @param iter the iterator to close
+ */
+CEPH_RADOS_API void rados_omap_get_end(rados_omap_iter_t iter);
+
+/**
+ * Get object size and most recent update time from the OSD.
+ *
+ * @param io ioctx
+ * @param o object name
+ * @param psize where to store object size
+ * @param pmtime where to store modification time
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_stat(rados_ioctx_t io, const char *o, uint64_t *psize,
+ time_t *pmtime);
+
+CEPH_RADOS_API int rados_stat2(rados_ioctx_t io, const char *o, uint64_t *psize,
+ struct timespec *pmtime);
+
+/**
+ * Execute an OSD class method on an object
+ *
+ * The OSD has a plugin mechanism for performing complicated
+ * operations on an object atomically. These plugins are called
+ * classes. This function allows librados users to call the custom
+ * methods. The input and output formats are defined by the class.
+ * Classes in ceph.git can be found in src/cls subdirectories
+ *
+ * @param io the context in which to call the method
+ * @param oid the object to call the method on
+ * @param cls the name of the class
+ * @param method the name of the method
+ * @param in_buf where to find input
+ * @param in_len length of in_buf in bytes
+ * @param buf where to store output
+ * @param out_len length of buf in bytes
+ * @returns the length of the output, or
+ * -ERANGE if out_buf does not have enough space to store it (For methods that return data). For
+ * methods that don't return data, the return value is
+ * method-specific.
+ */
+CEPH_RADOS_API int rados_exec(rados_ioctx_t io, const char *oid,
+ const char *cls, const char *method,
+ const char *in_buf, size_t in_len, char *buf,
+ size_t out_len);
+
+
+/** @} Synchronous I/O */
+
+/**
+ * @name Asynchronous I/O
+ * Read and write to objects without blocking.
+ *
+ * @{
+ */
+
+/**
+ * @typedef rados_callback_t
+ * Callbacks for asynchrous operations take two parameters:
+ * - cb the completion that has finished
+ * - arg application defined data made available to the callback function
+ */
+typedef void (*rados_callback_t)(rados_completion_t cb, void *arg);
+
+/**
+ * Constructs a completion to use with asynchronous operations
+ *
+ * The complete and safe callbacks correspond to operations being
+ * acked and committed, respectively. The callbacks are called in
+ * order of receipt, so the safe callback may be triggered before the
+ * complete callback, and vice versa. This is affected by journalling
+ * on the OSDs.
+ *
+ * TODO: more complete documentation of this elsewhere (in the RADOS docs?)
+ *
+ * @note Read operations only get a complete callback.
+ * @note BUG: this should check for ENOMEM instead of throwing an exception
+ *
+ * @param cb_arg application-defined data passed to the callback functions
+ * @param cb_complete the function to be called when the operation is
+ * in memory on all replicas
+ * @param cb_safe the function to be called when the operation is on
+ * stable storage on all replicas
+ * @param pc where to store the completion
+ * @returns 0
+ */
+CEPH_RADOS_API int rados_aio_create_completion(void *cb_arg,
+ rados_callback_t cb_complete,
+ rados_callback_t cb_safe,
+ rados_completion_t *pc);
+
+/**
+ * Constructs a completion to use with asynchronous operations
+ *
+ * The complete callback corresponds to operation being acked.
+ *
+ * @note BUG: this should check for ENOMEM instead of throwing an exception
+ *
+ * @param cb_arg application-defined data passed to the callback functions
+ * @param cb_complete the function to be called when the operation is committed
+ * on all replicas
+ * @param pc where to store the completion
+ * @returns 0
+ */
+CEPH_RADOS_API int rados_aio_create_completion2(void *cb_arg,
+ rados_callback_t cb_complete,
+ rados_completion_t *pc);
+
+/**
+ * Block until an operation completes
+ *
+ * This means it is in memory on all replicas.
+ *
+ * @note BUG: this should be void
+ *
+ * @param c operation to wait for
+ * @returns 0
+ */
+CEPH_RADOS_API int rados_aio_wait_for_complete(rados_completion_t c);
+
+/**
+ * Block until an operation is safe
+ *
+ * This means it is on stable storage on all replicas.
+ *
+ * @note BUG: this should be void
+ *
+ * @param c operation to wait for
+ * @returns 0
+ */
+CEPH_RADOS_API int rados_aio_wait_for_safe(rados_completion_t c)
+ __attribute__((deprecated));
+
+/**
+ * Has an asynchronous operation completed?
+ *
+ * @warning This does not imply that the complete callback has
+ * finished
+ *
+ * @param c async operation to inspect
+ * @returns whether c is complete
+ */
+CEPH_RADOS_API int rados_aio_is_complete(rados_completion_t c);
+
+/**
+ * Is an asynchronous operation safe?
+ *
+ * @warning This does not imply that the safe callback has
+ * finished
+ *
+ * @param c async operation to inspect
+ * @returns whether c is safe
+ */
+CEPH_RADOS_API int rados_aio_is_safe(rados_completion_t c);
+
+/**
+ * Block until an operation completes and callback completes
+ *
+ * This means it is in memory on all replicas and can be read.
+ *
+ * @note BUG: this should be void
+ *
+ * @param c operation to wait for
+ * @returns 0
+ */
+CEPH_RADOS_API int rados_aio_wait_for_complete_and_cb(rados_completion_t c);
+
+/**
+ * Block until an operation is safe and callback has completed
+ *
+ * This means it is on stable storage on all replicas.
+ *
+ * @note BUG: this should be void
+ *
+ * @param c operation to wait for
+ * @returns 0
+ */
+CEPH_RADOS_API int rados_aio_wait_for_safe_and_cb(rados_completion_t c)
+ __attribute__((deprecated));
+
+/**
+ * Has an asynchronous operation and callback completed
+ *
+ * @param c async operation to inspect
+ * @returns whether c is complete
+ */
+CEPH_RADOS_API int rados_aio_is_complete_and_cb(rados_completion_t c);
+
+/**
+ * Is an asynchronous operation safe and has the callback completed
+ *
+ * @param c async operation to inspect
+ * @returns whether c is safe
+ */
+CEPH_RADOS_API int rados_aio_is_safe_and_cb(rados_completion_t c);
+
+/**
+ * Get the return value of an asychronous operation
+ *
+ * The return value is set when the operation is complete or safe,
+ * whichever comes first.
+ *
+ * @pre The operation is safe or complete
+ *
+ * @note BUG: complete callback may never be called when the safe
+ * message is received before the complete message
+ *
+ * @param c async operation to inspect
+ * @returns return value of the operation
+ */
+CEPH_RADOS_API int rados_aio_get_return_value(rados_completion_t c);
+
+/**
+ * Get the internal object version of the target of an asychronous operation
+ *
+ * The return value is set when the operation is complete or safe,
+ * whichever comes first.
+ *
+ * @pre The operation is safe or complete
+ *
+ * @note BUG: complete callback may never be called when the safe
+ * message is received before the complete message
+ *
+ * @param c async operation to inspect
+ * @returns version number of the asychronous operation's target
+ */
+CEPH_RADOS_API uint64_t rados_aio_get_version(rados_completion_t c);
+
+/**
+ * Release a completion
+ *
+ * Call this when you no longer need the completion. It may not be
+ * freed immediately if the operation is not acked and committed.
+ *
+ * @param c completion to release
+ */
+CEPH_RADOS_API void rados_aio_release(rados_completion_t c);
+
+/**
+ * Write data to an object asynchronously
+ *
+ * Queues the write and returns. The return value of the completion
+ * will be 0 on success, negative error code on failure.
+ *
+ * @param io the context in which the write will occur
+ * @param oid name of the object
+ * @param completion what to do when the write is safe and complete
+ * @param buf data to write
+ * @param len length of the data, in bytes
+ * @param off byte offset in the object to begin writing at
+ * @returns 0 on success, -EROFS if the io context specifies a snap_seq
+ * other than LIBRADOS_SNAP_HEAD
+ */
+CEPH_RADOS_API int rados_aio_write(rados_ioctx_t io, const char *oid,
+ rados_completion_t completion,
+ const char *buf, size_t len, uint64_t off);
+
+/**
+ * Asynchronously append data to an object
+ *
+ * Queues the append and returns.
+ *
+ * The return value of the completion will be 0 on success, negative
+ * error code on failure.
+ *
+ * @param io the context to operate in
+ * @param oid the name of the object
+ * @param completion what to do when the append is safe and complete
+ * @param buf the data to append
+ * @param len length of buf (in bytes)
+ * @returns 0 on success, -EROFS if the io context specifies a snap_seq
+ * other than LIBRADOS_SNAP_HEAD
+ */
+CEPH_RADOS_API int rados_aio_append(rados_ioctx_t io, const char *oid,
+ rados_completion_t completion,
+ const char *buf, size_t len);
+
+/**
+ * Asynchronously write an entire object
+ *
+ * The object is filled with the provided data. If the object exists,
+ * it is atomically truncated and then written.
+ * Queues the write_full and returns.
+ *
+ * The return value of the completion will be 0 on success, negative
+ * error code on failure.
+ *
+ * @param io the io context in which the write will occur
+ * @param oid name of the object
+ * @param completion what to do when the write_full is safe and complete
+ * @param buf data to write
+ * @param len length of the data, in bytes
+ * @returns 0 on success, -EROFS if the io context specifies a snap_seq
+ * other than LIBRADOS_SNAP_HEAD
+ */
+CEPH_RADOS_API int rados_aio_write_full(rados_ioctx_t io, const char *oid,
+ rados_completion_t completion,
+ const char *buf, size_t len);
+
+/**
+ * Asynchronously write the same buffer multiple times
+ *
+ * Queues the writesame and returns.
+ *
+ * The return value of the completion will be 0 on success, negative
+ * error code on failure.
+ *
+ * @param io the io context in which the write will occur
+ * @param oid name of the object
+ * @param completion what to do when the writesame is safe and complete
+ * @param buf data to write
+ * @param data_len length of the data, in bytes
+ * @param write_len the total number of bytes to write
+ * @param off byte offset in the object to begin writing at
+ * @returns 0 on success, -EROFS if the io context specifies a snap_seq
+ * other than LIBRADOS_SNAP_HEAD
+ */
+CEPH_RADOS_API int rados_aio_writesame(rados_ioctx_t io, const char *oid,
+ rados_completion_t completion,
+ const char *buf, size_t data_len,
+ size_t write_len, uint64_t off);
+
+/**
+ * Asynchronously remove an object
+ *
+ * Queues the remove and returns.
+ *
+ * The return value of the completion will be 0 on success, negative
+ * error code on failure.
+ *
+ * @param io the context to operate in
+ * @param oid the name of the object
+ * @param completion what to do when the remove is safe and complete
+ * @returns 0 on success, -EROFS if the io context specifies a snap_seq
+ * other than LIBRADOS_SNAP_HEAD
+ */
+CEPH_RADOS_API int rados_aio_remove(rados_ioctx_t io, const char *oid,
+ rados_completion_t completion);
+
+/**
+ * Asynchronously read data from an object
+ *
+ * The io context determines the snapshot to read from, if any was set
+ * by rados_ioctx_snap_set_read().
+ *
+ * The return value of the completion will be number of bytes read on
+ * success, negative error code on failure.
+ *
+ * @note only the 'complete' callback of the completion will be called.
+ *
+ * @param io the context in which to perform the read
+ * @param oid the name of the object to read from
+ * @param completion what to do when the read is complete
+ * @param buf where to store the results
+ * @param len the number of bytes to read
+ * @param off the offset to start reading from in the object
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_aio_read(rados_ioctx_t io, const char *oid,
+ rados_completion_t completion,
+ char *buf, size_t len, uint64_t off);
+
+/**
+ * Block until all pending writes in an io context are safe
+ *
+ * This is not equivalent to calling rados_aio_wait_for_safe() on all
+ * write completions, since this waits for the associated callbacks to
+ * complete as well.
+ *
+ * @note BUG: always returns 0, should be void or accept a timeout
+ *
+ * @param io the context to flush
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_aio_flush(rados_ioctx_t io);
+
+
+/**
+ * Schedule a callback for when all currently pending
+ * aio writes are safe. This is a non-blocking version of
+ * rados_aio_flush().
+ *
+ * @param io the context to flush
+ * @param completion what to do when the writes are safe
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_aio_flush_async(rados_ioctx_t io,
+ rados_completion_t completion);
+
+
+/**
+ * Asynchronously get object stats (size/mtime)
+ *
+ * @param io ioctx
+ * @param o object name
+ * @param completion what to do when the stat is complete
+ * @param psize where to store object size
+ * @param pmtime where to store modification time
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_aio_stat(rados_ioctx_t io, const char *o,
+ rados_completion_t completion,
+ uint64_t *psize, time_t *pmtime);
+
+CEPH_RADOS_API int rados_aio_stat2(rados_ioctx_t io, const char *o,
+ rados_completion_t completion,
+ uint64_t *psize, struct timespec *pmtime);
+
+/**
+ * Asynchronously compare an on-disk object range with a buffer
+ *
+ * @param io the context in which to perform the comparison
+ * @param o the name of the object to compare with
+ * @param completion what to do when the comparison is complete
+ * @param cmp_buf buffer containing bytes to be compared with object contents
+ * @param cmp_len length to compare and size of @c cmp_buf in bytes
+ * @param off object byte offset at which to start the comparison
+ * @returns 0 on success, negative error code on failure,
+ * (-MAX_ERRNO - mismatch_off) on mismatch
+ */
+CEPH_RADOS_API int rados_aio_cmpext(rados_ioctx_t io, const char *o,
+ rados_completion_t completion,
+ const char *cmp_buf,
+ size_t cmp_len,
+ uint64_t off);
+
+/**
+ * Cancel async operation
+ *
+ * @param io ioctx
+ * @param completion completion handle
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_aio_cancel(rados_ioctx_t io,
+ rados_completion_t completion);
+
+/**
+ * Asynchronously execute an OSD class method on an object
+ *
+ * The OSD has a plugin mechanism for performing complicated
+ * operations on an object atomically. These plugins are called
+ * classes. This function allows librados users to call the custom
+ * methods. The input and output formats are defined by the class.
+ * Classes in ceph.git can be found in src/cls subdirectories
+ *
+ * @param io the context in which to call the method
+ * @param o name of the object
+ * @param completion what to do when the exec completes
+ * @param cls the name of the class
+ * @param method the name of the method
+ * @param in_buf where to find input
+ * @param in_len length of in_buf in bytes
+ * @param buf where to store output
+ * @param out_len length of buf in bytes
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_aio_exec(rados_ioctx_t io, const char *o,
+ rados_completion_t completion,
+ const char *cls, const char *method,
+ const char *in_buf, size_t in_len,
+ char *buf, size_t out_len);
+
+/** @} Asynchronous I/O */
+
+/**
+ * @name Asynchronous Xattrs
+ * Extended attributes are stored as extended attributes on the files
+ * representing an object on the OSDs. Thus, they have the same
+ * limitations as the underlying filesystem. On ext4, this means that
+ * the total data stored in xattrs cannot exceed 4KB.
+ *
+ * @{
+ */
+
+/**
+ * Asynchronously get the value of an extended attribute on an object.
+ *
+ * @param io the context in which the attribute is read
+ * @param o name of the object
+ * @param completion what to do when the getxattr completes
+ * @param name which extended attribute to read
+ * @param buf where to store the result
+ * @param len size of buf in bytes
+ * @returns length of xattr value on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_aio_getxattr(rados_ioctx_t io, const char *o,
+ rados_completion_t completion,
+ const char *name, char *buf, size_t len);
+
+/**
+ * Asynchronously set an extended attribute on an object.
+ *
+ * @param io the context in which xattr is set
+ * @param o name of the object
+ * @param completion what to do when the setxattr completes
+ * @param name which extended attribute to set
+ * @param buf what to store in the xattr
+ * @param len the number of bytes in buf
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_aio_setxattr(rados_ioctx_t io, const char *o,
+ rados_completion_t completion,
+ const char *name, const char *buf,
+ size_t len);
+
+/**
+ * Asynchronously delete an extended attribute from an object.
+ *
+ * @param io the context in which to delete the xattr
+ * @param o the name of the object
+ * @param completion what to do when the rmxattr completes
+ * @param name which xattr to delete
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_aio_rmxattr(rados_ioctx_t io, const char *o,
+ rados_completion_t completion,
+ const char *name);
+
+/**
+ * Asynchronously start iterating over xattrs on an object.
+ *
+ * @post iter is a valid iterator
+ *
+ * @param io the context in which to list xattrs
+ * @param oid name of the object
+ * @param completion what to do when the getxattrs completes
+ * @param iter where to store the iterator
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_aio_getxattrs(rados_ioctx_t io, const char *oid,
+ rados_completion_t completion,
+ rados_xattrs_iter_t *iter);
+
+/** @} Asynchronous Xattrs */
+
+/**
+ * @name Watch/Notify
+ *
+ * Watch/notify is a protocol to help communicate among clients. It
+ * can be used to sychronize client state. All that's needed is a
+ * well-known object name (for example, rbd uses the header object of
+ * an image).
+ *
+ * Watchers register an interest in an object, and receive all
+ * notifies on that object. A notify attempts to communicate with all
+ * clients watching an object, and blocks on the notifier until each
+ * client responds or a timeout is reached.
+ *
+ * See rados_watch() and rados_notify() for more details.
+ *
+ * @{
+ */
+
+/**
+ * @typedef rados_watchcb_t
+ *
+ * Callback activated when a notify is received on a watched
+ * object.
+ *
+ * @param opcode undefined
+ * @param ver version of the watched object
+ * @param arg application-specific data
+ *
+ * @note BUG: opcode is an internal detail that shouldn't be exposed
+ * @note BUG: ver is unused
+ */
+typedef void (*rados_watchcb_t)(uint8_t opcode, uint64_t ver, void *arg);
+
+/**
+ * @typedef rados_watchcb2_t
+ *
+ * Callback activated when a notify is received on a watched
+ * object.
+ *
+ * @param arg opaque user-defined value provided to rados_watch2()
+ * @param notify_id an id for this notify event
+ * @param handle the watcher handle we are notifying
+ * @param notifier_id the unique client id for the notifier
+ * @param data payload from the notifier
+ * @param data_len length of payload buffer
+ */
+typedef void (*rados_watchcb2_t)(void *arg,
+ uint64_t notify_id,
+ uint64_t handle,
+ uint64_t notifier_id,
+ void *data,
+ size_t data_len);
+
+/**
+ * @typedef rados_watcherrcb_t
+ *
+ * Callback activated when we encounter an error with the watch session.
+ * This can happen when the location of the objects moves within the
+ * cluster and we fail to register our watch with the new object location,
+ * or when our connection with the object OSD is otherwise interrupted and
+ * we may have missed notify events.
+ *
+ * @param pre opaque user-defined value provided to rados_watch2()
+ * @param cookie the internal id assigned to the watch session
+ * @param err error code
+ */
+ typedef void (*rados_watcherrcb_t)(void *pre, uint64_t cookie, int err);
+
+/**
+ * Register an interest in an object
+ *
+ * A watch operation registers the client as being interested in
+ * notifications on an object. OSDs keep track of watches on
+ * persistent storage, so they are preserved across cluster changes by
+ * the normal recovery process. If the client loses its connection to
+ * the primary OSD for a watched object, the watch will be removed
+ * after 30 seconds. Watches are automatically reestablished when a new
+ * connection is made, or a placement group switches OSDs.
+ *
+ * @note BUG: librados should provide a way for watchers to notice connection resets
+ * @note BUG: the ver parameter does not work, and -ERANGE will never be returned
+ * (See URL tracker.ceph.com/issues/2592)
+ *
+ * @param io the pool the object is in
+ * @param o the object to watch
+ * @param ver expected version of the object
+ * @param cookie where to store the internal id assigned to this watch
+ * @param watchcb what to do when a notify is received on this object
+ * @param arg application defined data to pass when watchcb is called
+ * @returns 0 on success, negative error code on failure
+ * @returns -ERANGE if the version of the object is greater than ver
+ */
+CEPH_RADOS_API int rados_watch(rados_ioctx_t io, const char *o, uint64_t ver,
+ uint64_t *cookie,
+ rados_watchcb_t watchcb, void *arg)
+ __attribute__((deprecated));
+
+
+/**
+ * Register an interest in an object
+ *
+ * A watch operation registers the client as being interested in
+ * notifications on an object. OSDs keep track of watches on
+ * persistent storage, so they are preserved across cluster changes by
+ * the normal recovery process. If the client loses its connection to the
+ * primary OSD for a watched object, the watch will be removed after
+ * a timeout configured with osd_client_watch_timeout.
+ * Watches are automatically reestablished when a new
+ * connection is made, or a placement group switches OSDs.
+ *
+ * @param io the pool the object is in
+ * @param o the object to watch
+ * @param cookie where to store the internal id assigned to this watch
+ * @param watchcb what to do when a notify is received on this object
+ * @param watcherrcb what to do when the watch session encounters an error
+ * @param arg opaque value to pass to the callback
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_watch2(rados_ioctx_t io, const char *o, uint64_t *cookie,
+ rados_watchcb2_t watchcb,
+ rados_watcherrcb_t watcherrcb,
+ void *arg);
+
+/**
+ * Register an interest in an object
+ *
+ * A watch operation registers the client as being interested in
+ * notifications on an object. OSDs keep track of watches on
+ * persistent storage, so they are preserved across cluster changes by
+ * the normal recovery process. Watches are automatically reestablished when a new
+ * connection is made, or a placement group switches OSDs.
+ *
+ * @param io the pool the object is in
+ * @param o the object to watch
+ * @param cookie where to store the internal id assigned to this watch
+ * @param watchcb what to do when a notify is received on this object
+ * @param watcherrcb what to do when the watch session encounters an error
+ * @param timeout how many seconds the connection will keep after disconnection
+ * @param arg opaque value to pass to the callback
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_watch3(rados_ioctx_t io, const char *o, uint64_t *cookie,
+ rados_watchcb2_t watchcb,
+ rados_watcherrcb_t watcherrcb,
+ uint32_t timeout,
+ void *arg);
+
+/**
+ * Asynchronous register an interest in an object
+ *
+ * A watch operation registers the client as being interested in
+ * notifications on an object. OSDs keep track of watches on
+ * persistent storage, so they are preserved across cluster changes by
+ * the normal recovery process. If the client loses its connection to
+ * the primary OSD for a watched object, the watch will be removed
+ * after 30 seconds. Watches are automatically reestablished when a new
+ * connection is made, or a placement group switches OSDs.
+ *
+ * @param io the pool the object is in
+ * @param o the object to watch
+ * @param completion what to do when operation has been attempted
+ * @param handle where to store the internal id assigned to this watch
+ * @param watchcb what to do when a notify is received on this object
+ * @param watcherrcb what to do when the watch session encounters an error
+ * @param arg opaque value to pass to the callback
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_aio_watch(rados_ioctx_t io, const char *o,
+ rados_completion_t completion, uint64_t *handle,
+ rados_watchcb2_t watchcb,
+ rados_watcherrcb_t watcherrcb,
+ void *arg);
+
+/**
+ * Asynchronous register an interest in an object
+ *
+ * A watch operation registers the client as being interested in
+ * notifications on an object. OSDs keep track of watches on
+ * persistent storage, so they are preserved across cluster changes by
+ * the normal recovery process. If the client loses its connection to
+ * the primary OSD for a watched object, the watch will be removed
+ * after the number of seconds that configured in timeout parameter.
+ * Watches are automatically reestablished when a new
+ * connection is made, or a placement group switches OSDs.
+ *
+ * @param io the pool the object is in
+ * @param o the object to watch
+ * @param completion what to do when operation has been attempted
+ * @param handle where to store the internal id assigned to this watch
+ * @param watchcb what to do when a notify is received on this object
+ * @param watcherrcb what to do when the watch session encounters an error
+ * @param timeout how many seconds the connection will keep after disconnection
+ * @param arg opaque value to pass to the callback
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_aio_watch2(rados_ioctx_t io, const char *o,
+ rados_completion_t completion, uint64_t *handle,
+ rados_watchcb2_t watchcb,
+ rados_watcherrcb_t watcherrcb,
+ uint32_t timeout,
+ void *arg);
+
+/**
+ * Check on the status of a watch
+ *
+ * Return the number of milliseconds since the watch was last confirmed.
+ * Or, if there has been an error, return that.
+ *
+ * If there is an error, the watch is no longer valid, and should be
+ * destroyed with rados_unwatch2(). The the user is still interested
+ * in the object, a new watch should be created with rados_watch2().
+ *
+ * @param io the pool the object is in
+ * @param cookie the watch handle
+ * @returns ms since last confirmed on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_watch_check(rados_ioctx_t io, uint64_t cookie);
+
+/**
+ * Unregister an interest in an object
+ *
+ * Once this completes, no more notifies will be sent to us for this
+ * watch. This should be called to clean up unneeded watchers.
+ *
+ * @param io the pool the object is in
+ * @param o the name of the watched object (ignored)
+ * @param cookie which watch to unregister
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_unwatch(rados_ioctx_t io, const char *o, uint64_t cookie)
+ __attribute__((deprecated));
+
+/**
+ * Unregister an interest in an object
+ *
+ * Once this completes, no more notifies will be sent to us for this
+ * watch. This should be called to clean up unneeded watchers.
+ *
+ * @param io the pool the object is in
+ * @param cookie which watch to unregister
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_unwatch2(rados_ioctx_t io, uint64_t cookie);
+
+/**
+ * Asynchronous unregister an interest in an object
+ *
+ * Once this completes, no more notifies will be sent to us for this
+ * watch. This should be called to clean up unneeded watchers.
+ *
+ * @param io the pool the object is in
+ * @param completion what to do when operation has been attempted
+ * @param cookie which watch to unregister
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_aio_unwatch(rados_ioctx_t io, uint64_t cookie,
+ rados_completion_t completion);
+
+/**
+ * Sychronously notify watchers of an object
+ *
+ * This blocks until all watchers of the object have received and
+ * reacted to the notify, or a timeout is reached.
+ *
+ * @note BUG: the timeout is not changeable via the C API
+ * @note BUG: the bufferlist is inaccessible in a rados_watchcb_t
+ *
+ * @param io the pool the object is in
+ * @param o the name of the object
+ * @param ver obsolete - just pass zero
+ * @param buf data to send to watchers
+ * @param buf_len length of buf in bytes
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_notify(rados_ioctx_t io, const char *o, uint64_t ver,
+ const char *buf, int buf_len)
+ __attribute__((deprecated));
+
+/**
+ * Sychronously notify watchers of an object
+ *
+ * This blocks until all watchers of the object have received and
+ * reacted to the notify, or a timeout is reached.
+ *
+ * The reply buffer is optional. If specified, the client will get
+ * back an encoded buffer that includes the ids of the clients that
+ * acknowledged the notify as well as their notify ack payloads (if
+ * any). Clients that timed out are not included. Even clients that
+ * do not include a notify ack payload are included in the list but
+ * have a 0-length payload associated with them. The format:
+ *
+ * le32 num_acks
+ * {
+ * le64 gid global id for the client (for client.1234 that's 1234)
+ * le64 cookie cookie for the client
+ * le32 buflen length of reply message buffer
+ * u8 * buflen payload
+ * } * num_acks
+ * le32 num_timeouts
+ * {
+ * le64 gid global id for the client
+ * le64 cookie cookie for the client
+ * } * num_timeouts
+ *
+ * Note: There may be multiple instances of the same gid if there are
+ * multiple watchers registered via the same client.
+ *
+ * Note: The buffer must be released with rados_buffer_free() when the
+ * user is done with it.
+ *
+ * Note: Since the result buffer includes clients that time out, it
+ * will be set even when rados_notify() returns an error code (like
+ * -ETIMEDOUT).
+ *
+ * @param io the pool the object is in
+ * @param completion what to do when operation has been attempted
+ * @param o the name of the object
+ * @param buf data to send to watchers
+ * @param buf_len length of buf in bytes
+ * @param timeout_ms notify timeout (in ms)
+ * @param reply_buffer pointer to reply buffer pointer (free with rados_buffer_free)
+ * @param reply_buffer_len pointer to size of reply buffer
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_aio_notify(rados_ioctx_t io, const char *o,
+ rados_completion_t completion,
+ const char *buf, int buf_len,
+ uint64_t timeout_ms, char **reply_buffer,
+ size_t *reply_buffer_len);
+CEPH_RADOS_API int rados_notify2(rados_ioctx_t io, const char *o,
+ const char *buf, int buf_len,
+ uint64_t timeout_ms,
+ char **reply_buffer, size_t *reply_buffer_len);
+
+/**
+ * Decode a notify response
+ *
+ * Decode a notify response (from rados_aio_notify() call) into acks and
+ * timeout arrays.
+ *
+ * @param reply_buffer buffer from rados_aio_notify() call
+ * @param reply_buffer_len reply_buffer length
+ * @param acks pointer to struct notify_ack_t pointer
+ * @param nr_acks pointer to ack count
+ * @param timeouts pointer to notify_timeout_t pointer
+ * @param nr_timeouts pointer to timeout count
+ * @returns 0 on success
+ */
+CEPH_RADOS_API int rados_decode_notify_response(char *reply_buffer, size_t reply_buffer_len,
+ struct notify_ack_t **acks, size_t *nr_acks,
+ struct notify_timeout_t **timeouts, size_t *nr_timeouts);
+
+/**
+ * Free notify allocated buffer
+ *
+ * Release memory allocated by rados_decode_notify_response() call
+ *
+ * @param acks notify_ack_t struct (from rados_decode_notify_response())
+ * @param nr_acks ack count
+ * @param timeouts notify_timeout_t struct (from rados_decode_notify_response())
+ */
+CEPH_RADOS_API void rados_free_notify_response(struct notify_ack_t *acks, size_t nr_acks,
+ struct notify_timeout_t *timeouts);
+
+/**
+ * Acknolwedge receipt of a notify
+ *
+ * @param io the pool the object is in
+ * @param o the name of the object
+ * @param notify_id the notify_id we got on the watchcb2_t callback
+ * @param cookie the watcher handle
+ * @param buf payload to return to notifier (optional)
+ * @param buf_len payload length
+ * @returns 0 on success
+ */
+CEPH_RADOS_API int rados_notify_ack(rados_ioctx_t io, const char *o,
+ uint64_t notify_id, uint64_t cookie,
+ const char *buf, int buf_len);
+
+/**
+ * Flush watch/notify callbacks
+ *
+ * This call will block until all pending watch/notify callbacks have
+ * been executed and the queue is empty. It should usually be called
+ * after shutting down any watches before shutting down the ioctx or
+ * librados to ensure that any callbacks do not misuse the ioctx (for
+ * example by calling rados_notify_ack after the ioctx has been
+ * destroyed).
+ *
+ * @param cluster the cluster handle
+ */
+CEPH_RADOS_API int rados_watch_flush(rados_t cluster);
+/**
+ * Flush watch/notify callbacks
+ *
+ * This call will be nonblock, and the completion will be called
+ * until all pending watch/notify callbacks have been executed and
+ * the queue is empty. It should usually be called after shutting
+ * down any watches before shutting down the ioctx or
+ * librados to ensure that any callbacks do not misuse the ioctx (for
+ * example by calling rados_notify_ack after the ioctx has been
+ * destroyed).
+ *
+ * @param cluster the cluster handle
+ * @param completion what to do when operation has been attempted
+ */
+CEPH_RADOS_API int rados_aio_watch_flush(rados_t cluster, rados_completion_t completion);
+
+/** @} Watch/Notify */
+
+/**
+ * Pin an object in the cache tier
+ *
+ * When an object is pinned in the cache tier, it stays in the cache
+ * tier, and won't be flushed out.
+ *
+ * @param io the pool the object is in
+ * @param o the object id
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_cache_pin(rados_ioctx_t io, const char *o);
+
+/**
+ * Unpin an object in the cache tier
+ *
+ * After an object is unpinned in the cache tier, it can be flushed out
+ *
+ * @param io the pool the object is in
+ * @param o the object id
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_cache_unpin(rados_ioctx_t io, const char *o);
+
+/**
+ * @name Hints
+ *
+ * @{
+ */
+
+/**
+ * Set allocation hint for an object
+ *
+ * This is an advisory operation, it will always succeed (as if it was
+ * submitted with a LIBRADOS_OP_FLAG_FAILOK flag set) and is not
+ * guaranteed to do anything on the backend.
+ *
+ * @param io the pool the object is in
+ * @param o the name of the object
+ * @param expected_object_size expected size of the object, in bytes
+ * @param expected_write_size expected size of writes to the object, in bytes
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_set_alloc_hint(rados_ioctx_t io, const char *o,
+ uint64_t expected_object_size,
+ uint64_t expected_write_size);
+
+/**
+ * Set allocation hint for an object
+ *
+ * This is an advisory operation, it will always succeed (as if it was
+ * submitted with a LIBRADOS_OP_FLAG_FAILOK flag set) and is not
+ * guaranteed to do anything on the backend.
+ *
+ * @param io the pool the object is in
+ * @param o the name of the object
+ * @param expected_object_size expected size of the object, in bytes
+ * @param expected_write_size expected size of writes to the object, in bytes
+ * @param flags hints about future IO patterns
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_set_alloc_hint2(rados_ioctx_t io, const char *o,
+ uint64_t expected_object_size,
+ uint64_t expected_write_size,
+ uint32_t flags);
+
+/** @} Hints */
+
+/**
+ * @name Object Operations
+ *
+ * A single rados operation can do multiple operations on one object
+ * atomically. The whole operation will succeed or fail, and no partial
+ * results will be visible.
+ *
+ * Operations may be either reads, which can return data, or writes,
+ * which cannot. The effects of writes are applied and visible all at
+ * once, so an operation that sets an xattr and then checks its value
+ * will not see the updated value.
+ *
+ * @{
+ */
+
+/**
+ * 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.
+ *
+ * @note the ownership of a write operartion is passed to the function
+ * performing the operation, so the same instance of @c rados_write_op_t
+ * cannot be used again after being performed.
+ *
+ * @returns non-NULL on success, NULL on memory allocation error.
+ */
+CEPH_RADOS_API rados_write_op_t rados_create_write_op(void);
+
+/**
+ * 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
+ */
+CEPH_RADOS_API void rados_release_write_op(rados_write_op_t write_op);
+
+/**
+ * Set flags for the last operation added to this write_op.
+ * At least one op must have been added to the write_op.
+ * @param write_op operation to add this action to
+ * @param flags see librados.h constants beginning with LIBRADOS_OP_FLAG
+ */
+CEPH_RADOS_API void rados_write_op_set_flags(rados_write_op_t write_op,
+ int flags);
+
+/**
+ * Ensure that the object exists before writing
+ * @param write_op operation to add this action to
+ */
+CEPH_RADOS_API void rados_write_op_assert_exists(rados_write_op_t write_op);
+
+/**
+ * Ensure that the object exists and that its internal version
+ * number is equal to "ver" before writing. "ver" should be a
+ * version number previously obtained with rados_get_last_version().
+ * - If the object's version is greater than the asserted version
+ * then rados_write_op_operate will return -ERANGE instead of
+ * executing the op.
+ * - If the object's version is less than the asserted version
+ * then rados_write_op_operate will return -EOVERFLOW instead
+ * of executing the op.
+ * @param write_op operation to add this action to
+ * @param ver object version number
+ */
+CEPH_RADOS_API void rados_write_op_assert_version(rados_write_op_t write_op, uint64_t ver);
+
+/**
+ * Ensure that given object range (extent) satisfies comparison.
+ *
+ * @param write_op operation to add this action to
+ * @param cmp_buf buffer containing bytes to be compared with object contents
+ * @param cmp_len length to compare and size of @c cmp_buf in bytes
+ * @param off object byte offset at which to start the comparison
+ * @param prval returned result of comparison, 0 on success, negative error code
+ * on failure, (-MAX_ERRNO - mismatch_off) on mismatch
+ */
+CEPH_RADOS_API void rados_write_op_cmpext(rados_write_op_t write_op,
+ const char *cmp_buf,
+ size_t cmp_len,
+ uint64_t off,
+ int *prval);
+
+/**
+ * Ensure that given xattr satisfies comparison.
+ * If the comparison is not satisfied, the return code of the
+ * operation will be -ECANCELED
+ * @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
+ */
+CEPH_RADOS_API 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);
+
+/**
+ * Ensure that the an omap value satisfies a comparison,
+ * with the supplied value on the right hand side (i.e.
+ * for OP_LT, the comparison is actual_value < value.
+ *
+ * @param write_op operation to add this action to
+ * @param key which omap value to compare
+ * @param comparison_operator one of LIBRADOS_CMPXATTR_OP_EQ,
+ LIBRADOS_CMPXATTR_OP_LT, or LIBRADOS_CMPXATTR_OP_GT
+ * @param val value to compare with
+ * @param val_len length of value in bytes
+ * @param prval where to store the return value from this action
+ */
+CEPH_RADOS_API void rados_write_op_omap_cmp(rados_write_op_t write_op,
+ const char *key,
+ uint8_t comparison_operator,
+ const char *val,
+ size_t val_len,
+ int *prval);
+
+/**
+ * Ensure that the an omap value satisfies a comparison,
+ * with the supplied value on the right hand side (i.e.
+ * for OP_LT, the comparison is actual_value < value.
+ *
+ * @param write_op operation to add this action to
+ * @param key which omap value to compare
+ * @param comparison_operator one of LIBRADOS_CMPXATTR_OP_EQ,
+ LIBRADOS_CMPXATTR_OP_LT, or LIBRADOS_CMPXATTR_OP_GT
+ * @param val value to compare with
+ * @param key_len length of key in bytes
+ * @param val_len length of value in bytes
+ * @param prval where to store the return value from this action
+ */
+CEPH_RADOS_API void rados_write_op_omap_cmp2(rados_write_op_t write_op,
+ const char *key,
+ uint8_t comparison_operator,
+ const char *val,
+ size_t key_len,
+ size_t val_len,
+ int *prval);
+
+/**
+ * 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
+ */
+CEPH_RADOS_API 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
+ */
+CEPH_RADOS_API 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.
+ * @param category category string (DEPRECATED, HAS NO EFFECT)
+ */
+CEPH_RADOS_API 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
+ */
+CEPH_RADOS_API 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
+ */
+CEPH_RADOS_API void rados_write_op_write_full(rados_write_op_t write_op,
+ const char *buffer,
+ size_t len);
+
+/**
+ * Write the same buffer multiple times
+ * @param write_op operation to add this action to
+ * @param buffer bytes to write
+ * @param data_len length of buffer
+ * @param write_len total number of bytes to write, as a multiple of @c data_len
+ * @param offset offset to write to
+ */
+CEPH_RADOS_API void rados_write_op_writesame(rados_write_op_t write_op,
+ const char *buffer,
+ size_t data_len,
+ size_t write_len,
+ uint64_t offset);
+
+/**
+ * Append to end of object.
+ * @param write_op operation to add this action to
+ * @param buffer bytes to write
+ * @param len length of buffer
+ */
+CEPH_RADOS_API 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
+ */
+CEPH_RADOS_API void rados_write_op_remove(rados_write_op_t write_op);
+
+/**
+ * Truncate an object
+ * @param write_op operation to add this action to
+ * @param offset Offset to truncate to
+ */
+CEPH_RADOS_API 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
+ * @param offset Offset to zero
+ * @param len length to zero
+ */
+CEPH_RADOS_API void rados_write_op_zero(rados_write_op_t write_op,
+ uint64_t offset,
+ uint64_t len);
+
+/**
+ * Execute an OSD class method on an object
+ * See rados_exec() for general description.
+ *
+ * @param write_op operation to add this action to
+ * @param cls the name of the class
+ * @param method the name of the method
+ * @param in_buf where to find input
+ * @param in_len length of in_buf in bytes
+ * @param prval where to store the return value from the method
+ */
+CEPH_RADOS_API void rados_write_op_exec(rados_write_op_t write_op,
+ const char *cls,
+ const char *method,
+ const char *in_buf,
+ size_t in_len,
+ int *prval);
+
+/**
+ * Set key/value pairs on an object
+ *
+ * @param write_op operation to add this action to
+ * @param keys array of null-terminated char arrays representing keys to set
+ * @param vals array of pointers to values to set
+ * @param lens array of lengths corresponding to each value
+ * @param num number of key/value pairs to set
+ */
+CEPH_RADOS_API void rados_write_op_omap_set(rados_write_op_t write_op,
+ char const* const* keys,
+ char const* const* vals,
+ const size_t *lens,
+ size_t num);
+
+/**
+ * Set key/value pairs on an object
+ *
+ * @param write_op operation to add this action to
+ * @param keys array of null-terminated char arrays representing keys to set
+ * @param vals array of pointers to values to set
+ * @param key_lens array of lengths corresponding to each key
+ * @param val_lens array of lengths corresponding to each value
+ * @param num number of key/value pairs to set
+ */
+CEPH_RADOS_API void rados_write_op_omap_set2(rados_write_op_t write_op,
+ char const* const* keys,
+ char const* const* vals,
+ const size_t *key_lens,
+ const size_t *val_lens,
+ size_t num);
+
+/**
+ * Remove key/value pairs from an object
+ *
+ * @param write_op operation to add this action to
+ * @param keys array of null-terminated char arrays representing keys to remove
+ * @param keys_len number of key/value pairs to remove
+ */
+CEPH_RADOS_API void rados_write_op_omap_rm_keys(rados_write_op_t write_op,
+ char const* const* keys,
+ size_t keys_len);
+
+/**
+ * Remove key/value pairs from an object
+ *
+ * @param write_op operation to add this action to
+ * @param keys array of char arrays representing keys to remove
+ * @param key_lens array of size_t values representing length of each key
+ * @param keys_len number of key/value pairs to remove
+ */
+CEPH_RADOS_API void rados_write_op_omap_rm_keys2(rados_write_op_t write_op,
+ char const* const* keys,
+ const size_t* key_lens,
+ size_t keys_len);
+
+
+/**
+ * Remove key/value pairs from an object whose keys are in the range
+ * [key_begin, key_end)
+ *
+ * @param write_op operation to add this action to
+ * @param key_begin the lower bound of the key range to remove
+ * @param key_begin_len length of key_begin
+ * @param key_end the upper bound of the key range to remove
+ * @param key_end_len length of key_end
+ */
+CEPH_RADOS_API void rados_write_op_omap_rm_range2(rados_write_op_t write_op,
+ const char *key_begin,
+ size_t key_begin_len,
+ const char *key_end,
+ size_t key_end_len);
+
+/**
+ * Remove all key/value pairs from an object
+ *
+ * @param write_op operation to add this action to
+ */
+CEPH_RADOS_API void rados_write_op_omap_clear(rados_write_op_t write_op);
+
+/**
+ * Set allocation hint for an object
+ *
+ * @param write_op operation to add this action to
+ * @param expected_object_size expected size of the object, in bytes
+ * @param expected_write_size expected size of writes to the object, in bytes
+ */
+CEPH_RADOS_API void rados_write_op_set_alloc_hint(rados_write_op_t write_op,
+ uint64_t expected_object_size,
+ uint64_t expected_write_size);
+
+/**
+ * Set allocation hint for an object
+ *
+ * @param write_op operation to add this action to
+ * @param expected_object_size expected size of the object, in bytes
+ * @param expected_write_size expected size of writes to the object, in bytes
+ * @param flags hints about future IO patterns
+ */
+CEPH_RADOS_API void rados_write_op_set_alloc_hint2(rados_write_op_t write_op,
+ uint64_t expected_object_size,
+ uint64_t expected_write_size,
+ uint32_t flags);
+
+/**
+ * Perform a write operation synchronously
+ * @param write_op operation to perform
+ * @param io the ioctx that the object is in
+ * @param oid the object id
+ * @param mtime the time to set the mtime to, NULL for the current time
+ * @param flags flags to apply to the entire operation (LIBRADOS_OPERATION_*)
+ */
+CEPH_RADOS_API int rados_write_op_operate(rados_write_op_t write_op,
+ rados_ioctx_t io,
+ const char *oid,
+ time_t *mtime,
+ int flags);
+/**
+ * Perform a write operation synchronously
+ * @param write_op operation to perform
+ * @param io the ioctx that the object is in
+ * @param oid the object id
+ * @param mtime the time to set the mtime to, NULL for the current time
+ * @param flags flags to apply to the entire operation (LIBRADOS_OPERATION_*)
+ */
+
+CEPH_RADOS_API int rados_write_op_operate2(rados_write_op_t write_op,
+ rados_ioctx_t io,
+ const char *oid,
+ struct timespec *mtime,
+ int flags);
+
+/**
+ * Perform a write operation asynchronously
+ * @param write_op operation to perform
+ * @param io the ioctx that the object is in
+ * @param completion what to do when operation has been attempted
+ * @param oid the object id
+ * @param mtime the time to set the mtime to, NULL for the current time
+ * @param flags flags to apply to the entire operation (LIBRADOS_OPERATION_*)
+ */
+CEPH_RADOS_API 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,
+ int flags);
+
+/**
+ * Perform a write operation asynchronously
+ * @param write_op operation to perform
+ * @param io the ioctx that the object is in
+ * @param completion what to do when operation has been attempted
+ * @param oid the object id
+ * @param mtime the time to set the mtime to, NULL for the current time
+ * @param flags flags to apply to the entire operation (LIBRADOS_OPERATION_*)
+ */
+CEPH_RADOS_API int rados_aio_write_op_operate2(rados_write_op_t write_op,
+ rados_ioctx_t io,
+ rados_completion_t completion,
+ const char *oid,
+ struct timespec *mtime,
+ int flags);
+
+/**
+ * Create a new rados_read_op_t read operation. This will store all
+ * actions to be performed atomically. You must call
+ * rados_release_read_op when you are finished with it (after it
+ * completes, or you decide not to send it in the first place).
+ *
+ * @note the ownership of a read operartion is passed to the function
+ * performing the operation, so the same instance of @c rados_read_op_t
+ * cannot be used again after being performed.
+ *
+ * @returns non-NULL on success, NULL on memory allocation error.
+ */
+CEPH_RADOS_API rados_read_op_t rados_create_read_op(void);
+
+/**
+ * Free a rados_read_op_t, must be called when you're done with it.
+ * @param read_op operation to deallocate, created with rados_create_read_op
+ */
+CEPH_RADOS_API void rados_release_read_op(rados_read_op_t read_op);
+
+/**
+ * Set flags for the last operation added to this read_op.
+ * At least one op must have been added to the read_op.
+ * @param read_op operation to add this action to
+ * @param flags see librados.h constants beginning with LIBRADOS_OP_FLAG
+ */
+CEPH_RADOS_API void rados_read_op_set_flags(rados_read_op_t read_op, int flags);
+
+/**
+ * Ensure that the object exists before reading
+ * @param read_op operation to add this action to
+ */
+CEPH_RADOS_API void rados_read_op_assert_exists(rados_read_op_t read_op);
+
+/**
+ * Ensure that the object exists and that its internal version
+ * number is equal to "ver" before reading. "ver" should be a
+ * version number previously obtained with rados_get_last_version().
+ * - If the object's version is greater than the asserted version
+ * then rados_read_op_operate will return -ERANGE instead of
+ * executing the op.
+ * - If the object's version is less than the asserted version
+ * then rados_read_op_operate will return -EOVERFLOW instead
+ * of executing the op.
+ * @param read_op operation to add this action to
+ * @param ver object version number
+ */
+CEPH_RADOS_API void rados_read_op_assert_version(rados_read_op_t read_op, uint64_t ver);
+
+/**
+ * Ensure that given object range (extent) satisfies comparison.
+ *
+ * @param read_op operation to add this action to
+ * @param cmp_buf buffer containing bytes to be compared with object contents
+ * @param cmp_len length to compare and size of @c cmp_buf in bytes
+ * @param off object byte offset at which to start the comparison
+ * @param prval returned result of comparison, 0 on success, negative error code
+ * on failure, (-MAX_ERRNO - mismatch_off) on mismatch
+ */
+CEPH_RADOS_API void rados_read_op_cmpext(rados_read_op_t read_op,
+ const char *cmp_buf,
+ size_t cmp_len,
+ uint64_t off,
+ int *prval);
+
+/**
+ * Ensure that the an xattr satisfies a comparison
+ * If the comparison is not satisfied, the return code of the
+ * operation will be -ECANCELED
+ * @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
+ */
+CEPH_RADOS_API 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
+ */
+CEPH_RADOS_API void rados_read_op_getxattrs(rados_read_op_t read_op,
+ rados_xattrs_iter_t *iter,
+ int *prval);
+
+/**
+ * Ensure that the an omap value satisfies a comparison,
+ * with the supplied value on the right hand side (i.e.
+ * for OP_LT, the comparison is actual_value < value.
+ *
+ * @param read_op operation to add this action to
+ * @param key which omap value to compare
+ * @param comparison_operator one of LIBRADOS_CMPXATTR_OP_EQ,
+ LIBRADOS_CMPXATTR_OP_LT, or LIBRADOS_CMPXATTR_OP_GT
+ * @param val value to compare with
+ * @param val_len length of value in bytes
+ * @param prval where to store the return value from this action
+ */
+CEPH_RADOS_API void rados_read_op_omap_cmp(rados_read_op_t read_op,
+ const char *key,
+ uint8_t comparison_operator,
+ const char *val,
+ size_t val_len,
+ int *prval);
+
+/**
+ * Ensure that the an omap value satisfies a comparison,
+ * with the supplied value on the right hand side (i.e.
+ * for OP_LT, the comparison is actual_value < value.
+ *
+ * @param read_op operation to add this action to
+ * @param key which omap value to compare
+ * @param comparison_operator one of LIBRADOS_CMPXATTR_OP_EQ,
+ LIBRADOS_CMPXATTR_OP_LT, or LIBRADOS_CMPXATTR_OP_GT
+ * @param val value to compare with
+ * @param key_len length of key in bytes
+ * @param val_len length of value in bytes
+ * @param prval where to store the return value from this action
+ */
+CEPH_RADOS_API void rados_read_op_omap_cmp2(rados_read_op_t read_op,
+ const char *key,
+ uint8_t comparison_operator,
+ const char *val,
+ size_t key_len,
+ size_t val_len,
+ int *prval);
+
+/**
+ * Get object size and mtime
+ * @param read_op operation to add this action to
+ * @param psize where to store object size
+ * @param pmtime where to store modification time
+ * @param prval where to store the return value of this action
+ */
+CEPH_RADOS_API void rados_read_op_stat(rados_read_op_t read_op,
+ uint64_t *psize,
+ time_t *pmtime,
+ int *prval);
+
+CEPH_RADOS_API void rados_read_op_stat2(rados_read_op_t read_op,
+ uint64_t *psize,
+ struct timespec *pmtime,
+ int *prval);
+/**
+ * Read bytes from offset into buffer.
+ *
+ * prlen will be filled with the number of bytes read if successful.
+ * A short read can only occur if the read reaches the end of the
+ * object.
+ *
+ * @param read_op operation to add this action to
+ * @param offset offset to read from
+ * @param len length of buffer
+ * @param buffer where to put the data
+ * @param bytes_read where to store the number of bytes read by this action
+ * @param prval where to store the return value of this action
+ */
+CEPH_RADOS_API void rados_read_op_read(rados_read_op_t read_op,
+ uint64_t offset,
+ size_t len,
+ char *buffer,
+ size_t *bytes_read,
+ int *prval);
+
+/**
+ * Compute checksum from object data
+ *
+ * @param read_op operation to add this action to
+ * @param type the checksum algorithm to utilize
+ * @param init_value the init value for the algorithm
+ * @param init_value_len the length of the init value
+ * @param offset the offset to start checksumming in the object
+ * @param len the number of bytes to checksum
+ * @param chunk_size optional length-aligned chunk size for checksums
+ * @param pchecksum where to store the checksum result for this action
+ * @param checksum_len the number of bytes available for the result
+ * @param prval where to store the return value for this action
+ */
+CEPH_RADOS_API void rados_read_op_checksum(rados_read_op_t read_op,
+ rados_checksum_type_t type,
+ const char *init_value,
+ size_t init_value_len,
+ uint64_t offset, size_t len,
+ size_t chunk_size, char *pchecksum,
+ size_t checksum_len, int *prval);
+
+/**
+ * Execute an OSD class method on an object
+ * See rados_exec() for general description.
+ *
+ * The output buffer is allocated on the heap; the caller is
+ * expected to release that memory with rados_buffer_free(). The
+ * buffer and length pointers can all be NULL, in which case they are
+ * not filled in.
+ *
+ * @param read_op operation to add this action to
+ * @param cls the name of the class
+ * @param method the name of the method
+ * @param in_buf where to find input
+ * @param in_len length of in_buf in bytes
+ * @param out_buf where to put librados-allocated output buffer
+ * @param out_len length of out_buf in bytes
+ * @param prval where to store the return value from the method
+ */
+CEPH_RADOS_API void rados_read_op_exec(rados_read_op_t read_op,
+ const char *cls,
+ const char *method,
+ const char *in_buf,
+ size_t in_len,
+ char **out_buf,
+ size_t *out_len,
+ int *prval);
+
+/**
+ * Execute an OSD class method on an object
+ * See rados_exec() for general description.
+ *
+ * If the output buffer is too small, prval will
+ * be set to -ERANGE and used_len will be 0.
+ *
+ * @param read_op operation to add this action to
+ * @param cls the name of the class
+ * @param method the name of the method
+ * @param in_buf where to find input
+ * @param in_len length of in_buf in bytes
+ * @param out_buf user-provided buffer to read into
+ * @param out_len length of out_buf in bytes
+ * @param used_len where to store the number of bytes read into out_buf
+ * @param prval where to store the return value from the method
+ */
+CEPH_RADOS_API void rados_read_op_exec_user_buf(rados_read_op_t read_op,
+ const char *cls,
+ const char *method,
+ const char *in_buf,
+ size_t in_len,
+ char *out_buf,
+ size_t out_len,
+ size_t *used_len,
+ int *prval);
+
+/**
+ * Start iterating over key/value pairs on an object.
+ *
+ * They will be returned sorted by key.
+ *
+ * @param read_op operation to add this action to
+ * @param start_after list keys starting after start_after
+ * @param filter_prefix list only keys beginning with filter_prefix
+ * @param max_return list no more than max_return key/value pairs
+ * @param iter where to store the iterator
+ * @param prval where to store the return value from this action
+ */
+CEPH_RADOS_API void rados_read_op_omap_get_vals(rados_read_op_t read_op,
+ const char *start_after,
+ const char *filter_prefix,
+ uint64_t max_return,
+ rados_omap_iter_t *iter,
+ int *prval)
+ __attribute__((deprecated)); /* use v2 below */
+
+/**
+ * Start iterating over key/value pairs on an object.
+ *
+ * They will be returned sorted by key.
+ *
+ * @param read_op operation to add this action to
+ * @param start_after list keys starting after start_after
+ * @param filter_prefix list only keys beginning with filter_prefix
+ * @param max_return list no more than max_return key/value pairs
+ * @param iter where to store the iterator
+ * @param pmore flag indicating whether there are more keys to fetch
+ * @param prval where to store the return value from this action
+ */
+CEPH_RADOS_API void rados_read_op_omap_get_vals2(rados_read_op_t read_op,
+ const char *start_after,
+ const char *filter_prefix,
+ uint64_t max_return,
+ rados_omap_iter_t *iter,
+ unsigned char *pmore,
+ int *prval);
+
+/**
+ * Start iterating over keys on an object.
+ *
+ * They will be returned sorted by key, and the iterator
+ * will fill in NULL for all values if specified.
+ *
+ * @param read_op operation to add this action to
+ * @param start_after list keys starting after start_after
+ * @param max_return list no more than max_return keys
+ * @param iter where to store the iterator
+ * @param prval where to store the return value from this action
+ */
+CEPH_RADOS_API void rados_read_op_omap_get_keys(rados_read_op_t read_op,
+ const char *start_after,
+ uint64_t max_return,
+ rados_omap_iter_t *iter,
+ int *prval)
+ __attribute__((deprecated)); /* use v2 below */
+
+/**
+ * Start iterating over keys on an object.
+ *
+ * They will be returned sorted by key, and the iterator
+ * will fill in NULL for all values if specified.
+ *
+ * @param read_op operation to add this action to
+ * @param start_after list keys starting after start_after
+ * @param max_return list no more than max_return keys
+ * @param iter where to store the iterator
+ * @param pmore flag indicating whether there are more keys to fetch
+ * @param prval where to store the return value from this action
+ */
+CEPH_RADOS_API void rados_read_op_omap_get_keys2(rados_read_op_t read_op,
+ const char *start_after,
+ uint64_t max_return,
+ rados_omap_iter_t *iter,
+ unsigned char *pmore,
+ int *prval);
+
+/**
+ * Start iterating over specific key/value pairs
+ *
+ * They will be returned sorted by key.
+ *
+ * @param read_op operation to add this action to
+ * @param keys array of pointers to null-terminated keys to get
+ * @param keys_len the number of strings in keys
+ * @param iter where to store the iterator
+ * @param prval where to store the return value from this action
+ */
+CEPH_RADOS_API void rados_read_op_omap_get_vals_by_keys(rados_read_op_t read_op,
+ char const* const* keys,
+ size_t keys_len,
+ rados_omap_iter_t *iter,
+ int *prval);
+
+/**
+ * Start iterating over specific key/value pairs
+ *
+ * They will be returned sorted by key.
+ *
+ * @param read_op operation to add this action to
+ * @param keys array of pointers to keys to get
+ * @param num_keys the number of strings in keys
+ * @param key_lens array of size_t's describing each key len (in bytes)
+ * @param iter where to store the iterator
+ * @param prval where to store the return value from this action
+ */
+CEPH_RADOS_API void rados_read_op_omap_get_vals_by_keys2(rados_read_op_t read_op,
+ char const* const* keys,
+ size_t num_keys,
+ const size_t* key_lens,
+ rados_omap_iter_t *iter,
+ int *prval);
+
+/**
+ * Perform a read operation synchronously
+ * @param read_op operation to perform
+ * @param io the ioctx that the object is in
+ * @param oid the object id
+ * @param flags flags to apply to the entire operation (LIBRADOS_OPERATION_*)
+ */
+CEPH_RADOS_API int rados_read_op_operate(rados_read_op_t read_op,
+ rados_ioctx_t io,
+ const char *oid,
+ int flags);
+
+/**
+ * Perform a read operation asynchronously
+ * @param read_op operation to perform
+ * @param io the ioctx that the object is in
+ * @param completion what to do when operation has been attempted
+ * @param oid the object id
+ * @param flags flags to apply to the entire operation (LIBRADOS_OPERATION_*)
+ */
+CEPH_RADOS_API int rados_aio_read_op_operate(rados_read_op_t read_op,
+ rados_ioctx_t io,
+ rados_completion_t completion,
+ const char *oid,
+ int flags);
+
+/** @} Object Operations */
+
+/**
+ * Take an exclusive lock on an object.
+ *
+ * @param io the context to operate in
+ * @param oid the name of the object
+ * @param name the name of the lock
+ * @param cookie user-defined identifier for this instance of the lock
+ * @param desc user-defined lock description
+ * @param duration the duration of the lock. Set to NULL for infinite duration.
+ * @param flags lock flags
+ * @returns 0 on success, negative error code on failure
+ * @returns -EBUSY if the lock is already held by another (client, cookie) pair
+ * @returns -EEXIST if the lock is already held by the same (client, cookie) pair
+ */
+CEPH_RADOS_API int rados_lock_exclusive(rados_ioctx_t io, const char * oid,
+ const char * name, const char * cookie,
+ const char * desc,
+ struct timeval * duration,
+ uint8_t flags);
+
+/**
+ * Take a shared lock on an object.
+ *
+ * @param io the context to operate in
+ * @param o the name of the object
+ * @param name the name of the lock
+ * @param cookie user-defined identifier for this instance of the lock
+ * @param tag The tag of the lock
+ * @param desc user-defined lock description
+ * @param duration the duration of the lock. Set to NULL for infinite duration.
+ * @param flags lock flags
+ * @returns 0 on success, negative error code on failure
+ * @returns -EBUSY if the lock is already held by another (client, cookie) pair
+ * @returns -EEXIST if the lock is already held by the same (client, cookie) pair
+ */
+CEPH_RADOS_API int rados_lock_shared(rados_ioctx_t io, const char * o,
+ const char * name, const char * cookie,
+ const char * tag, const char * desc,
+ struct timeval * duration, uint8_t flags);
+
+/**
+ * Release a shared or exclusive lock on an object.
+ *
+ * @param io the context to operate in
+ * @param o the name of the object
+ * @param name the name of the lock
+ * @param cookie user-defined identifier for the instance of the lock
+ * @returns 0 on success, negative error code on failure
+ * @returns -ENOENT if the lock is not held by the specified (client, cookie) pair
+ */
+CEPH_RADOS_API int rados_unlock(rados_ioctx_t io, const char *o,
+ const char *name, const char *cookie);
+
+/**
+ * Asynchronous release a shared or exclusive lock on an object.
+ *
+ * @param io the context to operate in
+ * @param o the name of the object
+ * @param name the name of the lock
+ * @param cookie user-defined identifier for the instance of the lock
+ * @param completion what to do when operation has been attempted
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_aio_unlock(rados_ioctx_t io, const char *o,
+ const char *name, const char *cookie,
+ rados_completion_t completion);
+
+/**
+ * List clients that have locked the named object lock and information about
+ * the lock.
+ *
+ * The number of bytes required in each buffer is put in the
+ * corresponding size out parameter. If any of the provided buffers
+ * are too short, -ERANGE is returned after these sizes are filled in.
+ *
+ * @param io the context to operate in
+ * @param o the name of the object
+ * @param name the name of the lock
+ * @param exclusive where to store whether the lock is exclusive (1) or shared (0)
+ * @param tag where to store the tag associated with the object lock
+ * @param tag_len number of bytes in tag buffer
+ * @param clients buffer in which locker clients are stored, separated by '\0'
+ * @param clients_len number of bytes in the clients buffer
+ * @param cookies buffer in which locker cookies are stored, separated by '\0'
+ * @param cookies_len number of bytes in the cookies buffer
+ * @param addrs buffer in which locker addresses are stored, separated by '\0'
+ * @param addrs_len number of bytes in the clients buffer
+ * @returns number of lockers on success, negative error code on failure
+ * @returns -ERANGE if any of the buffers are too short
+ */
+CEPH_RADOS_API ssize_t rados_list_lockers(rados_ioctx_t io, const char *o,
+ const char *name, int *exclusive,
+ char *tag, size_t *tag_len,
+ char *clients, size_t *clients_len,
+ char *cookies, size_t *cookies_len,
+ char *addrs, size_t *addrs_len);
+
+/**
+ * Releases a shared or exclusive lock on an object, which was taken by the
+ * specified client.
+ *
+ * @param io the context to operate in
+ * @param o the name of the object
+ * @param name the name of the lock
+ * @param client the client currently holding the lock
+ * @param cookie user-defined identifier for the instance of the lock
+ * @returns 0 on success, negative error code on failure
+ * @returns -ENOENT if the lock is not held by the specified (client, cookie) pair
+ * @returns -EINVAL if the client cannot be parsed
+ */
+CEPH_RADOS_API int rados_break_lock(rados_ioctx_t io, const char *o,
+ const char *name, const char *client,
+ const char *cookie);
+
+/**
+ * Blocklists the specified client from the OSDs
+ *
+ * @param cluster cluster handle
+ * @param client_address client address
+ * @param expire_seconds number of seconds to blocklist (0 for default)
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_blocklist_add(rados_t cluster,
+ char *client_address,
+ uint32_t expire_seconds);
+CEPH_RADOS_API int rados_blacklist_add(rados_t cluster,
+ char *client_address,
+ uint32_t expire_seconds)
+ __attribute__((deprecated));
+
+/**
+ * Gets addresses of the RADOS session, suitable for blocklisting.
+ *
+ * @param cluster cluster handle
+ * @param addrs the output string.
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_getaddrs(rados_t cluster, char** addrs);
+
+CEPH_RADOS_API void rados_set_osdmap_full_try(rados_ioctx_t io)
+ __attribute__((deprecated));
+
+CEPH_RADOS_API void rados_unset_osdmap_full_try(rados_ioctx_t io)
+ __attribute__((deprecated));
+
+CEPH_RADOS_API void rados_set_pool_full_try(rados_ioctx_t io);
+
+CEPH_RADOS_API void rados_unset_pool_full_try(rados_ioctx_t io);
+
+/**
+ * Enable an application on a pool
+ *
+ * @param io pool ioctx
+ * @param app_name application name
+ * @param force 0 if only single application per pool
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_application_enable(rados_ioctx_t io,
+ const char *app_name, int force);
+
+/**
+ * List all enabled applications
+ *
+ * If the provided buffer is too short, the required length is filled in and
+ * -ERANGE is returned. Otherwise, the buffers are filled with the application
+ * names, with a '\0' after each.
+ *
+ * @param io pool ioctx
+ * @param values buffer in which to store application names
+ * @param values_len number of bytes in values buffer
+ * @returns 0 on success, negative error code on failure
+ * @returns -ERANGE if either buffer is too short
+ */
+CEPH_RADOS_API int rados_application_list(rados_ioctx_t io, char *values,
+ size_t *values_len);
+
+/**
+ * Get application metadata value from pool
+ *
+ * @param io pool ioctx
+ * @param app_name application name
+ * @param key metadata key
+ * @param value result buffer
+ * @param value_len maximum len of value
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_application_metadata_get(rados_ioctx_t io,
+ const char *app_name,
+ const char *key, char *value,
+ size_t *value_len);
+
+/**
+ * Set application metadata on a pool
+ *
+ * @param io pool ioctx
+ * @param app_name application name
+ * @param key metadata key
+ * @param value metadata key
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_application_metadata_set(rados_ioctx_t io,
+ const char *app_name,
+ const char *key,
+ const char *value);
+
+/**
+ * Remove application metadata from a pool
+ *
+ * @param io pool ioctx
+ * @param app_name application name
+ * @param key metadata key
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_application_metadata_remove(rados_ioctx_t io,
+ const char *app_name,
+ const char *key);
+
+/**
+ * List all metadata key/value pairs associated with an application.
+ *
+ * This iterates over all metadata, key_len and val_len are filled in
+ * with the number of bytes put into the keys and values buffers.
+ *
+ * If the provided buffers are too short, the required lengths are filled
+ * in and -ERANGE is returned. Otherwise, the buffers are filled with
+ * the keys and values of the metadata, with a '\0' after each.
+ *
+ * @param io pool ioctx
+ * @param app_name application name
+ * @param keys buffer in which to store key names
+ * @param key_len number of bytes in keys buffer
+ * @param values buffer in which to store values
+ * @param vals_len number of bytes in values buffer
+ * @returns 0 on success, negative error code on failure
+ * @returns -ERANGE if either buffer is too short
+ */
+CEPH_RADOS_API int rados_application_metadata_list(rados_ioctx_t io,
+ const char *app_name,
+ char *keys, size_t *key_len,
+ char *values,
+ size_t *vals_len);
+
+/**
+ * @name Mon/OSD/PG Commands
+ *
+ * These interfaces send commands relating to the monitor, OSD, or PGs.
+ *
+ * @{
+ */
+
+/**
+ * Send monitor command.
+ *
+ * @note Takes command string in carefully-formatted JSON; must match
+ * defined commands, types, etc.
+ *
+ * The result buffers are allocated on the heap; the caller is
+ * expected to release that memory with rados_buffer_free(). The
+ * buffer and length pointers can all be NULL, in which case they are
+ * not filled in.
+ *
+ * @param cluster cluster handle
+ * @param cmd an array of char *'s representing the command
+ * @param cmdlen count of valid entries in cmd
+ * @param inbuf any bulk input data (crush map, etc.)
+ * @param inbuflen input buffer length
+ * @param outbuf double pointer to output buffer
+ * @param outbuflen pointer to output buffer length
+ * @param outs double pointer to status string
+ * @param outslen pointer to status string length
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_mon_command(rados_t cluster, const char **cmd,
+ size_t cmdlen, const char *inbuf,
+ size_t inbuflen, char **outbuf,
+ size_t *outbuflen, char **outs,
+ size_t *outslen);
+
+/**
+ * Send ceph-mgr command.
+ *
+ * @note Takes command string in carefully-formatted JSON; must match
+ * defined commands, types, etc.
+ *
+ * The result buffers are allocated on the heap; the caller is
+ * expected to release that memory with rados_buffer_free(). The
+ * buffer and length pointers can all be NULL, in which case they are
+ * not filled in.
+ *
+ * @param cluster cluster handle
+ * @param cmd an array of char *'s representing the command
+ * @param cmdlen count of valid entries in cmd
+ * @param inbuf any bulk input data (crush map, etc.)
+ * @param inbuflen input buffer length
+ * @param outbuf double pointer to output buffer
+ * @param outbuflen pointer to output buffer length
+ * @param outs double pointer to status string
+ * @param outslen pointer to status string length
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_mgr_command(rados_t cluster, const char **cmd,
+ size_t cmdlen, const char *inbuf,
+ size_t inbuflen, char **outbuf,
+ size_t *outbuflen, char **outs,
+ size_t *outslen);
+
+/**
+ * Send ceph-mgr tell command.
+ *
+ * @note Takes command string in carefully-formatted JSON; must match
+ * defined commands, types, etc.
+ *
+ * The result buffers are allocated on the heap; the caller is
+ * expected to release that memory with rados_buffer_free(). The
+ * buffer and length pointers can all be NULL, in which case they are
+ * not filled in.
+ *
+ * @param cluster cluster handle
+ * @param name mgr name to target
+ * @param cmd an array of char *'s representing the command
+ * @param cmdlen count of valid entries in cmd
+ * @param inbuf any bulk input data (crush map, etc.)
+ * @param inbuflen input buffer length
+ * @param outbuf double pointer to output buffer
+ * @param outbuflen pointer to output buffer length
+ * @param outs double pointer to status string
+ * @param outslen pointer to status string length
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_mgr_command_target(
+ rados_t cluster,
+ const char *name,
+ const char **cmd,
+ size_t cmdlen, const char *inbuf,
+ size_t inbuflen, char **outbuf,
+ size_t *outbuflen, char **outs,
+ size_t *outslen);
+
+/**
+ * Send monitor command to a specific monitor.
+ *
+ * @note Takes command string in carefully-formatted JSON; must match
+ * defined commands, types, etc.
+ *
+ * The result buffers are allocated on the heap; the caller is
+ * expected to release that memory with rados_buffer_free(). The
+ * buffer and length pointers can all be NULL, in which case they are
+ * not filled in.
+ *
+ * @param cluster cluster handle
+ * @param name target monitor's name
+ * @param cmd an array of char *'s representing the command
+ * @param cmdlen count of valid entries in cmd
+ * @param inbuf any bulk input data (crush map, etc.)
+ * @param inbuflen input buffer length
+ * @param outbuf double pointer to output buffer
+ * @param outbuflen pointer to output buffer length
+ * @param outs double pointer to status string
+ * @param outslen pointer to status string length
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_mon_command_target(rados_t cluster, const char *name,
+ const char **cmd, size_t cmdlen,
+ const char *inbuf, size_t inbuflen,
+ char **outbuf, size_t *outbuflen,
+ char **outs, size_t *outslen);
+
+/**
+ * free a rados-allocated buffer
+ *
+ * Release memory allocated by librados calls like rados_mon_command().
+ *
+ * @param buf buffer pointer
+ */
+CEPH_RADOS_API void rados_buffer_free(char *buf);
+
+CEPH_RADOS_API int rados_osd_command(rados_t cluster, int osdid,
+ const char **cmd, size_t cmdlen,
+ const char *inbuf, size_t inbuflen,
+ char **outbuf, size_t *outbuflen,
+ char **outs, size_t *outslen);
+
+CEPH_RADOS_API int rados_pg_command(rados_t cluster, const char *pgstr,
+ const char **cmd, size_t cmdlen,
+ const char *inbuf, size_t inbuflen,
+ char **outbuf, size_t *outbuflen,
+ char **outs, size_t *outslen);
+
+/*
+ * This is not a doxygen comment leadin, because doxygen breaks on
+ * a typedef with function params and returns, and I can't figure out
+ * how to fix it.
+ *
+ * Monitor cluster log
+ *
+ * Monitor events logged to the cluster log. The callback get each
+ * log entry both as a single formatted line and with each field in a
+ * separate arg.
+ *
+ * Calling with a cb argument of NULL will deregister any previously
+ * registered callback.
+ *
+ * @param cluster cluster handle
+ * @param level minimum log level (debug, info, warn|warning, err|error)
+ * @param cb callback to run for each log message. It MUST NOT block
+ * nor call back into librados.
+ * @param arg void argument to pass to cb
+ *
+ * @returns 0 on success, negative code on error
+ */
+typedef void (*rados_log_callback_t)(void *arg,
+ const char *line,
+ const char *who,
+ uint64_t sec, uint64_t nsec,
+ uint64_t seq, const char *level,
+ const char *msg);
+
+/*
+ * This is not a doxygen comment leadin, because doxygen breaks on
+ * a typedef with function params and returns, and I can't figure out
+ * how to fix it.
+ *
+ * Monitor cluster log
+ *
+ * Monitor events logged to the cluster log. The callback get each
+ * log entry both as a single formatted line and with each field in a
+ * separate arg.
+ *
+ * Calling with a cb argument of NULL will deregister any previously
+ * registered callback.
+ *
+ * @param cluster cluster handle
+ * @param level minimum log level (debug, info, warn|warning, err|error)
+ * @param cb callback to run for each log message. It MUST NOT block
+ * nor call back into librados.
+ * @param arg void argument to pass to cb
+ *
+ * @returns 0 on success, negative code on error
+ */
+typedef void (*rados_log_callback2_t)(void *arg,
+ const char *line,
+ const char *channel,
+ const char *who,
+ const char *name,
+ uint64_t sec, uint64_t nsec,
+ uint64_t seq, const char *level,
+ const char *msg);
+
+CEPH_RADOS_API int rados_monitor_log(rados_t cluster, const char *level,
+ rados_log_callback_t cb, void *arg);
+CEPH_RADOS_API int rados_monitor_log2(rados_t cluster, const char *level,
+ rados_log_callback2_t cb, void *arg);
+
+
+/**
+ * register daemon instance for a service
+ *
+ * Register us as a daemon providing a particular service. We identify
+ * the service (e.g., 'rgw') and our instance name (e.g., 'rgw.$hostname').
+ * The metadata is a map of keys and values with arbitrary static metdata
+ * for this instance. The encoding is a series of NULL-terminated strings,
+ * alternating key names and values, terminating with an empty key name.
+ * For example, "foo\0bar\0this\0that\0\0" is the dict {foo=bar,this=that}.
+ *
+ * For the lifetime of the librados instance, regular beacons will be sent
+ * to the cluster to maintain our registration in the service map.
+ *
+ * @param cluster handle
+ * @param service service name
+ * @param daemon daemon instance name
+ * @param metadata_dict static daemon metadata dict
+ */
+CEPH_RADOS_API int rados_service_register(
+ rados_t cluster,
+ const char *service,
+ const char *daemon,
+ const char *metadata_dict);
+
+/**
+ * update daemon status
+ *
+ * Update our mutable status information in the service map.
+ *
+ * The status dict is encoded the same way the daemon metadata is encoded
+ * for rados_service_register. For example, "foo\0bar\0this\0that\0\0" is
+ * {foo=bar,this=that}.
+ *
+ * @param cluster rados cluster handle
+ * @param status_dict status dict
+ */
+CEPH_RADOS_API int rados_service_update_status(
+ rados_t cluster,
+ const char *status_dict);
+
+/** @} Mon/OSD/PG commands */
+
+/*
+ * These methods are no longer supported and return -ENOTSUP where possible.
+ */
+CEPH_RADOS_API int rados_objects_list_open(
+ rados_ioctx_t io,
+ rados_list_ctx_t *ctx) __attribute__((deprecated));
+CEPH_RADOS_API uint32_t rados_objects_list_get_pg_hash_position(
+ rados_list_ctx_t ctx) __attribute__((deprecated));
+CEPH_RADOS_API uint32_t rados_objects_list_seek(
+ rados_list_ctx_t ctx,
+ uint32_t pos) __attribute__((deprecated));
+CEPH_RADOS_API int rados_objects_list_next(
+ rados_list_ctx_t ctx,
+ const char **entry,
+ const char **key) __attribute__((deprecated));
+CEPH_RADOS_API void rados_objects_list_close(
+ rados_list_ctx_t ctx) __attribute__((deprecated));
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/include/rados/librados.hpp b/src/include/rados/librados.hpp
new file mode 100644
index 000000000..cb8261af1
--- /dev/null
+++ b/src/include/rados/librados.hpp
@@ -0,0 +1,1568 @@
+#ifndef __LIBRADOS_HPP
+#define __LIBRADOS_HPP
+
+#include <string>
+#include <list>
+#include <map>
+#include <memory>
+#include <set>
+#include <vector>
+#include <utility>
+#include "buffer.h"
+
+#include "librados.h"
+#include "librados_fwd.hpp"
+#include "rados_types.hpp"
+
+namespace libradosstriper
+{
+ class RadosStriper;
+}
+
+namespace neorados { class RADOS; }
+
+namespace librados {
+
+using ceph::bufferlist;
+
+struct AioCompletionImpl;
+struct IoCtxImpl;
+struct ListObjectImpl;
+class NObjectIteratorImpl;
+struct ObjListCtx;
+class ObjectOperationImpl;
+struct PlacementGroupImpl;
+struct PoolAsyncCompletionImpl;
+
+typedef struct rados_cluster_stat_t cluster_stat_t;
+typedef struct rados_pool_stat_t pool_stat_t;
+
+typedef void *list_ctx_t;
+typedef uint64_t auid_t;
+typedef void *config_t;
+
+typedef struct {
+ std::string client;
+ std::string cookie;
+ std::string address;
+} locker_t;
+
+typedef std::map<std::string, pool_stat_t> stats_map;
+
+typedef void *completion_t;
+typedef void (*callback_t)(completion_t cb, void *arg);
+
+inline namespace v14_2_0 {
+
+ class IoCtx;
+ class RadosClient;
+
+ class CEPH_RADOS_API ListObject
+ {
+ public:
+ const std::string& get_nspace() const;
+ const std::string& get_oid() const;
+ const std::string& get_locator() const;
+
+ ListObject();
+ ~ListObject();
+ ListObject( const ListObject&);
+ ListObject& operator=(const ListObject& rhs);
+ private:
+ ListObject(ListObjectImpl *impl);
+
+ friend class librados::NObjectIteratorImpl;
+ friend std::ostream& operator<<(std::ostream& out, const ListObject& lop);
+
+ ListObjectImpl *impl;
+ };
+ CEPH_RADOS_API std::ostream& operator<<(std::ostream& out, const librados::ListObject& lop);
+
+ class CEPH_RADOS_API NObjectIterator;
+
+ class CEPH_RADOS_API ObjectCursor
+ {
+ public:
+ ObjectCursor();
+ ObjectCursor(const ObjectCursor &rhs);
+ explicit ObjectCursor(rados_object_list_cursor c);
+ ~ObjectCursor();
+ ObjectCursor& operator=(const ObjectCursor& rhs);
+ bool operator<(const ObjectCursor &rhs) const;
+ bool operator==(const ObjectCursor &rhs) const;
+ void set(rados_object_list_cursor c);
+
+ friend class IoCtx;
+ friend class librados::NObjectIteratorImpl;
+ friend std::ostream& operator<<(std::ostream& os, const librados::ObjectCursor& oc);
+
+ std::string to_str() const;
+ bool from_str(const std::string& s);
+
+ protected:
+ rados_object_list_cursor c_cursor;
+ };
+ CEPH_RADOS_API std::ostream& operator<<(std::ostream& os, const librados::ObjectCursor& oc);
+
+ class CEPH_RADOS_API NObjectIterator {
+ public:
+ using iterator_category = std::forward_iterator_tag;
+ using value_type = ListObject;
+ using difference_type = std::ptrdiff_t;
+ using pointer = ListObject*;
+ using reference = ListObject&;
+ static const NObjectIterator __EndObjectIterator;
+ NObjectIterator(): impl(NULL) {}
+ ~NObjectIterator();
+ NObjectIterator(const NObjectIterator &rhs);
+ NObjectIterator& operator=(const NObjectIterator& rhs);
+
+ bool operator==(const NObjectIterator& rhs) const;
+ bool operator!=(const NObjectIterator& rhs) const;
+ const ListObject& operator*() const;
+ const ListObject* operator->() const;
+ NObjectIterator &operator++(); //< Preincrement; errors are thrown as exceptions
+ NObjectIterator operator++(int); //< Postincrement; errors are thrown as exceptions
+ friend class IoCtx;
+ friend class librados::NObjectIteratorImpl;
+
+ /// get current hash position of the iterator, rounded to the current pg
+ uint32_t get_pg_hash_position() const;
+
+ /// move the iterator to a given hash position. this may (will!) be rounded
+ /// to the nearest pg. errors are thrown as exceptions
+ uint32_t seek(uint32_t pos);
+
+ /// move the iterator to a given cursor position. errors are thrown as exceptions
+ uint32_t seek(const ObjectCursor& cursor);
+
+ /// get current cursor position
+ ObjectCursor get_cursor();
+
+ /**
+ * Configure PGLS filter to be applied OSD-side (requires caller
+ * to know/understand the format expected by the OSD)
+ */
+ void set_filter(const bufferlist &bl);
+
+ private:
+ NObjectIterator(ObjListCtx *ctx_);
+ void get_next();
+ NObjectIteratorImpl *impl;
+ };
+
+ class CEPH_RADOS_API ObjectItem
+ {
+ public:
+ std::string oid;
+ std::string nspace;
+ std::string locator;
+ };
+
+ /// DEPRECATED; do not use
+ class CEPH_RADOS_API WatchCtx {
+ public:
+ virtual ~WatchCtx();
+ virtual void notify(uint8_t opcode, uint64_t ver, bufferlist& bl) = 0;
+ };
+
+ class CEPH_RADOS_API WatchCtx2 {
+ public:
+ virtual ~WatchCtx2();
+ /**
+ * Callback activated when we receive a notify event.
+ *
+ * @param notify_id unique id for this notify event
+ * @param cookie the watcher we are notifying
+ * @param notifier_id the unique client id of the notifier
+ * @param bl opaque notify payload (from the notifier)
+ */
+ virtual void handle_notify(uint64_t notify_id,
+ uint64_t cookie,
+ uint64_t notifier_id,
+ bufferlist& bl) = 0;
+
+ /**
+ * Callback activated when we encounter an error with the watch.
+ *
+ * Errors we may see:
+ * -ENOTCONN : our watch was disconnected
+ * -ETIMEDOUT : our watch is still valid, but we may have missed
+ * a notify event.
+ *
+ * @param cookie the watcher with the problem
+ * @param err error
+ */
+ virtual void handle_error(uint64_t cookie, int err) = 0;
+ };
+
+ struct CEPH_RADOS_API AioCompletion {
+ AioCompletion(AioCompletionImpl *pc_) : pc(pc_) {}
+ ~AioCompletion();
+ int set_complete_callback(void *cb_arg, callback_t cb);
+ int set_safe_callback(void *cb_arg, callback_t cb)
+ __attribute__ ((deprecated));
+ int wait_for_complete();
+ int wait_for_safe() __attribute__ ((deprecated));
+ int wait_for_complete_and_cb();
+ int wait_for_safe_and_cb() __attribute__ ((deprecated));
+ bool is_complete();
+ bool is_safe() __attribute__ ((deprecated));
+ bool is_complete_and_cb();
+ bool is_safe_and_cb() __attribute__ ((deprecated));
+ int get_return_value();
+ int get_version() __attribute__ ((deprecated));
+ uint64_t get_version64();
+ void release();
+ AioCompletionImpl *pc;
+ };
+
+ struct CEPH_RADOS_API PoolAsyncCompletion {
+ PoolAsyncCompletion(PoolAsyncCompletionImpl *pc_) : pc(pc_) {}
+ ~PoolAsyncCompletion();
+ int set_callback(void *cb_arg, callback_t cb);
+ int wait();
+ bool is_complete();
+ int get_return_value();
+ void release();
+ PoolAsyncCompletionImpl *pc;
+ };
+
+ /**
+ * These are per-op flags which may be different among
+ * ops added to an ObjectOperation.
+ */
+ enum ObjectOperationFlags {
+ OP_EXCL = LIBRADOS_OP_FLAG_EXCL,
+ OP_FAILOK = LIBRADOS_OP_FLAG_FAILOK,
+ OP_FADVISE_RANDOM = LIBRADOS_OP_FLAG_FADVISE_RANDOM,
+ OP_FADVISE_SEQUENTIAL = LIBRADOS_OP_FLAG_FADVISE_SEQUENTIAL,
+ OP_FADVISE_WILLNEED = LIBRADOS_OP_FLAG_FADVISE_WILLNEED,
+ OP_FADVISE_DONTNEED = LIBRADOS_OP_FLAG_FADVISE_DONTNEED,
+ OP_FADVISE_NOCACHE = LIBRADOS_OP_FLAG_FADVISE_NOCACHE,
+ };
+
+ class CEPH_RADOS_API ObjectOperationCompletion {
+ public:
+ virtual ~ObjectOperationCompletion() {}
+ virtual void handle_completion(int r, bufferlist& outbl) = 0;
+ };
+
+ /**
+ * These flags apply to the ObjectOperation as a whole.
+ *
+ * Prior to octopus BALANCE_READS and LOCALIZE_READS should only
+ * be used when reading from data you're certain won't change, like
+ * a snapshot, or where eventual consistency is ok. Since octopus
+ * (get_min_compatible_osd() >= CEPH_RELEASE_OCTOPUS) both are safe
+ * for general use.
+ *
+ * ORDER_READS_WRITES will order reads the same way writes are
+ * ordered (e.g., waiting for degraded objects). In particular, it
+ * will make a write followed by a read sequence be preserved.
+ *
+ * IGNORE_CACHE will skip the caching logic on the OSD that normally
+ * handles promotion of objects between tiers. This allows an operation
+ * to operate (or read) the cached (or uncached) object, even if it is
+ * not coherent.
+ *
+ * IGNORE_OVERLAY will ignore the pool overlay tiering metadata and
+ * process the op directly on the destination pool. This is useful
+ * for CACHE_FLUSH and CACHE_EVICT operations.
+ */
+ enum ObjectOperationGlobalFlags {
+ OPERATION_NOFLAG = LIBRADOS_OPERATION_NOFLAG,
+ OPERATION_BALANCE_READS = LIBRADOS_OPERATION_BALANCE_READS,
+ OPERATION_LOCALIZE_READS = LIBRADOS_OPERATION_LOCALIZE_READS,
+ OPERATION_ORDER_READS_WRITES = LIBRADOS_OPERATION_ORDER_READS_WRITES,
+ OPERATION_IGNORE_CACHE = LIBRADOS_OPERATION_IGNORE_CACHE,
+ OPERATION_SKIPRWLOCKS = LIBRADOS_OPERATION_SKIPRWLOCKS,
+ OPERATION_IGNORE_OVERLAY = LIBRADOS_OPERATION_IGNORE_OVERLAY,
+ // send requests to cluster despite the cluster or pool being
+ // marked full; ops will either succeed (e.g., delete) or return
+ // EDQUOT or ENOSPC
+ OPERATION_FULL_TRY = LIBRADOS_OPERATION_FULL_TRY,
+ // mainly for delete
+ OPERATION_FULL_FORCE = LIBRADOS_OPERATION_FULL_FORCE,
+ OPERATION_IGNORE_REDIRECT = LIBRADOS_OPERATION_IGNORE_REDIRECT,
+ OPERATION_ORDERSNAP = LIBRADOS_OPERATION_ORDERSNAP,
+ // enable/allow return value and per-op return code/buffers
+ OPERATION_RETURNVEC = LIBRADOS_OPERATION_RETURNVEC,
+ };
+
+ /*
+ * Alloc hint flags for the alloc_hint operation.
+ */
+ enum AllocHintFlags {
+ ALLOC_HINT_FLAG_SEQUENTIAL_WRITE = 1,
+ ALLOC_HINT_FLAG_RANDOM_WRITE = 2,
+ ALLOC_HINT_FLAG_SEQUENTIAL_READ = 4,
+ ALLOC_HINT_FLAG_RANDOM_READ = 8,
+ ALLOC_HINT_FLAG_APPEND_ONLY = 16,
+ ALLOC_HINT_FLAG_IMMUTABLE = 32,
+ ALLOC_HINT_FLAG_SHORTLIVED = 64,
+ ALLOC_HINT_FLAG_LONGLIVED = 128,
+ ALLOC_HINT_FLAG_COMPRESSIBLE = 256,
+ ALLOC_HINT_FLAG_INCOMPRESSIBLE = 512,
+ };
+
+ /*
+ * ObjectOperation : compound object operation
+ * Batch multiple object operations into a single request, to be applied
+ * atomically.
+ */
+ class CEPH_RADOS_API ObjectOperation
+ {
+ public:
+ ObjectOperation();
+ virtual ~ObjectOperation();
+
+ ObjectOperation(const ObjectOperation&) = delete;
+ ObjectOperation& operator=(const ObjectOperation&) = delete;
+
+ /**
+ * Move constructor.
+ * \warning A moved from ObjectOperation is invalid and may not be used for
+ * any purpose. This is a hard contract violation and will
+ * kill your program.
+ */
+ ObjectOperation(ObjectOperation&&);
+ ObjectOperation& operator =(ObjectOperation&&);
+
+ size_t size();
+ void set_op_flags(ObjectOperationFlags flags) __attribute__((deprecated));
+ //flag mean ObjectOperationFlags
+ void set_op_flags2(int flags);
+
+ void cmpext(uint64_t off, const bufferlist& cmp_bl, int *prval);
+ void cmpxattr(const char *name, uint8_t op, const bufferlist& val);
+ void cmpxattr(const char *name, uint8_t op, uint64_t v);
+ void exec(const char *cls, const char *method, bufferlist& inbl);
+ void exec(const char *cls, const char *method, bufferlist& inbl, bufferlist *obl, int *prval);
+ void exec(const char *cls, const char *method, bufferlist& inbl, ObjectOperationCompletion *completion);
+ /**
+ * Guard operation with a check that object version == ver
+ *
+ * @param ver [in] version to check
+ */
+ void assert_version(uint64_t ver);
+
+ /**
+ * Guard operation with a check that the object already exists
+ */
+ void assert_exists();
+
+ /**
+ * get key/value pairs for specified keys
+ *
+ * @param assertions [in] comparison assertions
+ * @param prval [out] place error code in prval upon completion
+ *
+ * assertions has the form of mappings from keys to (comparison rval, assertion)
+ * The assertion field may be CEPH_OSD_CMPXATTR_OP_[GT|LT|EQ].
+ *
+ * That is, to assert that the value at key 'foo' is greater than 'bar':
+ *
+ * ObjectReadOperation op;
+ * int r;
+ * map<string, pair<bufferlist, int> > assertions;
+ * bufferlist bar(string('bar'));
+ * assertions['foo'] = make_pair(bar, CEPH_OSD_CMP_XATTR_OP_GT);
+ * op.omap_cmp(assertions, &r);
+ */
+ void omap_cmp(
+ const std::map<std::string, std::pair<bufferlist, int> > &assertions,
+ int *prval);
+
+ protected:
+ ObjectOperationImpl* impl;
+ friend class IoCtx;
+ friend class Rados;
+ };
+
+ /*
+ * ObjectWriteOperation : compound object write operation
+ * Batch multiple object operations into a single request, to be applied
+ * atomically.
+ */
+ class CEPH_RADOS_API ObjectWriteOperation : public ObjectOperation
+ {
+ protected:
+ time_t *unused;
+ public:
+ ObjectWriteOperation() : unused(NULL) {}
+ ~ObjectWriteOperation() override {}
+
+ ObjectWriteOperation(ObjectWriteOperation&&) = default;
+ ObjectWriteOperation& operator =(ObjectWriteOperation&&) = default;
+
+ void mtime(time_t *pt);
+ void mtime2(struct timespec *pts);
+
+ void create(bool exclusive);
+ void create(bool exclusive,
+ const std::string& category); ///< NOTE: category is unused
+
+ void write(uint64_t off, const bufferlist& bl);
+ void write_full(const bufferlist& bl);
+ void writesame(uint64_t off, uint64_t write_len,
+ const bufferlist& bl);
+ void append(const bufferlist& bl);
+ void remove();
+ void truncate(uint64_t off);
+ void zero(uint64_t off, uint64_t len);
+ void rmxattr(const char *name);
+ void setxattr(const char *name, const bufferlist& bl);
+ void setxattr(const char *name, const bufferlist&& bl);
+ void tmap_update(const bufferlist& cmdbl);
+ void tmap_put(const bufferlist& bl);
+ void selfmanaged_snap_rollback(uint64_t snapid);
+
+ /**
+ * Rollback an object to the specified snapshot id
+ *
+ * Used with pool snapshots
+ *
+ * @param snapid [in] snopshot id specified
+ */
+ void snap_rollback(uint64_t snapid);
+
+ /**
+ * set keys and values according to map
+ *
+ * @param map [in] keys and values to set
+ */
+ void omap_set(const std::map<std::string, bufferlist> &map);
+
+ /**
+ * set header
+ *
+ * @param bl [in] header to set
+ */
+ void omap_set_header(const bufferlist &bl);
+
+ /**
+ * Clears omap contents
+ */
+ void omap_clear();
+
+ /**
+ * Clears keys in to_rm
+ *
+ * @param to_rm [in] keys to remove
+ */
+ void omap_rm_keys(const std::set<std::string> &to_rm);
+
+ /**
+ * Copy an object
+ *
+ * Copies an object from another location. The operation is atomic in that
+ * the copy either succeeds in its entirety or fails (e.g., because the
+ * source object was modified while the copy was in progress).
+ *
+ * @param src source object name
+ * @param src_ioctx ioctx for the source object
+ * @param src_version current version of the source object
+ * @param src_fadvise_flags the fadvise flags for source object
+ */
+ void copy_from(const std::string& src, const IoCtx& src_ioctx,
+ uint64_t src_version, uint32_t src_fadvise_flags);
+
+ /**
+ * Copy an object
+ *
+ * Copies an object from another location. The operation is atomic in that
+ * the copy either succeeds in its entirety or fails (e.g., because the
+ * source object was modified while the copy was in progress). Instead of
+ * copying truncate_seq and truncate_size from the source object it receives
+ * these values as parameters.
+ *
+ * @param src source object name
+ * @param src_ioctx ioctx for the source object
+ * @param src_version current version of the source object
+ * @param truncate_seq truncate sequence for the destination object
+ * @param truncate_size truncate size for the destination object
+ * @param src_fadvise_flags the fadvise flags for source object
+ */
+ void copy_from2(const std::string& src, const IoCtx& src_ioctx,
+ uint64_t src_version, uint32_t truncate_seq,
+ uint64_t truncate_size, uint32_t src_fadvise_flags);
+
+ /**
+ * undirty an object
+ *
+ * Clear an objects dirty flag
+ */
+ void undirty();
+
+ /**
+ * Set allocation hint for an object
+ *
+ * @param expected_object_size expected size of the object, in bytes
+ * @param expected_write_size expected size of writes to the object, in bytes
+ * @param flags flags ()
+ */
+ void set_alloc_hint(uint64_t expected_object_size,
+ uint64_t expected_write_size);
+ void set_alloc_hint2(uint64_t expected_object_size,
+ uint64_t expected_write_size,
+ uint32_t flags);
+
+ /**
+ * Pin/unpin an object in cache tier
+ *
+ * @returns 0 on success, negative error code on failure
+ */
+ void cache_pin();
+ void cache_unpin();
+
+ /**
+ * Extensible tier
+ *
+ * Set redirect target
+ */
+ void set_redirect(const std::string& tgt_obj, const IoCtx& tgt_ioctx,
+ uint64_t tgt_version, int flag = 0);
+ void tier_promote();
+ void unset_manifest();
+
+ friend class IoCtx;
+ };
+
+ /*
+ * ObjectReadOperation : compound object operation that return value
+ * Batch multiple object operations into a single request, to be applied
+ * atomically.
+ */
+ class CEPH_RADOS_API ObjectReadOperation : public ObjectOperation
+ {
+ public:
+ ObjectReadOperation() {}
+ ~ObjectReadOperation() override {}
+
+ ObjectReadOperation(ObjectReadOperation&&) = default;
+ ObjectReadOperation& operator =(ObjectReadOperation&&) = default;
+
+ void stat(uint64_t *psize, time_t *pmtime, int *prval);
+ void stat2(uint64_t *psize, struct timespec *pts, int *prval);
+ void getxattr(const char *name, bufferlist *pbl, int *prval);
+ void getxattrs(std::map<std::string, bufferlist> *pattrs, int *prval);
+ void read(size_t off, uint64_t len, bufferlist *pbl, int *prval);
+ void checksum(rados_checksum_type_t type, const bufferlist &init_value_bl,
+ uint64_t off, size_t len, size_t chunk_size, bufferlist *pbl,
+ int *prval);
+
+ /**
+ * see aio_sparse_read()
+ */
+ void sparse_read(uint64_t off, uint64_t len, std::map<uint64_t,uint64_t> *m,
+ bufferlist *data_bl, int *prval,
+ uint64_t truncate_size = 0,
+ uint32_t truncate_seq = 0);
+
+ /**
+ * omap_get_vals: keys and values from the object omap
+ *
+ * Get up to max_return keys and values beginning after start_after
+ *
+ * @param start_after [in] list no keys smaller than start_after
+ * @param max_return [in] list no more than max_return key/value pairs
+ * @param out_vals [out] place returned values in out_vals on completion
+ * @param prval [out] place error code in prval upon completion
+ */
+ void omap_get_vals(
+ const std::string &start_after,
+ uint64_t max_return,
+ std::map<std::string, bufferlist> *out_vals,
+ int *prval) __attribute__ ((deprecated)); // use v2
+
+ /**
+ * omap_get_vals: keys and values from the object omap
+ *
+ * Get up to max_return keys and values beginning after start_after
+ *
+ * @param start_after [in] list no keys smaller than start_after
+ * @param max_return [in] list no more than max_return key/value pairs
+ * @param out_vals [out] place returned values in out_vals on completion
+ * @param prval [out] place error code in prval upon completion
+ */
+ void omap_get_vals2(
+ const std::string &start_after,
+ uint64_t max_return,
+ std::map<std::string, bufferlist> *out_vals,
+ bool *pmore,
+ int *prval);
+
+ /**
+ * omap_get_vals: keys and values from the object omap
+ *
+ * Get up to max_return keys and values beginning after start_after
+ *
+ * @param start_after [in] list keys starting after start_after
+ * @param filter_prefix [in] list only keys beginning with filter_prefix
+ * @param max_return [in] list no more than max_return key/value pairs
+ * @param out_vals [out] place returned values in out_vals on completion
+ * @param prval [out] place error code in prval upon completion
+ */
+ void omap_get_vals(
+ const std::string &start_after,
+ const std::string &filter_prefix,
+ uint64_t max_return,
+ std::map<std::string, bufferlist> *out_vals,
+ int *prval) __attribute__ ((deprecated)); // use v2
+
+ /**
+ * omap_get_vals2: keys and values from the object omap
+ *
+ * Get up to max_return keys and values beginning after start_after
+ *
+ * @param start_after [in] list keys starting after start_after
+ * @param filter_prefix [in] list only keys beginning with filter_prefix
+ * @param max_return [in] list no more than max_return key/value pairs
+ * @param out_vals [out] place returned values in out_vals on completion
+ * @param pmore [out] pointer to bool indicating whether there are more keys
+ * @param prval [out] place error code in prval upon completion
+ */
+ void omap_get_vals2(
+ const std::string &start_after,
+ const std::string &filter_prefix,
+ uint64_t max_return,
+ std::map<std::string, bufferlist> *out_vals,
+ bool *pmore,
+ int *prval);
+
+
+ /**
+ * omap_get_keys: keys from the object omap
+ *
+ * Get up to max_return keys beginning after start_after
+ *
+ * @param start_after [in] list keys starting after start_after
+ * @param max_return [in] list no more than max_return keys
+ * @param out_keys [out] place returned values in out_keys on completion
+ * @param prval [out] place error code in prval upon completion
+ */
+ void omap_get_keys(const std::string &start_after,
+ uint64_t max_return,
+ std::set<std::string> *out_keys,
+ int *prval) __attribute__ ((deprecated)); // use v2
+
+ /**
+ * omap_get_keys2: keys from the object omap
+ *
+ * Get up to max_return keys beginning after start_after
+ *
+ * @param start_after [in] list keys starting after start_after
+ * @param max_return [in] list no more than max_return keys
+ * @param out_keys [out] place returned values in out_keys on completion
+ * @param pmore [out] pointer to bool indicating whether there are more keys
+ * @param prval [out] place error code in prval upon completion
+ */
+ void omap_get_keys2(const std::string &start_after,
+ uint64_t max_return,
+ std::set<std::string> *out_keys,
+ bool *pmore,
+ int *prval);
+
+ /**
+ * omap_get_header: get header from object omap
+ *
+ * @param header [out] place header here upon completion
+ * @param prval [out] place error code in prval upon completion
+ */
+ void omap_get_header(bufferlist *header, int *prval);
+
+ /**
+ * get key/value pairs for specified keys
+ *
+ * @param keys [in] keys to get
+ * @param map [out] place key/value pairs found here on completion
+ * @param prval [out] place error code in prval upon completion
+ */
+ void omap_get_vals_by_keys(const std::set<std::string> &keys,
+ std::map<std::string, bufferlist> *map,
+ int *prval);
+
+ /**
+ * list_watchers: Get list watchers of object
+ *
+ * @param out_watchers [out] place returned values in out_watchers on completion
+ * @param prval [out] place error code in prval upon completion
+ */
+ void list_watchers(std::list<obj_watch_t> *out_watchers, int *prval);
+
+ /**
+ * list snapshot clones associated with a logical object
+ *
+ * This will include a record for each version of the object,
+ * include the "HEAD" (which will have a cloneid of SNAP_HEAD).
+ * Each clone includes a vector of snap ids for which it is
+ * defined to exist.
+ *
+ * NOTE: this operation must be submitted from an IoCtx with a
+ * read snapid of SNAP_DIR for reliable results.
+ *
+ * @param out_snaps [out] pointer to resulting snap_set_t
+ * @param prval [out] place error code in prval upon completion
+ */
+ void list_snaps(snap_set_t *out_snaps, int *prval);
+
+ /**
+ * query dirty state of an object
+ *
+ * @param isdirty [out] pointer to resulting bool
+ * @param prval [out] place error code in prval upon completion
+ */
+ void is_dirty(bool *isdirty, int *prval);
+
+ /**
+ * flush a cache tier object to backing tier; will block racing
+ * updates.
+ *
+ * This should be used in concert with OPERATION_IGNORE_CACHE to avoid
+ * triggering a promotion.
+ */
+ void cache_flush();
+
+ /**
+ * Flush a cache tier object to backing tier; will EAGAIN if we race
+ * with an update. Must be used with the SKIPRWLOCKS flag.
+ *
+ * This should be used in concert with OPERATION_IGNORE_CACHE to avoid
+ * triggering a promotion.
+ */
+ void cache_try_flush();
+
+ /**
+ * evict a clean cache tier object
+ *
+ * This should be used in concert with OPERATION_IGNORE_CACHE to avoid
+ * triggering a promote on the OSD (that is then evicted).
+ */
+ void cache_evict();
+
+ /**
+ * Extensible tier
+ *
+ * set_chunk: make a chunk pointing a part of the source object at the target
+ * object
+ *
+ * @param src_offset [in] source offset to indicate the start position of
+ * a chunk in the source object
+ * @param src_length [in] source length to set the length of the chunk
+ * @param tgt_oid [in] target object's id to set a chunk
+ * @param tgt_offset [in] the start position of the target object
+ * @param flag [in] flag for the source object
+ *
+ */
+ void set_chunk(uint64_t src_offset, uint64_t src_length, const IoCtx& tgt_ioctx,
+ std::string tgt_oid, uint64_t tgt_offset, int flag = 0);
+ /**
+ * flush a manifest tier object to backing tier, performing deduplication;
+ * will block racing updates.
+ *
+ * Invoking tier_flush() implicitly makes a manifest object even if
+ * the target object is not manifest.
+ */
+ void tier_flush();
+ /**
+ * evict a manifest tier object to backing tier; will block racing
+ * updates.
+ */
+ void tier_evict();
+ };
+
+ /* IoCtx : This is a context in which we can perform I/O.
+ * It includes a Pool,
+ *
+ * Typical use (error checking omitted):
+ *
+ * IoCtx p;
+ * rados.ioctx_create("my_pool", p);
+ * p->stat(&stats);
+ * ... etc ...
+ *
+ * NOTE: be sure to call watch_flush() prior to destroying any IoCtx
+ * that is used for watch events to ensure that racing callbacks
+ * have completed.
+ */
+ class CEPH_RADOS_API IoCtx
+ {
+ public:
+ IoCtx();
+ static void from_rados_ioctx_t(rados_ioctx_t p, IoCtx &pool);
+ IoCtx(const IoCtx& rhs);
+ IoCtx& operator=(const IoCtx& rhs);
+ IoCtx(IoCtx&& rhs) noexcept;
+ IoCtx& operator=(IoCtx&& rhs) noexcept;
+
+ ~IoCtx();
+
+ bool is_valid() const;
+
+ // Close our pool handle
+ void close();
+
+ // deep copy
+ void dup(const IoCtx& rhs);
+
+ // set pool auid
+ int set_auid(uint64_t auid_)
+ __attribute__ ((deprecated));
+
+ // set pool auid
+ int set_auid_async(uint64_t auid_, PoolAsyncCompletion *c)
+ __attribute__ ((deprecated));
+
+ // get pool auid
+ int get_auid(uint64_t *auid_)
+ __attribute__ ((deprecated));
+
+ uint64_t get_instance_id() const;
+
+ std::string get_pool_name();
+
+ bool pool_requires_alignment();
+ int pool_requires_alignment2(bool * req);
+ uint64_t pool_required_alignment();
+ int pool_required_alignment2(uint64_t * alignment);
+
+ // create an object
+ int create(const std::string& oid, bool exclusive);
+ int create(const std::string& oid, bool exclusive,
+ const std::string& category); ///< category is unused
+
+ /**
+ * write bytes to an object at a specified offset
+ *
+ * NOTE: this call steals the contents of @param bl.
+ */
+ int write(const std::string& oid, bufferlist& bl, size_t len, uint64_t off);
+ /**
+ * append bytes to an object
+ *
+ * NOTE: this call steals the contents of @param bl.
+ */
+ int append(const std::string& oid, bufferlist& bl, size_t len);
+ /**
+ * replace object contents with provided data
+ *
+ * NOTE: this call steals the contents of @param bl.
+ */
+ int write_full(const std::string& oid, bufferlist& bl);
+ int writesame(const std::string& oid, bufferlist& bl,
+ size_t write_len, uint64_t off);
+ int read(const std::string& oid, bufferlist& bl, size_t len, uint64_t off);
+ int checksum(const std::string& o, rados_checksum_type_t type,
+ const bufferlist &init_value_bl, size_t len, uint64_t off,
+ size_t chunk_size, bufferlist *pbl);
+ int remove(const std::string& oid);
+ int remove(const std::string& oid, int flags);
+ int trunc(const std::string& oid, uint64_t size);
+ int mapext(const std::string& o, uint64_t off, size_t len, std::map<uint64_t,uint64_t>& m);
+ int cmpext(const std::string& o, uint64_t off, bufferlist& cmp_bl);
+ int sparse_read(const std::string& o, std::map<uint64_t,uint64_t>& m, bufferlist& bl, size_t len, uint64_t off);
+ int getxattr(const std::string& oid, const char *name, bufferlist& bl);
+ int getxattrs(const std::string& oid, std::map<std::string, bufferlist>& attrset);
+ int setxattr(const std::string& oid, const char *name, bufferlist& bl);
+ int rmxattr(const std::string& oid, const char *name);
+ int stat(const std::string& oid, uint64_t *psize, time_t *pmtime);
+ int stat2(const std::string& oid, uint64_t *psize, struct timespec *pts);
+ int exec(const std::string& oid, const char *cls, const char *method,
+ bufferlist& inbl, bufferlist& outbl);
+ /**
+ * modify object tmap based on encoded update sequence
+ *
+ * NOTE: this call steals the contents of @param bl
+ */
+ int tmap_update(const std::string& oid, bufferlist& cmdbl);
+
+ int omap_get_vals(const std::string& oid,
+ const std::string& start_after,
+ uint64_t max_return,
+ std::map<std::string, bufferlist> *out_vals);
+ int omap_get_vals2(const std::string& oid,
+ const std::string& start_after,
+ uint64_t max_return,
+ std::map<std::string, bufferlist> *out_vals,
+ bool *pmore);
+ int omap_get_vals(const std::string& oid,
+ const std::string& start_after,
+ const std::string& filter_prefix,
+ uint64_t max_return,
+ std::map<std::string, bufferlist> *out_vals);
+ int omap_get_vals2(const std::string& oid,
+ const std::string& start_after,
+ const std::string& filter_prefix,
+ uint64_t max_return,
+ std::map<std::string, bufferlist> *out_vals,
+ bool *pmore);
+ int omap_get_keys(const std::string& oid,
+ const std::string& start_after,
+ uint64_t max_return,
+ std::set<std::string> *out_keys);
+ int omap_get_keys2(const std::string& oid,
+ const std::string& start_after,
+ uint64_t max_return,
+ std::set<std::string> *out_keys,
+ bool *pmore);
+ int omap_get_header(const std::string& oid,
+ bufferlist *bl);
+ int omap_get_vals_by_keys(const std::string& oid,
+ const std::set<std::string>& keys,
+ std::map<std::string, bufferlist> *vals);
+ int omap_set(const std::string& oid,
+ const std::map<std::string, bufferlist>& map);
+ int omap_set_header(const std::string& oid,
+ const bufferlist& bl);
+ int omap_clear(const std::string& oid);
+ int omap_rm_keys(const std::string& oid,
+ const std::set<std::string>& keys);
+
+ void snap_set_read(snap_t seq);
+ int selfmanaged_snap_set_write_ctx(snap_t seq, std::vector<snap_t>& snaps);
+
+ // Create a snapshot with a given name
+ int snap_create(const char *snapname);
+
+ // Look up a snapshot by name.
+ // Returns 0 on success; error code otherwise
+ int snap_lookup(const char *snapname, snap_t *snap);
+
+ // Gets a timestamp for a snap
+ int snap_get_stamp(snap_t snapid, time_t *t);
+
+ // Gets the name of a snap
+ int snap_get_name(snap_t snapid, std::string *s);
+
+ // Remove a snapshot from this pool
+ int snap_remove(const char *snapname);
+
+ int snap_list(std::vector<snap_t> *snaps);
+
+ int snap_rollback(const std::string& oid, const char *snapname);
+
+ // Deprecated name kept for backward compatibility - same as snap_rollback()
+ int rollback(const std::string& oid, const char *snapname)
+ __attribute__ ((deprecated));
+
+ int selfmanaged_snap_create(uint64_t *snapid);
+ void aio_selfmanaged_snap_create(uint64_t *snapid, AioCompletion *c);
+
+ int selfmanaged_snap_remove(uint64_t snapid);
+ void aio_selfmanaged_snap_remove(uint64_t snapid, AioCompletion *c);
+
+ int selfmanaged_snap_rollback(const std::string& oid, uint64_t snapid);
+
+ // Advisory locking on rados objects.
+ int lock_exclusive(const std::string &oid, const std::string &name,
+ const std::string &cookie,
+ const std::string &description,
+ struct timeval * duration, uint8_t flags);
+
+ int lock_shared(const std::string &oid, const std::string &name,
+ const std::string &cookie, const std::string &tag,
+ const std::string &description,
+ struct timeval * duration, uint8_t flags);
+
+ int unlock(const std::string &oid, const std::string &name,
+ const std::string &cookie);
+
+ int break_lock(const std::string &oid, const std::string &name,
+ const std::string &client, const std::string &cookie);
+
+ int list_lockers(const std::string &oid, const std::string &name,
+ int *exclusive,
+ std::string *tag,
+ std::list<librados::locker_t> *lockers);
+
+
+ /// Start enumerating objects for a pool. Errors are thrown as exceptions.
+ NObjectIterator nobjects_begin(const bufferlist &filter=bufferlist());
+ /// Start enumerating objects for a pool starting from a hash position.
+ /// Errors are thrown as exceptions.
+ NObjectIterator nobjects_begin(uint32_t start_hash_position,
+ const bufferlist &filter=bufferlist());
+ /// Start enumerating objects for a pool starting from cursor. Errors are
+ /// thrown as exceptions.
+ NObjectIterator nobjects_begin(const librados::ObjectCursor& cursor,
+ const bufferlist &filter=bufferlist());
+ /// Iterator indicating the end of a pool
+ const NObjectIterator& nobjects_end() const;
+
+ /// Get cursor for pool beginning
+ ObjectCursor object_list_begin();
+
+ /// Get cursor for pool end
+ ObjectCursor object_list_end();
+
+ /// Check whether a cursor is at the end of a pool
+ bool object_list_is_end(const ObjectCursor &oc);
+
+ /// List some objects between two cursors
+ int object_list(const ObjectCursor &start, const ObjectCursor &finish,
+ const size_t result_count,
+ const bufferlist &filter,
+ std::vector<ObjectItem> *result,
+ ObjectCursor *next);
+
+ /// Generate cursors that include the N out of Mth slice of the pool
+ void object_list_slice(
+ const ObjectCursor start,
+ const ObjectCursor finish,
+ const size_t n,
+ const size_t m,
+ ObjectCursor *split_start,
+ ObjectCursor *split_finish);
+
+ /**
+ * List available hit set objects
+ *
+ * @param uint32_t [in] hash position to query
+ * @param c [in] completion
+ * @param pls [out] list of available intervals
+ */
+ int hit_set_list(uint32_t hash, AioCompletion *c,
+ std::list< std::pair<time_t, time_t> > *pls);
+
+ /**
+ * Retrieve hit set for a given hash, and time
+ *
+ * @param hash [in] hash position
+ * @param c [in] completion
+ * @param stamp [in] time interval that falls within the hit set's interval
+ * @param pbl [out] buffer to store the result in
+ */
+ int hit_set_get(uint32_t hash, AioCompletion *c, time_t stamp,
+ bufferlist *pbl);
+
+ uint64_t get_last_version();
+
+ int aio_read(const std::string& oid, AioCompletion *c,
+ bufferlist *pbl, size_t len, uint64_t off);
+ /**
+ * Asynchronously read from an object at a particular snapshot
+ *
+ * This is the same as normal aio_read, except that it chooses
+ * the snapshot to read from from its arguments instead of the
+ * internal IoCtx state.
+ *
+ * The return value of the completion will be number of bytes read on
+ * success, negative error code on failure.
+ *
+ * @param oid the name of the object to read from
+ * @param c what to do when the read is complete
+ * @param pbl where to store the results
+ * @param len the number of bytes to read
+ * @param off the offset to start reading from in the object
+ * @param snapid the id of the snapshot to read from
+ * @returns 0 on success, negative error code on failure
+ */
+ int aio_read(const std::string& oid, AioCompletion *c,
+ bufferlist *pbl, size_t len, uint64_t off, uint64_t snapid);
+ int aio_sparse_read(const std::string& oid, AioCompletion *c,
+ std::map<uint64_t,uint64_t> *m, bufferlist *data_bl,
+ size_t len, uint64_t off);
+ /**
+ * Asynchronously read existing extents from an object at a
+ * particular snapshot
+ *
+ * This is the same as normal aio_sparse_read, except that it chooses
+ * the snapshot to read from from its arguments instead of the
+ * internal IoCtx state.
+ *
+ * m will be filled in with a map of extents in the object,
+ * mapping offsets to lengths (in bytes) within the range
+ * requested. The data for all of the extents are stored
+ * back-to-back in offset order in data_bl.
+ *
+ * @param oid the name of the object to read from
+ * @param c what to do when the read is complete
+ * @param m where to store the map of extents
+ * @param data_bl where to store the data
+ * @param len the number of bytes to read
+ * @param off the offset to start reading from in the object
+ * @param snapid the id of the snapshot to read from
+ * @returns 0 on success, negative error code on failure
+ */
+ int aio_sparse_read(const std::string& oid, AioCompletion *c,
+ std::map<uint64_t,uint64_t> *m, bufferlist *data_bl,
+ size_t len, uint64_t off, uint64_t snapid);
+ /**
+ * Asynchronously compare an on-disk object range with a buffer
+ *
+ * @param oid the name of the object to read from
+ * @param c what to do when the read is complete
+ * @param off object byte offset at which to start the comparison
+ * @param cmp_bl buffer containing bytes to be compared with object contents
+ * @returns 0 on success, negative error code on failure,
+ * (-MAX_ERRNO - mismatch_off) on mismatch
+ */
+ int aio_cmpext(const std::string& oid,
+ librados::AioCompletion *c,
+ uint64_t off,
+ bufferlist& cmp_bl);
+ int aio_write(const std::string& oid, AioCompletion *c, const bufferlist& bl,
+ size_t len, uint64_t off);
+ int aio_append(const std::string& oid, AioCompletion *c, const bufferlist& bl,
+ size_t len);
+ int aio_write_full(const std::string& oid, AioCompletion *c, const bufferlist& bl);
+ int aio_writesame(const std::string& oid, AioCompletion *c, const bufferlist& bl,
+ size_t write_len, uint64_t off);
+
+ /**
+ * Asynchronously remove an object
+ *
+ * Queues the remove and returns.
+ *
+ * The return value of the completion will be 0 on success, negative
+ * error code on failure.
+ *
+ * @param oid the name of the object
+ * @param c what to do when the remove is safe and complete
+ * @returns 0 on success, -EROFS if the io context specifies a snap_seq
+ * other than SNAP_HEAD
+ */
+ int aio_remove(const std::string& oid, AioCompletion *c);
+ int aio_remove(const std::string& oid, AioCompletion *c, int flags);
+
+ /**
+ * Wait for all currently pending aio writes to be safe.
+ *
+ * @returns 0 on success, negative error code on failure
+ */
+ int aio_flush();
+
+ /**
+ * Schedule a callback for when all currently pending
+ * aio writes are safe. This is a non-blocking version of
+ * aio_flush().
+ *
+ * @param c what to do when the writes are safe
+ * @returns 0 on success, negative error code on failure
+ */
+ int aio_flush_async(AioCompletion *c);
+ int aio_getxattr(const std::string& oid, AioCompletion *c, const char *name, bufferlist& bl);
+ int aio_getxattrs(const std::string& oid, AioCompletion *c, std::map<std::string, bufferlist>& attrset);
+ int aio_setxattr(const std::string& oid, AioCompletion *c, const char *name, bufferlist& bl);
+ int aio_rmxattr(const std::string& oid, AioCompletion *c, const char *name);
+ int aio_stat(const std::string& oid, AioCompletion *c, uint64_t *psize, time_t *pmtime);
+ int aio_stat2(const std::string& oid, AioCompletion *c, uint64_t *psize, struct timespec *pts);
+
+ /**
+ * Cancel aio operation
+ *
+ * @param c completion handle
+ * @returns 0 on success, negative error code on failure
+ */
+ int aio_cancel(AioCompletion *c);
+
+ int aio_exec(const std::string& oid, AioCompletion *c, const char *cls, const char *method,
+ bufferlist& inbl, bufferlist *outbl);
+
+ /*
+ * asynchronous version of unlock
+ */
+ int aio_unlock(const std::string &oid, const std::string &name,
+ const std::string &cookie, AioCompletion *c);
+
+ // compound object operations
+ int operate(const std::string& oid, ObjectWriteOperation *op);
+ int operate(const std::string& oid, ObjectWriteOperation *op, int flags);
+ int operate(const std::string& oid, ObjectReadOperation *op, bufferlist *pbl);
+ int operate(const std::string& oid, ObjectReadOperation *op, bufferlist *pbl, int flags);
+ int aio_operate(const std::string& oid, AioCompletion *c, ObjectWriteOperation *op);
+ int aio_operate(const std::string& oid, AioCompletion *c, ObjectWriteOperation *op, int flags);
+ /**
+ * Schedule an async write operation with explicit snapshot parameters
+ *
+ * This is the same as the first aio_operate(), except that it
+ * gets the snapshot context from its arguments instead of the
+ * IoCtx internal state.
+ *
+ * @param oid the object to operate on
+ * @param c what to do when the operation is complete and safe
+ * @param op which operations to perform
+ * @param seq latest selfmanaged snapshot sequence number for this object
+ * @param snaps currently existing selfmanaged snapshot ids for this object
+ * @returns 0 on success, negative error code on failure
+ */
+ int aio_operate(const std::string& oid, AioCompletion *c,
+ ObjectWriteOperation *op, snap_t seq,
+ std::vector<snap_t>& snaps);
+ int aio_operate(const std::string& oid, AioCompletion *c,
+ ObjectWriteOperation *op, snap_t seq,
+ std::vector<snap_t>& snaps,
+ const blkin_trace_info *trace_info);
+ int aio_operate(const std::string& oid, AioCompletion *c,
+ ObjectWriteOperation *op, snap_t seq,
+ std::vector<snap_t>& snaps, int flags,
+ const blkin_trace_info *trace_info);
+ int aio_operate(const std::string& oid, AioCompletion *c,
+ ObjectReadOperation *op, bufferlist *pbl);
+
+ int aio_operate(const std::string& oid, AioCompletion *c,
+ ObjectReadOperation *op, snap_t snapid, int flags,
+ bufferlist *pbl)
+ __attribute__ ((deprecated));
+
+ int aio_operate(const std::string& oid, AioCompletion *c,
+ ObjectReadOperation *op, int flags,
+ bufferlist *pbl);
+ int aio_operate(const std::string& oid, AioCompletion *c,
+ ObjectReadOperation *op, int flags,
+ bufferlist *pbl, const blkin_trace_info *trace_info);
+
+ // watch/notify
+ int watch2(const std::string& o, uint64_t *handle,
+ librados::WatchCtx2 *ctx);
+ int watch3(const std::string& o, uint64_t *handle,
+ librados::WatchCtx2 *ctx, uint32_t timeout);
+ int aio_watch(const std::string& o, AioCompletion *c, uint64_t *handle,
+ librados::WatchCtx2 *ctx);
+ int aio_watch2(const std::string& o, AioCompletion *c, uint64_t *handle,
+ librados::WatchCtx2 *ctx, uint32_t timeout);
+ int unwatch2(uint64_t handle);
+ int aio_unwatch(uint64_t handle, AioCompletion *c);
+ /**
+ * Send a notify event to watchers
+ *
+ * Upon completion the pbl bufferlist reply payload will be
+ * encoded like so:
+ *
+ * le32 num_acks
+ * {
+ * le64 gid global id for the client (for client.1234 that's 1234)
+ * le64 cookie cookie for the client
+ * le32 buflen length of reply message buffer
+ * u8 * buflen payload
+ * } * num_acks
+ * le32 num_timeouts
+ * {
+ * le64 gid global id for the client
+ * le64 cookie cookie for the client
+ * } * num_timeouts
+ *
+ *
+ */
+ int notify2(const std::string& o, ///< object
+ bufferlist& bl, ///< optional broadcast payload
+ uint64_t timeout_ms, ///< timeout (in ms)
+ bufferlist *pbl); ///< reply buffer
+ int aio_notify(const std::string& o, ///< object
+ AioCompletion *c, ///< completion when notify completes
+ bufferlist& bl, ///< optional broadcast payload
+ uint64_t timeout_ms, ///< timeout (in ms)
+ bufferlist *pbl); ///< reply buffer
+ /*
+ * Decode a notify response into acks and timeout vectors.
+ */
+ void decode_notify_response(bufferlist &bl,
+ std::vector<librados::notify_ack_t> *acks,
+ std::vector<librados::notify_timeout_t> *timeouts);
+
+ int list_watchers(const std::string& o, std::list<obj_watch_t> *out_watchers);
+ int list_snaps(const std::string& o, snap_set_t *out_snaps);
+ void set_notify_timeout(uint32_t timeout);
+
+ /// acknowledge a notify we received.
+ void notify_ack(const std::string& o, ///< watched object
+ uint64_t notify_id, ///< notify id
+ uint64_t cookie, ///< our watch handle
+ bufferlist& bl); ///< optional reply payload
+
+ /***
+ * check on watch validity
+ *
+ * Check if a watch is valid. If so, return the number of
+ * milliseconds since we last confirmed its liveness. If there is
+ * a known error, return it.
+ *
+ * If there is an error, the watch is no longer valid, and should
+ * be destroyed with unwatch(). The user is still interested in
+ * the object, a new watch should be created with watch().
+ *
+ * @param cookie watch handle
+ * @returns ms since last confirmed valid, or error
+ */
+ int watch_check(uint64_t cookie);
+
+ // old, deprecated versions
+ int watch(const std::string& o, uint64_t ver, uint64_t *cookie,
+ librados::WatchCtx *ctx) __attribute__ ((deprecated));
+ int notify(const std::string& o, uint64_t ver, bufferlist& bl)
+ __attribute__ ((deprecated));
+ int unwatch(const std::string& o, uint64_t cookie)
+ __attribute__ ((deprecated));
+
+ /**
+ * Set allocation hint for an object
+ *
+ * This is an advisory operation, it will always succeed (as if it
+ * was submitted with a OP_FAILOK flag set) and is not guaranteed
+ * to do anything on the backend.
+ *
+ * @param o the name of the object
+ * @param expected_object_size expected size of the object, in bytes
+ * @param expected_write_size expected size of writes to the object, in bytes
+ * @returns 0 on success, negative error code on failure
+ */
+ int set_alloc_hint(const std::string& o,
+ uint64_t expected_object_size,
+ uint64_t expected_write_size);
+ int set_alloc_hint2(const std::string& o,
+ uint64_t expected_object_size,
+ uint64_t expected_write_size,
+ uint32_t flags);
+
+ // assert version for next sync operations
+ void set_assert_version(uint64_t ver);
+
+ /**
+ * Pin/unpin an object in cache tier
+ *
+ * @param o the name of the object
+ * @returns 0 on success, negative error code on failure
+ */
+ int cache_pin(const std::string& o);
+ int cache_unpin(const std::string& o);
+
+ std::string get_pool_name() const;
+
+ void locator_set_key(const std::string& key);
+ void set_namespace(const std::string& nspace);
+ std::string get_namespace() const;
+
+ int64_t get_id();
+
+ // deprecated versions
+ uint32_t get_object_hash_position(const std::string& oid)
+ __attribute__ ((deprecated));
+ uint32_t get_object_pg_hash_position(const std::string& oid)
+ __attribute__ ((deprecated));
+
+ int get_object_hash_position2(const std::string& oid, uint32_t *hash_position);
+ int get_object_pg_hash_position2(const std::string& oid, uint32_t *pg_hash_position);
+
+ config_t cct();
+
+ void set_osdmap_full_try()
+ __attribute__ ((deprecated));
+ void unset_osdmap_full_try()
+ __attribute__ ((deprecated));
+
+ bool get_pool_full_try();
+ void set_pool_full_try();
+ void unset_pool_full_try();
+
+ int application_enable(const std::string& app_name, bool force);
+ int application_enable_async(const std::string& app_name,
+ bool force, PoolAsyncCompletion *c);
+ int application_list(std::set<std::string> *app_names);
+ int application_metadata_get(const std::string& app_name,
+ const std::string &key,
+ std::string *value);
+ int application_metadata_set(const std::string& app_name,
+ const std::string &key,
+ const std::string& value);
+ int application_metadata_remove(const std::string& app_name,
+ const std::string &key);
+ int application_metadata_list(const std::string& app_name,
+ std::map<std::string, std::string> *values);
+
+ private:
+ /* You can only get IoCtx instances from Rados */
+ IoCtx(IoCtxImpl *io_ctx_impl_);
+
+ friend class Rados; // Only Rados can use our private constructor to create IoCtxes.
+ friend class libradosstriper::RadosStriper; // Striper needs to see our IoCtxImpl
+ friend class ObjectWriteOperation; // copy_from needs to see our IoCtxImpl
+ friend class ObjectReadOperation; // set_chunk needs to see our IoCtxImpl
+
+ IoCtxImpl *io_ctx_impl;
+ };
+
+ struct CEPH_RADOS_API PlacementGroup {
+ PlacementGroup();
+ PlacementGroup(const PlacementGroup&);
+ ~PlacementGroup();
+ bool parse(const char*);
+ std::unique_ptr<PlacementGroupImpl> impl;
+ };
+
+ CEPH_RADOS_API std::ostream& operator<<(std::ostream&, const PlacementGroup&);
+
+ class CEPH_RADOS_API Rados
+ {
+ public:
+ static void version(int *major, int *minor, int *extra);
+
+ Rados();
+ explicit Rados(IoCtx& ioctx);
+ ~Rados();
+ static void from_rados_t(rados_t cluster, Rados &rados);
+
+ int init(const char * const id);
+ int init2(const char * const name, const char * const clustername,
+ uint64_t flags);
+ int init_with_context(config_t cct_);
+ config_t cct();
+ int connect();
+ void shutdown();
+ int watch_flush();
+ int aio_watch_flush(AioCompletion*);
+ int conf_read_file(const char * const path) const;
+ int conf_parse_argv(int argc, const char ** argv) const;
+ int conf_parse_argv_remainder(int argc, const char ** argv,
+ const char ** remargv) const;
+ int conf_parse_env(const char *env) const;
+ int conf_set(const char *option, const char *value);
+ int conf_get(const char *option, std::string &val);
+
+ int service_daemon_register(
+ const std::string& service, ///< service name (e.g., 'rgw')
+ const std::string& name, ///< daemon name (e.g., 'gwfoo')
+ const std::map<std::string,std::string>& metadata); ///< static metadata about daemon
+ int service_daemon_update_status(
+ std::map<std::string,std::string>&& status);
+
+ int pool_create(const char *name);
+ int pool_create(const char *name, uint64_t auid)
+ __attribute__ ((deprecated));
+ int pool_create(const char *name, uint64_t auid, uint8_t crush_rule)
+ __attribute__ ((deprecated));
+ int pool_create_with_rule(const char *name, uint8_t crush_rule);
+ int pool_create_async(const char *name, PoolAsyncCompletion *c);
+ int pool_create_async(const char *name, uint64_t auid, PoolAsyncCompletion *c)
+ __attribute__ ((deprecated));
+ int pool_create_async(const char *name, uint64_t auid, uint8_t crush_rule, PoolAsyncCompletion *c)
+ __attribute__ ((deprecated));
+ int pool_create_with_rule_async(const char *name, uint8_t crush_rule, PoolAsyncCompletion *c);
+ int pool_get_base_tier(int64_t pool, int64_t* base_tier);
+ int pool_delete(const char *name);
+ int pool_delete_async(const char *name, PoolAsyncCompletion *c);
+ int64_t pool_lookup(const char *name);
+ int pool_reverse_lookup(int64_t id, std::string *name);
+
+ uint64_t get_instance_id();
+
+ int get_min_compatible_osd(int8_t* require_osd_release);
+ int get_min_compatible_client(int8_t* min_compat_client,
+ int8_t* require_min_compat_client);
+
+ int mon_command(std::string cmd, const bufferlist& inbl,
+ bufferlist *outbl, std::string *outs);
+ int mgr_command(std::string cmd, const bufferlist& inbl,
+ bufferlist *outbl, std::string *outs);
+ int osd_command(int osdid, std::string cmd, const bufferlist& inbl,
+ bufferlist *outbl, std::string *outs);
+ int pg_command(const char *pgstr, std::string cmd, const bufferlist& inbl,
+ bufferlist *outbl, std::string *outs);
+
+ int ioctx_create(const char *name, IoCtx &pioctx);
+ int ioctx_create2(int64_t pool_id, IoCtx &pioctx);
+
+ // Features useful for test cases
+ void test_blocklist_self(bool set);
+
+ /* pool info */
+ int pool_list(std::list<std::string>& v);
+ int pool_list2(std::list<std::pair<int64_t, std::string> >& v);
+ int get_pool_stats(std::list<std::string>& v,
+ stats_map& result);
+ /// deprecated; use simpler form. categories no longer supported.
+ int get_pool_stats(std::list<std::string>& v,
+ std::map<std::string, stats_map>& stats);
+ /// deprecated; categories no longer supported
+ int get_pool_stats(std::list<std::string>& v,
+ std::string& category,
+ std::map<std::string, stats_map>& stats);
+ /// check if pool has selfmanaged snaps
+ bool get_pool_is_selfmanaged_snaps_mode(const std::string& poolname);
+
+ int cluster_stat(cluster_stat_t& result);
+ int cluster_fsid(std::string *fsid);
+
+ /**
+ * List inconsistent placement groups in the given pool
+ *
+ * @param pool_id the pool id
+ * @param pgs [out] the inconsistent PGs
+ */
+ int get_inconsistent_pgs(int64_t pool_id,
+ std::vector<PlacementGroup>* pgs);
+ /**
+ * List the inconsistent objects found in a given PG by last scrub
+ *
+ * @param pg the placement group returned by @c pg_list()
+ * @param start_after the first returned @c objects
+ * @param max_return the max number of the returned @c objects
+ * @param c what to do when the operation is complete and safe
+ * @param objects [out] the objects where inconsistencies are found
+ * @param interval [in,out] an epoch indicating current interval
+ * @returns if a non-zero @c interval is specified, will return -EAGAIN i
+ * the current interval begin epoch is different.
+ */
+ int get_inconsistent_objects(const PlacementGroup& pg,
+ const object_id_t &start_after,
+ unsigned max_return,
+ AioCompletion *c,
+ std::vector<inconsistent_obj_t>* objects,
+ uint32_t* interval);
+ /**
+ * List the inconsistent snapsets found in a given PG by last scrub
+ *
+ * @param pg the placement group returned by @c pg_list()
+ * @param start_after the first returned @c objects
+ * @param max_return the max number of the returned @c objects
+ * @param c what to do when the operation is complete and safe
+ * @param snapsets [out] the objects where inconsistencies are found
+ * @param interval [in,out] an epoch indicating current interval
+ * @returns if a non-zero @c interval is specified, will return -EAGAIN i
+ * the current interval begin epoch is different.
+ */
+ int get_inconsistent_snapsets(const PlacementGroup& pg,
+ const object_id_t &start_after,
+ unsigned max_return,
+ AioCompletion *c,
+ std::vector<inconsistent_snapset_t>* snapset,
+ uint32_t* interval);
+
+ /// get/wait for the most recent osdmap
+ int wait_for_latest_osdmap();
+
+ int blocklist_add(const std::string& client_address,
+ uint32_t expire_seconds);
+
+ std::string get_addrs() const;
+
+ /*
+ * pool aio
+ *
+ * It is up to the caller to release the completion handler, even if the pool_create_async()
+ * and/or pool_delete_async() fails and does not send the async request
+ */
+ static PoolAsyncCompletion *pool_async_create_completion();
+
+ // -- aio --
+ static AioCompletion *aio_create_completion();
+ static AioCompletion *aio_create_completion(void *cb_arg, callback_t cb_complete,
+ callback_t cb_safe)
+ __attribute__ ((deprecated));
+ static AioCompletion *aio_create_completion(void *cb_arg, callback_t cb_complete);
+
+ friend std::ostream& operator<<(std::ostream &oss, const Rados& r);
+ private:
+ friend class neorados::RADOS;
+
+ // We don't allow assignment or copying
+ Rados(const Rados& rhs);
+ const Rados& operator=(const Rados& rhs);
+ RadosClient *client;
+ };
+
+} // namespace v14_2_0
+} // namespace librados
+
+#endif
+
diff --git a/src/include/rados/librados_fwd.hpp b/src/include/rados/librados_fwd.hpp
new file mode 100644
index 000000000..396f3a838
--- /dev/null
+++ b/src/include/rados/librados_fwd.hpp
@@ -0,0 +1,34 @@
+#ifndef __LIBRADOS_FWD_HPP
+#define __LIBRADOS_FWD_HPP
+
+struct blkin_trace_info;
+
+namespace libradosstriper {
+
+class RadosStriper;
+
+} // namespace libradosstriper
+
+namespace librados {
+inline namespace v14_2_0 {
+
+class AioCompletion;
+class IoCtx;
+class ListObject;
+class NObjectIterator;
+class ObjectCursor;
+class ObjectItem;
+class ObjectOperation;
+class ObjectOperationCompletion;
+class ObjectReadOperation;
+class ObjectWriteOperation;
+class PlacementGroup;
+class PoolAsyncCompletion;
+class Rados;
+class WatchCtx;
+class WatchCtx2;
+
+} // inline namespace v14_2_0
+} // namespace librados
+
+#endif // __LIBRADOS_FWD_HPP
diff --git a/src/include/rados/librgw.h b/src/include/rados/librgw.h
new file mode 100644
index 000000000..c20e96bed
--- /dev/null
+++ b/src/include/rados/librgw.h
@@ -0,0 +1,36 @@
+// -*- 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) 2011 New Dream Network
+ *
+ * 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_LIBRGW_H
+#define CEPH_LIBRGW_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LIBRGW_VER_MAJOR 1
+#define LIBRGW_VER_MINOR 1
+#define LIBRGW_VER_EXTRA 0
+
+#define LIBRGW_VERSION(maj, min, extra) ((maj << 16) + (min << 8) + extra)
+#define LIBRGW_VERSION_CODE LIBRGW_VERSION(LIBRGW_VER_MAJOR, LIBRGW_VER_MINOR, LIBRGW_VER_EXTRA)
+
+typedef void* librgw_t;
+int librgw_create(librgw_t *rgw, int argc, char **argv);
+void librgw_shutdown(librgw_t rgw);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CEPH_LIBRGW_H */
diff --git a/src/include/rados/objclass.h b/src/include/rados/objclass.h
new file mode 100644
index 000000000..80ae69d25
--- /dev/null
+++ b/src/include/rados/objclass.h
@@ -0,0 +1,177 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_OBJCLASS_OBJCLASS_PUBLIC_H
+#define CEPH_OBJCLASS_OBJCLASS_PUBLIC_H
+
+#ifdef __cplusplus
+
+#include "buffer.h"
+
+extern "C" {
+#endif
+
+#define CEPH_CLS_API [[gnu::visibility("default")]]
+
+#define CLS_VER(maj,min) \
+int __cls_ver__## maj ## _ ##min = 0; \
+int __cls_ver_maj = maj; \
+int __cls_ver_min = min;
+
+#define CLS_NAME(name) \
+int __cls_name__## name = 0; \
+const char *__cls_name = #name;
+
+#define CLS_INIT(name) \
+CEPH_CLS_API void __cls_init()
+
+#define CLS_METHOD_RD 0x1 /// method executes read operations
+#define CLS_METHOD_WR 0x2 /// method executes write operations
+#define CLS_METHOD_PROMOTE 0x8 /// method cannot be proxied to base tier
+
+#define CLS_LOG(level, fmt, ...) \
+ cls_log(level, "<cls> %s:%d: " fmt, __FILE__, __LINE__, ##__VA_ARGS__)
+#define CLS_ERR(fmt, ...) CLS_LOG(0, fmt, ##__VA_ARGS__)
+
+/**
+ * Initialize a class.
+ */
+void __cls_init();
+
+/**
+ * @typdef cls_handle_t
+ *
+ * A handle for interacting with the object class.
+ */
+typedef void *cls_handle_t;
+
+/**
+ * @typedef cls_method_handle_t
+ *
+ * A handle for interacting with the method of the object class.
+ */
+typedef void *cls_method_handle_t;
+
+/**
+ * @typedef cls_method_context_t
+ *
+ * A context for the method of the object class.
+ */
+typedef void* cls_method_context_t;
+
+/*class utils*/
+extern int cls_log(int level, const char *format, ...)
+ __attribute__((__format__(printf, 2, 3)));
+
+/* class registration api */
+extern int cls_register(const char *name, cls_handle_t *handle);
+
+#ifdef __cplusplus
+}
+
+/**
+ * @typedef cls_method_cxx_call_t
+ *
+ */
+typedef int (*cls_method_cxx_call_t)(cls_method_context_t ctx,
+ class ceph::buffer::list *inbl, class ceph::buffer::list *outbl);
+
+/**
+ * Register a method.
+ *
+ * @param hclass
+ * @param method
+ * @param flags
+ * @param class_call
+ * @param handle
+ */
+extern int cls_register_cxx_method(cls_handle_t hclass, const char *method, int flags,
+ cls_method_cxx_call_t class_call, cls_method_handle_t *handle);
+
+/**
+ * Create an object.
+ *
+ * @param hctx
+ * @param exclusive
+ */
+extern int cls_cxx_create(cls_method_context_t hctx, bool exclusive);
+
+/**
+ * Remove an object.
+ *
+ * @param hctx
+ */
+extern int cls_cxx_remove(cls_method_context_t hctx);
+
+/**
+ * Check on the status of an object.
+ *
+ * @param hctx
+ * @param size
+ * @param mtime
+ */
+extern int cls_cxx_stat(cls_method_context_t hctx, uint64_t *size, time_t *mtime);
+
+/**
+ * Read contents of an object.
+ *
+ * @param hctx
+ * @param ofs
+ * @param len
+ * @param bl
+ */
+extern int cls_cxx_read(cls_method_context_t hctx, int ofs, int len, ceph::bufferlist *bl);
+
+/**
+ * Write to the object.
+ *
+ * @param hctx
+ * @param ofs
+ * @param len
+ * @param bl
+ */
+extern int cls_cxx_write(cls_method_context_t hctx, int ofs, int len, ceph::bufferlist *bl);
+
+/**
+ * Get xattr of the object.
+ *
+ * @param hctx
+ * @param name
+ * @param outbl
+ */
+extern int cls_cxx_getxattr(cls_method_context_t hctx, const char *name,
+ ceph::bufferlist *outbl);
+
+/**
+ * Set xattr of the object.
+ *
+ * @param hctx
+ * @param name
+ * @param inbl
+ */
+extern int cls_cxx_setxattr(cls_method_context_t hctx, const char *name,
+ ceph::bufferlist *inbl);
+
+/**
+ * Get value corresponding to a key from the map.
+ *
+ * @param hctx
+ * @param key
+ * @param outbl
+ */
+extern int cls_cxx_map_get_val(cls_method_context_t hctx,
+ const std::string &key, ceph::bufferlist *outbl);
+
+/**
+ * Set value corresponding to a key in the map.
+ *
+ * @param hctx
+ * @param key
+ * @param inbl
+ */
+extern int cls_cxx_map_set_val(cls_method_context_t hctx,
+ const std::string &key, ceph::bufferlist *inbl);
+
+#endif
+
+#endif
diff --git a/src/include/rados/page.h b/src/include/rados/page.h
new file mode 120000
index 000000000..cf983e838
--- /dev/null
+++ b/src/include/rados/page.h
@@ -0,0 +1 @@
+../page.h \ No newline at end of file
diff --git a/src/include/rados/rados_types.h b/src/include/rados/rados_types.h
new file mode 100644
index 000000000..d308341ec
--- /dev/null
+++ b/src/include/rados/rados_types.h
@@ -0,0 +1,41 @@
+#ifndef CEPH_RADOS_TYPES_H
+#define CEPH_RADOS_TYPES_H
+
+#include <stdint.h>
+
+/**
+ * @struct obj_watch_t
+ * One item from list_watchers
+ */
+struct obj_watch_t {
+ /// Address of the Watcher
+ char addr[256];
+ /// Watcher ID
+ int64_t watcher_id;
+ /// Cookie
+ uint64_t cookie;
+ /// Timeout in Seconds
+ uint32_t timeout_seconds;
+};
+
+struct notify_ack_t {
+ uint64_t notifier_id;
+ uint64_t cookie;
+ char *payload;
+ uint64_t payload_len;
+};
+
+struct notify_timeout_t {
+ uint64_t notifier_id;
+ uint64_t cookie;
+};
+
+/**
+ *
+ * Pass as nspace argument to rados_ioctx_set_namespace()
+ * before calling rados_nobjects_list_open() to return
+ * all objects in all namespaces.
+ */
+#define LIBRADOS_ALL_NSPACES "\001"
+
+#endif
diff --git a/src/include/rados/rados_types.hpp b/src/include/rados/rados_types.hpp
new file mode 100644
index 000000000..84023579b
--- /dev/null
+++ b/src/include/rados/rados_types.hpp
@@ -0,0 +1,341 @@
+#ifndef CEPH_RADOS_TYPES_HPP
+#define CEPH_RADOS_TYPES_HPP
+
+#include <map>
+#include <utility>
+#include <vector>
+#include <stdint.h>
+#include <string>
+
+#include "buffer.h"
+#include "rados_types.h"
+
+namespace librados {
+
+typedef uint64_t snap_t;
+
+enum {
+ SNAP_HEAD = (uint64_t)(-2),
+ SNAP_DIR = (uint64_t)(-1)
+};
+
+struct clone_info_t {
+ snap_t cloneid;
+ std::vector<snap_t> snaps; // ascending
+ std::vector< std::pair<uint64_t,uint64_t> > overlap; // with next newest
+ uint64_t size;
+ clone_info_t() : cloneid(0), size(0) {}
+};
+
+struct snap_set_t {
+ std::vector<clone_info_t> clones; // ascending
+ snap_t seq; // newest snapid seen by the object
+ snap_set_t() : seq(0) {}
+};
+
+struct object_id_t {
+ std::string name;
+ std::string nspace;
+ std::string locator;
+ snap_t snap = 0;
+ object_id_t() = default;
+ object_id_t(const std::string& name,
+ const std::string& nspace,
+ const std::string& locator,
+ snap_t snap)
+ : name(name),
+ nspace(nspace),
+ locator(locator),
+ snap(snap)
+ {}
+};
+
+struct err_t {
+ enum : uint64_t {
+ SHARD_MISSING = 1 << 1,
+ SHARD_STAT_ERR = 1 << 2,
+ SHARD_READ_ERR = 1 << 3,
+ DATA_DIGEST_MISMATCH_OI = 1 << 9, // Old
+ DATA_DIGEST_MISMATCH_INFO = 1 << 9,
+ OMAP_DIGEST_MISMATCH_OI = 1 << 10, // Old
+ OMAP_DIGEST_MISMATCH_INFO = 1 << 10,
+ SIZE_MISMATCH_OI = 1 << 11, // Old
+ SIZE_MISMATCH_INFO = 1 << 11,
+ SHARD_EC_HASH_MISMATCH = 1 << 12,
+ SHARD_EC_SIZE_MISMATCH = 1 << 13,
+ OI_ATTR_MISSING = 1 << 14, // Old
+ INFO_MISSING = 1 << 14,
+ OI_ATTR_CORRUPTED = 1 << 15, // Old
+ INFO_CORRUPTED = 1 << 15,
+ SS_ATTR_MISSING = 1 << 16, // Old
+ SNAPSET_MISSING = 1 << 16,
+ SS_ATTR_CORRUPTED = 1 << 17, // Old
+ SNAPSET_CORRUPTED = 1 << 17,
+ OBJ_SIZE_OI_MISMATCH = 1 << 18, // Old
+ OBJ_SIZE_INFO_MISMATCH = 1 << 18,
+ HINFO_MISSING = 1 << 19,
+ HINFO_CORRUPTED = 1 << 20
+ // When adding more here add to either SHALLOW_ERRORS or DEEP_ERRORS
+ };
+ uint64_t errors = 0;
+ static constexpr uint64_t SHALLOW_ERRORS = SHARD_MISSING|SHARD_STAT_ERR|SIZE_MISMATCH_INFO|INFO_MISSING|INFO_CORRUPTED|SNAPSET_MISSING|SNAPSET_CORRUPTED|OBJ_SIZE_INFO_MISMATCH|HINFO_MISSING|HINFO_CORRUPTED;
+ static constexpr uint64_t DEEP_ERRORS = SHARD_READ_ERR|DATA_DIGEST_MISMATCH_INFO|OMAP_DIGEST_MISMATCH_INFO|SHARD_EC_HASH_MISMATCH|SHARD_EC_SIZE_MISMATCH;
+ bool has_shard_missing() const {
+ return errors & SHARD_MISSING;
+ }
+ bool has_stat_error() const {
+ return errors & SHARD_STAT_ERR;
+ }
+ bool has_read_error() const {
+ return errors & SHARD_READ_ERR;
+ }
+ bool has_data_digest_mismatch_oi() const { // Compatibility
+ return errors & DATA_DIGEST_MISMATCH_OI;
+ }
+ bool has_data_digest_mismatch_info() const {
+ return errors & DATA_DIGEST_MISMATCH_INFO;
+ }
+ bool has_omap_digest_mismatch_oi() const { // Compatibility
+ return errors & OMAP_DIGEST_MISMATCH_OI;
+ }
+ bool has_omap_digest_mismatch_info() const {
+ return errors & OMAP_DIGEST_MISMATCH_INFO;
+ }
+ bool has_size_mismatch_oi() const { // Compatibility
+ return errors & SIZE_MISMATCH_OI;
+ }
+ bool has_size_mismatch_info() const {
+ return errors & SIZE_MISMATCH_INFO;
+ }
+ bool has_ec_hash_error() const {
+ return errors & SHARD_EC_HASH_MISMATCH;
+ }
+ bool has_ec_size_error() const {
+ return errors & SHARD_EC_SIZE_MISMATCH;
+ }
+ bool has_oi_attr_missing() const { // Compatibility
+ return errors & OI_ATTR_MISSING;
+ }
+ bool has_info_missing() const {
+ return errors & INFO_MISSING;
+ }
+ bool has_oi_attr_corrupted() const { // Compatibility
+ return errors & OI_ATTR_CORRUPTED;
+ }
+ bool has_info_corrupted() const {
+ return errors & INFO_CORRUPTED;
+ }
+ bool has_ss_attr_missing() const { // Compatibility
+ return errors & SS_ATTR_MISSING;
+ }
+ bool has_snapset_missing() const {
+ return errors & SNAPSET_MISSING;
+ }
+ bool has_ss_attr_corrupted() const { // Compatibility
+ return errors & SS_ATTR_CORRUPTED;
+ }
+ bool has_snapset_corrupted() const {
+ return errors & SNAPSET_CORRUPTED;
+ }
+ bool has_shallow_errors() const {
+ return errors & SHALLOW_ERRORS;
+ }
+ bool has_deep_errors() const {
+ return errors & DEEP_ERRORS;
+ }
+ bool has_obj_size_oi_mismatch() const { // Compatibility
+ return errors & OBJ_SIZE_OI_MISMATCH;
+ }
+ bool has_obj_size_info_mismatch() const {
+ return errors & OBJ_SIZE_INFO_MISMATCH;
+ }
+ bool has_hinfo_missing() const {
+ return errors & HINFO_MISSING;
+ }
+ bool has_hinfo_corrupted() const {
+ return errors & HINFO_CORRUPTED;
+ }
+};
+
+struct shard_info_t : err_t {
+ std::map<std::string, ceph::bufferlist> attrs;
+ uint64_t size = -1;
+ bool omap_digest_present = false;
+ uint32_t omap_digest = 0;
+ bool data_digest_present = false;
+ uint32_t data_digest = 0;
+ bool selected_oi = false;
+ bool primary = false;
+};
+
+struct osd_shard_t {
+ int32_t osd;
+ int8_t shard;
+};
+
+inline bool operator<(const osd_shard_t &lhs, const osd_shard_t &rhs) {
+ if (lhs.osd < rhs.osd)
+ return true;
+ else if (lhs.osd > rhs.osd)
+ return false;
+ else
+ return lhs.shard < rhs.shard;
+}
+
+struct obj_err_t {
+ enum : uint64_t {
+ OBJECT_INFO_INCONSISTENCY = 1 << 1,
+ // XXX: Can an older rados binary work if these bits stay the same?
+ DATA_DIGEST_MISMATCH = 1 << 4,
+ OMAP_DIGEST_MISMATCH = 1 << 5,
+ SIZE_MISMATCH = 1 << 6,
+ ATTR_VALUE_MISMATCH = 1 << 7,
+ ATTR_NAME_MISMATCH = 1 << 8,
+ SNAPSET_INCONSISTENCY = 1 << 9,
+ HINFO_INCONSISTENCY = 1 << 10,
+ SIZE_TOO_LARGE = 1 << 11,
+ // When adding more here add to either SHALLOW_ERRORS or DEEP_ERRORS
+ };
+ uint64_t errors = 0;
+ static constexpr uint64_t SHALLOW_ERRORS = OBJECT_INFO_INCONSISTENCY|SIZE_MISMATCH|ATTR_VALUE_MISMATCH
+ |ATTR_NAME_MISMATCH|SNAPSET_INCONSISTENCY|HINFO_INCONSISTENCY|SIZE_TOO_LARGE;
+ static constexpr uint64_t DEEP_ERRORS = DATA_DIGEST_MISMATCH|OMAP_DIGEST_MISMATCH;
+ bool has_object_info_inconsistency() const {
+ return errors & OBJECT_INFO_INCONSISTENCY;
+ }
+ bool has_data_digest_mismatch() const {
+ return errors & DATA_DIGEST_MISMATCH;
+ }
+ bool has_omap_digest_mismatch() const {
+ return errors & OMAP_DIGEST_MISMATCH;
+ }
+ bool has_size_mismatch() const {
+ return errors & SIZE_MISMATCH;
+ }
+ bool has_attr_value_mismatch() const {
+ return errors & ATTR_VALUE_MISMATCH;
+ }
+ bool has_attr_name_mismatch() const {
+ return errors & ATTR_NAME_MISMATCH;
+ }
+ bool has_shallow_errors() const {
+ return errors & SHALLOW_ERRORS;
+ }
+ bool has_deep_errors() const {
+ return errors & DEEP_ERRORS;
+ }
+ bool has_snapset_inconsistency() const {
+ return errors & SNAPSET_INCONSISTENCY;
+ }
+ bool has_hinfo_inconsistency() const {
+ return errors & HINFO_INCONSISTENCY;
+ }
+ bool has_size_too_large() const {
+ return errors & SIZE_TOO_LARGE;
+ }
+};
+
+struct inconsistent_obj_t : obj_err_t {
+ inconsistent_obj_t() = default;
+ inconsistent_obj_t(const object_id_t& object)
+ : object{object}, version(0)
+ {}
+ object_id_t object;
+ uint64_t version; // XXX: Redundant with object info attr
+ std::map<osd_shard_t, shard_info_t> shards;
+ err_t union_shards;
+};
+
+struct inconsistent_snapset_t {
+ inconsistent_snapset_t() = default;
+ inconsistent_snapset_t(const object_id_t& head)
+ : object{head}
+ {}
+ enum {
+ SNAPSET_MISSING = 1 << 0,
+ SNAPSET_CORRUPTED = 1 << 1,
+ CLONE_MISSING = 1 << 2,
+ SNAP_ERROR = 1 << 3,
+ HEAD_MISMATCH = 1 << 4, // Unused
+ HEADLESS_CLONE = 1 << 5,
+ SIZE_MISMATCH = 1 << 6,
+ OI_MISSING = 1 << 7, // Old
+ INFO_MISSING = 1 << 7,
+ OI_CORRUPTED = 1 << 8, // Old
+ INFO_CORRUPTED = 1 << 8,
+ EXTRA_CLONES = 1 << 9,
+ };
+ uint64_t errors = 0;
+ object_id_t object;
+ // Extra clones
+ std::vector<snap_t> clones;
+ std::vector<snap_t> missing;
+ ceph::bufferlist ss_bl;
+
+ bool ss_attr_missing() const { // Compatibility
+ return errors & SNAPSET_MISSING;
+ }
+ bool snapset_missing() const {
+ return errors & SNAPSET_MISSING;
+ }
+ bool ss_attr_corrupted() const { // Compatibility
+ return errors & SNAPSET_CORRUPTED;
+ }
+ bool snapset_corrupted() const {
+ return errors & SNAPSET_CORRUPTED;
+ }
+ bool clone_missing() const {
+ return errors & CLONE_MISSING;
+ }
+ bool snapset_mismatch() const { // Compatibility
+ return errors & SNAP_ERROR;
+ }
+ bool snapset_error() const {
+ return errors & SNAP_ERROR;
+ }
+ bool head_mismatch() const { // Compatibility
+ return false;
+ }
+ bool headless() const {
+ return errors & HEADLESS_CLONE;
+ }
+ bool size_mismatch() const {
+ return errors & SIZE_MISMATCH;
+ }
+ bool oi_attr_missing() const { // Compatibility
+ return errors & OI_MISSING;
+ }
+ bool info_missing() const {
+ return errors & INFO_MISSING;
+ }
+ bool oi_attr_corrupted() const { // Compatibility
+ return errors & OI_CORRUPTED;
+ }
+ bool info_corrupted() const {
+ return errors & INFO_CORRUPTED;
+ }
+ bool extra_clones() const {
+ return errors & EXTRA_CLONES;
+ }
+};
+
+/**
+ * @var all_nspaces
+ * Pass as nspace argument to IoCtx::set_namespace()
+ * before calling nobjects_begin() to iterate
+ * through all objects in all namespaces.
+ */
+const std::string all_nspaces(LIBRADOS_ALL_NSPACES);
+
+struct notify_ack_t {
+ uint64_t notifier_id;
+ uint64_t cookie;
+ ceph::bufferlist payload_bl;
+};
+
+struct notify_timeout_t {
+ uint64_t notifier_id;
+ uint64_t cookie;
+};
+}
+#endif
diff --git a/src/include/rados/rgw_file.h b/src/include/rados/rgw_file.h
new file mode 100644
index 000000000..e1ea45593
--- /dev/null
+++ b/src/include/rados/rgw_file.h
@@ -0,0 +1,431 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * convert RGW commands to file commands
+ *
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * 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 RADOS_RGW_FILE_H
+#define RADOS_RGW_FILE_H
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "librgw.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LIBRGW_FILE_VER_MAJOR 1
+#define LIBRGW_FILE_VER_MINOR 2
+#define LIBRGW_FILE_VER_EXTRA 0
+
+#define LIBRGW_FILE_VERSION(maj, min, extra) ((maj << 16) + (min << 8) + extra)
+#define LIBRGW_FILE_VERSION_CODE LIBRGW_FILE_VERSION(LIBRGW_FILE_VER_MAJOR, LIBRGW_FILE_VER_MINOR, LIBRGW_FILE_VER_EXTRA)
+
+/*
+ * object types
+ */
+enum rgw_fh_type {
+ RGW_FS_TYPE_NIL = 0,
+ RGW_FS_TYPE_FILE,
+ RGW_FS_TYPE_DIRECTORY,
+ RGW_FS_TYPE_SYMBOLIC_LINK,
+};
+
+/*
+ * dynamic allocated handle to support nfs handle
+ */
+
+/* content-addressable hash */
+struct rgw_fh_hk {
+ uint64_t bucket;
+ uint64_t object;
+};
+
+struct rgw_file_handle
+{
+ /* content-addressable hash */
+ struct rgw_fh_hk fh_hk;
+ void *fh_private; /* librgw private data */
+ /* object type */
+ enum rgw_fh_type fh_type;
+};
+
+struct rgw_fs
+{
+ librgw_t rgw;
+ void *fs_private;
+ struct rgw_file_handle* root_fh;
+};
+
+
+/* XXX mount info hypothetical--emulate Unix, support at least
+ * UUID-length fsid */
+struct rgw_statvfs {
+ uint64_t f_bsize; /* file system block size */
+ uint64_t f_frsize; /* fragment size */
+ uint64_t f_blocks; /* size of fs in f_frsize units */
+ uint64_t f_bfree; /* # free blocks */
+ uint64_t f_bavail; /* # free blocks for unprivileged users */
+ uint64_t f_files; /* # inodes */
+ uint64_t f_ffree; /* # free inodes */
+ uint64_t f_favail; /* # free inodes for unprivileged users */
+ uint64_t f_fsid[2]; /* file system ID */
+ uint64_t f_flag; /* mount flags */
+ uint64_t f_namemax; /* maximum filename length */
+};
+
+
+void rgwfile_version(int *major, int *minor, int *extra);
+
+/*
+ lookup object by name (POSIX style)
+*/
+#define RGW_LOOKUP_FLAG_NONE 0x0000
+#define RGW_LOOKUP_FLAG_CREATE 0x0001
+#define RGW_LOOKUP_FLAG_RCB 0x0002 /* readdir callback hint */
+#define RGW_LOOKUP_FLAG_DIR 0x0004
+#define RGW_LOOKUP_FLAG_FILE 0x0008
+
+#define RGW_LOOKUP_TYPE_FLAGS \
+ (RGW_LOOKUP_FLAG_DIR|RGW_LOOKUP_FLAG_FILE)
+
+int rgw_lookup(struct rgw_fs *rgw_fs,
+ struct rgw_file_handle *parent_fh, const char *path,
+ struct rgw_file_handle **fh,
+ struct stat *st, uint32_t mask, uint32_t flags);
+
+/*
+ lookup object by handle (NFS style)
+*/
+int rgw_lookup_handle(struct rgw_fs *rgw_fs, struct rgw_fh_hk *fh_hk,
+ struct rgw_file_handle **fh, uint32_t flags);
+
+/*
+ * release file handle
+ */
+#define RGW_FH_RELE_FLAG_NONE 0x0000
+
+int rgw_fh_rele(struct rgw_fs *rgw_fs, struct rgw_file_handle *fh,
+ uint32_t flags);
+
+/*
+ attach rgw namespace
+*/
+#define RGW_MOUNT_FLAG_NONE 0x0000
+
+int rgw_mount(librgw_t rgw, const char *uid, const char *key,
+ const char *secret, struct rgw_fs **rgw_fs,
+ uint32_t flags);
+
+int rgw_mount2(librgw_t rgw, const char *uid, const char *key,
+ const char *secret, const char *root, struct rgw_fs **rgw_fs,
+ uint32_t flags);
+
+/*
+ register invalidate callbacks
+*/
+#define RGW_REG_INVALIDATE_FLAG_NONE 0x0000
+
+typedef void (*rgw_fh_callback_t)(void *handle, struct rgw_fh_hk fh_hk);
+
+int rgw_register_invalidate(struct rgw_fs *rgw_fs, rgw_fh_callback_t cb,
+ void *arg, uint32_t flags);
+
+/*
+ detach rgw namespace
+*/
+#define RGW_UMOUNT_FLAG_NONE 0x0000
+
+int rgw_umount(struct rgw_fs *rgw_fs, uint32_t flags);
+
+
+/*
+ get filesystem attributes
+*/
+#define RGW_STATFS_FLAG_NONE 0x0000
+
+int rgw_statfs(struct rgw_fs *rgw_fs,
+ struct rgw_file_handle *parent_fh,
+ struct rgw_statvfs *vfs_st,
+ uint32_t flags);
+
+
+/* XXX (get|set)attr mask bits */
+#define RGW_SETATTR_MODE 1
+#define RGW_SETATTR_UID 2
+#define RGW_SETATTR_GID 4
+#define RGW_SETATTR_MTIME 8
+#define RGW_SETATTR_ATIME 16
+#define RGW_SETATTR_SIZE 32
+#define RGW_SETATTR_CTIME 64
+
+/*
+ create file
+*/
+#define RGW_CREATE_FLAG_NONE 0x0000
+
+int rgw_create(struct rgw_fs *rgw_fs, struct rgw_file_handle *parent_fh,
+ const char *name, struct stat *st, uint32_t mask,
+ struct rgw_file_handle **fh, uint32_t posix_flags,
+ uint32_t flags);
+
+/*
+ create a symbolic link
+ */
+#define RGW_CREATELINK_FLAG_NONE 0x0000
+int rgw_symlink(struct rgw_fs *rgw_fs, struct rgw_file_handle *parent_fh,
+ const char *name, const char *link_path, struct stat *st,
+ uint32_t mask, struct rgw_file_handle **fh, uint32_t posix_flags,
+ uint32_t flags);
+
+/*
+ create a new directory
+*/
+#define RGW_MKDIR_FLAG_NONE 0x0000
+
+int rgw_mkdir(struct rgw_fs *rgw_fs,
+ struct rgw_file_handle *parent_fh,
+ const char *name, struct stat *st, uint32_t mask,
+ struct rgw_file_handle **fh, uint32_t flags);
+
+/*
+ rename object
+*/
+#define RGW_RENAME_FLAG_NONE 0x0000
+
+int rgw_rename(struct rgw_fs *rgw_fs,
+ struct rgw_file_handle *olddir, const char* old_name,
+ struct rgw_file_handle *newdir, const char* new_name,
+ uint32_t flags);
+
+/*
+ remove file or directory
+*/
+#define RGW_UNLINK_FLAG_NONE 0x0000
+
+int rgw_unlink(struct rgw_fs *rgw_fs,
+ struct rgw_file_handle *parent_fh, const char* path,
+ uint32_t flags);
+
+/*
+ read directory content
+*/
+typedef int (*rgw_readdir_cb)(const char *name, void *arg, uint64_t offset,
+ struct stat *st, uint32_t mask,
+ uint32_t flags);
+
+#define RGW_READDIR_FLAG_NONE 0x0000
+#define RGW_READDIR_FLAG_DOTDOT 0x0001 /* send dot names */
+
+int rgw_readdir(struct rgw_fs *rgw_fs,
+ struct rgw_file_handle *parent_fh, uint64_t *offset,
+ rgw_readdir_cb rcb, void *cb_arg, bool *eof,
+ uint32_t flags);
+
+/* enumeration continuing from name */
+int rgw_readdir2(struct rgw_fs *rgw_fs,
+ struct rgw_file_handle *parent_fh, const char *name,
+ rgw_readdir_cb rcb, void *cb_arg, bool *eof,
+ uint32_t flags);
+
+/* project offset of dirent name */
+#define RGW_DIRENT_OFFSET_FLAG_NONE 0x0000
+
+int rgw_dirent_offset(struct rgw_fs *rgw_fs,
+ struct rgw_file_handle *parent_fh,
+ const char *name, int64_t *offset,
+ uint32_t flags);
+
+/*
+ get unix attributes for object
+*/
+#define RGW_GETATTR_FLAG_NONE 0x0000
+
+int rgw_getattr(struct rgw_fs *rgw_fs,
+ struct rgw_file_handle *fh, struct stat *st,
+ uint32_t flags);
+
+/*
+ set unix attributes for object
+*/
+#define RGW_SETATTR_FLAG_NONE 0x0000
+
+int rgw_setattr(struct rgw_fs *rgw_fs,
+ struct rgw_file_handle *fh, struct stat *st,
+ uint32_t mask, uint32_t flags);
+
+/*
+ truncate file
+*/
+#define RGW_TRUNCATE_FLAG_NONE 0x0000
+
+int rgw_truncate(struct rgw_fs *rgw_fs,
+ struct rgw_file_handle *fh, uint64_t size,
+ uint32_t flags);
+
+/*
+ open file
+*/
+#define RGW_OPEN_FLAG_NONE 0x0000
+#define RGW_OPEN_FLAG_CREATE 0x0001
+#define RGW_OPEN_FLAG_V3 0x0002 /* ops have v3 semantics */
+#define RGW_OPEN_FLAG_STATELESS 0x0002 /* alias it */
+
+int rgw_open(struct rgw_fs *rgw_fs, struct rgw_file_handle *parent_fh,
+ uint32_t posix_flags, uint32_t flags);
+
+/*
+ close file
+*/
+
+#define RGW_CLOSE_FLAG_NONE 0x0000
+#define RGW_CLOSE_FLAG_RELE 0x0001
+
+int rgw_close(struct rgw_fs *rgw_fs, struct rgw_file_handle *fh,
+ uint32_t flags);
+
+/*
+ read data from file
+*/
+#define RGW_READ_FLAG_NONE 0x0000
+
+int rgw_read(struct rgw_fs *rgw_fs,
+ struct rgw_file_handle *fh, uint64_t offset,
+ size_t length, size_t *bytes_read, void *buffer,
+ uint32_t flags);
+
+/*
+ read symbolic link
+*/
+#define RGW_READLINK_FLAG_NONE 0x0000
+
+int rgw_readlink(struct rgw_fs *rgw_fs,
+ struct rgw_file_handle *fh, uint64_t offset,
+ size_t length, size_t *bytes_read, void *buffer,
+ uint32_t flags);
+
+/*
+ write data to file
+*/
+#define RGW_WRITE_FLAG_NONE 0x0000
+
+int rgw_write(struct rgw_fs *rgw_fs,
+ struct rgw_file_handle *fh, uint64_t offset,
+ size_t length, size_t *bytes_written, void *buffer,
+ uint32_t flags);
+
+#define RGW_UIO_NONE 0x0000
+#define RGW_UIO_GIFT 0x0001
+#define RGW_UIO_FREE 0x0002
+#define RGW_UIO_BUFQ 0x0004
+
+struct rgw_uio;
+typedef void (*rgw_uio_release)(struct rgw_uio *, uint32_t);
+
+/* buffer vector descriptors */
+struct rgw_vio {
+ void *vio_p1;
+ void *vio_u1;
+ void *vio_base;
+ int32_t vio_len;
+};
+
+struct rgw_uio {
+ rgw_uio_release uio_rele;
+ void *uio_p1;
+ void *uio_u1;
+ uint64_t uio_offset;
+ uint64_t uio_resid;
+ uint32_t uio_cnt;
+ uint32_t uio_flags;
+ struct rgw_vio *uio_vio; /* appended vectors */
+};
+
+typedef struct rgw_uio rgw_uio;
+
+int rgw_readv(struct rgw_fs *rgw_fs,
+ struct rgw_file_handle *fh, rgw_uio *uio, uint32_t flags);
+
+int rgw_writev(struct rgw_fs *rgw_fs,
+ struct rgw_file_handle *fh, rgw_uio *uio, uint32_t flags);
+
+/*
+ sync written data
+*/
+#define RGW_FSYNC_FLAG_NONE 0x0000
+
+int rgw_fsync(struct rgw_fs *rgw_fs, struct rgw_file_handle *fh,
+ uint32_t flags);
+
+/*
+ NFS commit operation
+*/
+
+#define RGW_COMMIT_FLAG_NONE 0x0000
+
+int rgw_commit(struct rgw_fs *rgw_fs, struct rgw_file_handle *fh,
+ uint64_t offset, uint64_t length, uint32_t flags);
+
+/*
+ extended attributes
+ */
+typedef struct rgw_xattrstr
+{
+ char *val;
+ uint32_t len;
+} rgw_xattrstr;
+
+typedef struct rgw_xattr
+{
+ rgw_xattrstr key;
+ rgw_xattrstr val;
+} rgw_xattr;
+
+typedef struct rgw_xattrlist
+{
+ rgw_xattr *xattrs;
+ uint32_t xattr_cnt;
+} rgw_xattrlist;
+
+#define RGW_GETXATTR_FLAG_NONE 0x0000
+
+typedef int (*rgw_getxattr_cb)(rgw_xattrlist *attrs, void *arg,
+ uint32_t flags);
+
+int rgw_getxattrs(struct rgw_fs *rgw_fs, struct rgw_file_handle *fh,
+ rgw_xattrlist *attrs, rgw_getxattr_cb cb, void *cb_arg,
+ uint32_t flags);
+
+#define RGW_LSXATTR_FLAG_NONE 0x0000
+#define RGW_LSXATTR_FLAG_STOP 0x0001
+
+int rgw_lsxattrs(struct rgw_fs *rgw_fs, struct rgw_file_handle *fh,
+ rgw_xattrstr *filter_prefix /* unimplemented for now */,
+ rgw_getxattr_cb cb, void *cb_arg, uint32_t flags);
+
+#define RGW_SETXATTR_FLAG_NONE 0x0000
+
+int rgw_setxattrs(struct rgw_fs *rgw_fs, struct rgw_file_handle *fh,
+ rgw_xattrlist *attrs, uint32_t flags);
+
+#define RGW_RMXATTR_FLAG_NONE 0x0000
+
+int rgw_rmxattrs(struct rgw_fs *rgw_fs, struct rgw_file_handle *fh,
+ rgw_xattrlist *attrs, uint32_t flags);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* RADOS_RGW_FILE_H */
diff --git a/src/include/radosstriper/libradosstriper.h b/src/include/radosstriper/libradosstriper.h
new file mode 100644
index 000000000..a35345f7d
--- /dev/null
+++ b/src/include/radosstriper/libradosstriper.h
@@ -0,0 +1,620 @@
+#ifndef CEPH_LIBRADOSSTRIPER_H
+#define CEPH_LIBRADOSSTRIPER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <string.h>
+
+#include "../rados/librados.h"
+
+#define LIBRADOSSTRIPER_VER_MAJOR 0
+#define LIBRADOSSTRIPER_VER_MINOR 0
+#define LIBRADOSSTRIPER_VER_EXTRA 0
+
+#define LIBRADOSSTRIPER_VERSION(maj, min, extra) ((maj << 16) + (min << 8) + extra)
+
+#define LIBRADOSSTRIPER_VERSION_CODE LIBRADOSSTRIPER_VERSION(LIBRADOSSTRIPER_VER_MAJOR, LIBRADOSSTRIPER_VER_MINOR, LIBRADOSSTRIPER_VER_EXTRA)
+
+/**
+ * @typedef rados_striper_t
+ *
+ * A handle for interacting with striped objects in a RADOS cluster.
+ */
+typedef void *rados_striper_t;
+
+/**
+ * @defgroup libradosstriper_h_init Setup and Teardown
+ * These are the first and last functions to that should be called
+ * when using libradosstriper.
+ *
+ * @{
+ */
+
+/**
+ * Creates a rados striper using the given io context
+ * Striper has initially default object layout.
+ * See rados_striper_set_object_layout_*() to change this
+ *
+ * @param ioctx the rados context to use
+ * @param striper where to store the rados striper
+ * @returns 0 on success, negative error code on failure
+ */
+ int rados_striper_create(rados_ioctx_t ioctx,
+ rados_striper_t *striper);
+
+/**
+ * Destroys a rados striper
+ *
+ * @param striper the striper to destroy
+ */
+void rados_striper_destroy(rados_striper_t striper);
+
+/**
+ * Sets the object layout's stripe unit of a rados striper for future objects.
+ * This layout will be used when new objects are created (by writing to them)
+ * Already existing objects will be opened with their own layout.
+ *
+ * @param striper the targeted striper
+ * @param stripe_unit the stripe_unit value of the new object layout
+ * @returns 0 on success, negative error code on failure
+ */
+int rados_striper_set_object_layout_stripe_unit(rados_striper_t striper,
+ unsigned int stripe_unit);
+
+/**
+ * Sets the object layout's stripe count of a rados striper for future objects.
+ * This layout will be used when new objects are created (by writing to them)
+ * Already existing objects will be opened with their own layout.
+ *
+ * @param striper the targeted striper
+ * @param stripe_count the stripe_count value of the new object layout
+ * @returns 0 on success, negative error code on failure
+ */
+int rados_striper_set_object_layout_stripe_count(rados_striper_t striper,
+ unsigned int stripe_count);
+
+/**
+ * Sets the object layout's object_size of a rados striper for future objects.
+ * This layout will be used when new objects are created (by writing to them)
+ * Already existing objects will be opened with their own layout.
+ *
+ * @param striper the targeted striper
+ * @param object_size the object_size value of the new object layout
+ * @returns 0 on success, negative error code on failure
+ */
+int rados_striper_set_object_layout_object_size(rados_striper_t striper,
+ unsigned int object_size);
+
+/** @} init */
+
+/**
+ * @defgroup libradosstriper_h_synch_io Synchronous I/O
+ * Writes are striped to several rados objects which are then
+ * replicated to a number of OSDs based on the configuration
+ * of the pool they are in. These write functions block
+ * until data is in memory on all replicas of the object they're
+ * writing to - they are equivalent to doing the corresponding
+ * asynchronous write, and the calling
+ * rados_striper_ioctx_wait_for_complete().
+ *
+ * @{
+ */
+
+/**
+ * Synchronously write data to a striped object at the specified offset
+ *
+ * @param striper the striper in which the write will occur
+ * @param soid the name of the striped object
+ * @param buf data to write
+ * @param len length of the data, in bytes
+ * @param off byte offset in the object to begin writing at
+ * @returns 0 on success, negative error code on failure
+ * failure
+ */
+int rados_striper_write(rados_striper_t striper,
+ const char *soid,
+ const char *buf,
+ size_t len,
+ uint64_t off);
+
+/**
+ * Synchronously write an entire striped object
+ *
+ * The striped object is filled with the provided data. If the striped object exists,
+ * it is truncated and then written.
+ *
+ * @param striper the striper in which the write will occur
+ * @param soid the name of the striped object
+ * @param buf data to write
+ * @param len length of the data, in bytes
+ * @returns 0 on success, negative error code on failure
+ */
+int rados_striper_write_full(rados_striper_t striper,
+ const char *soid,
+ const char *buf,
+ size_t len);
+
+/**
+ * Append data to an object
+ *
+ * @param striper the striper in which the write will occur
+ * @param soid the name of the striped object
+ * @param buf the data to append
+ * @param len length of buf (in bytes)
+ * @returns 0 on success, negative error code on failure
+ * failure
+ */
+int rados_striper_append(rados_striper_t striper,
+ const char *soid,
+ const char *buf,
+ size_t len);
+
+/**
+ * Synchronously read data from a striped object at the specified offset
+ *
+ * @param striper the striper in which the read will occur
+ * @param soid the name of the striped object
+ * @param buf where to store the results
+ * @param len the number of bytes to read
+ * @param off the offset to start reading from in the object
+ * @returns number of bytes read on success, negative error code on
+ * failure
+ */
+int rados_striper_read(rados_striper_t striper,
+ const char *soid,
+ char *buf,
+ size_t len,
+ uint64_t off);
+
+/**
+ * Synchronously removes a striped object
+ *
+ * @note There is no atomicity of the deletion and the striped
+ * object may be left incomplete if an error is returned (metadata
+ * all present, but some stripes missing)
+ * However, there is a atomicity of the metadata deletion and
+ * the deletion can not happen if any I/O is ongoing (it
+ * will return EBUSY). Identically, no I/O will be able to start
+ * during deletion (same EBUSY return code)
+ * @param striper the striper in which the remove will occur
+ * @param soid the name of the striped object
+ * @returns 0 on success, negative error code on failure
+ */
+int rados_striper_remove(rados_striper_t striper,
+ const char* soid);
+
+/**
+ * Resize an object
+ *
+ * If this enlarges the object, the new area is logically filled with
+ * zeroes. If this shrinks the object, the excess data is removed.
+ *
+ * @note the truncation is not fully atomic. The metadata part is,
+ * so the behavior will be atomic from user point of view when
+ * the object size is reduced. However, in case of failure, old data
+ * may stay around, hidden. They may reappear if the object size is
+ * later grown, instead of the expected 0s. When growing the
+ * object and in case of failure, the new 0 data may not be
+ * fully created. This can lead to ENOENT errors when
+ * writing/reading the missing parts.
+ * @note the truncation can not happen if any I/O is ongoing (it
+ * will return EBUSY). Identically, no I/O will be able to start
+ * during truncation (same EBUSY return code)
+ * @param io the rados context to use
+ * @param soid the name of the striped object
+ * @param size the new size of the object in bytes
+ * @returns 0 on success, negative error code on failure
+ */
+int rados_striper_trunc(rados_striper_t striper, const char *soid, uint64_t size);
+
+/** @} Synchronous I/O */
+
+/**
+ * @defgroup libradosstriper_h_xattrs Xattrs
+ * Extended attributes are stored as extended attributes on the
+ * first rados regular object of the striped object.
+ * Thus, they have the same limitations as the underlying
+ * rados extended attributes.
+ *
+ * @{
+ */
+
+/**
+ * Get the value of an extended attribute on a striped object.
+ *
+ * @param striper the striper in which the getxattr will occur
+ * @param oid name of the striped object
+ * @param name which extended attribute to read
+ * @param buf where to store the result
+ * @param len size of buf in bytes
+ * @returns length of xattr value on success, negative error code on failure
+ */
+int rados_striper_getxattr(rados_striper_t striper,
+ const char *oid,
+ const char *name,
+ char *buf,
+ size_t len);
+
+/**
+ * Set an extended attribute on a striped object.
+ *
+ * @param striper the striper in which the setxattr will occur
+ * @param oid name of the object
+ * @param name which extended attribute to set
+ * @param buf what to store in the xattr
+ * @param len the number of bytes in buf
+ * @returns 0 on success, negative error code on failure
+ */
+int rados_striper_setxattr(rados_striper_t striper,
+ const char *oid,
+ const char *name,
+ const char *buf,
+ size_t len);
+
+/**
+ * Delete an extended attribute from a striped object.
+ *
+ * @param striper the striper in which the rmxattr will occur
+ * @param oid name of the object
+ * @param name which xattr to delete
+ * @returns 0 on success, negative error code on failure
+ */
+int rados_striper_rmxattr(rados_striper_t striper,
+ const char *oid,
+ const char *name);
+
+/**
+ * Start iterating over xattrs on a striped object.
+ *
+ * @post iter is a valid iterator
+ *
+ * @param striper the striper in which the getxattrs will occur
+ * @param oid name of the object
+ * @param iter where to store the iterator
+ * @returns 0 on success, negative error code on failure
+ */
+int rados_striper_getxattrs(rados_striper_t striper,
+ const char *oid,
+ rados_xattrs_iter_t *iter);
+
+/**
+ * Get the next xattr on the striped object
+ *
+ * @pre iter is a valid iterator
+ *
+ * @post name is the NULL-terminated name of the next xattr, and val
+ * contains the value of the xattr, which is of length len. If the end
+ * of the list has been reached, name and val are NULL, and len is 0.
+ *
+ * @param iter iterator to advance
+ * @param name where to store the name of the next xattr
+ * @param val where to store the value of the next xattr
+ * @param len the number of bytes in val
+ * @returns 0 on success, negative error code on failure
+ */
+int rados_striper_getxattrs_next(rados_xattrs_iter_t iter,
+ const char **name,
+ const char **val,
+ size_t *len);
+
+/**
+ * Close the xattr iterator.
+ *
+ * iter should not be used after this is called.
+ *
+ * @param iter the iterator to close
+ */
+void rados_striper_getxattrs_end(rados_xattrs_iter_t iter);
+
+/** @} Xattrs */
+
+/**
+ * Synchronously get object stats (size/mtime)
+ *
+ * @param striper the striper in which the stat will occur
+ * @param soid the id of the striped object
+ * @param psize where to store object size
+ * @param pmtime where to store modification time
+ * @returns 0 on success, negative error code on failure
+ */
+int rados_striper_stat(rados_striper_t striper,
+ const char* soid,
+ uint64_t *psize,
+ time_t *pmtime);
+
+int rados_striper_stat2(rados_striper_t striper,
+ const char* soid,
+ uint64_t *psize,
+ struct timespec *pmtime);
+
+/**
+ * @defgroup libradosstriper_h_asynch_io Asynchronous I/O
+ * Read and write to objects without blocking.
+ *
+ * @{
+ */
+
+/**
+ * @typedef rados_striper_multi_completion_t
+ * Represents the state of a set of asynchronous operations
+ * it contains the aggregated return value once the operations complete
+ * and can be used to block until all operations are complete and/or safe.
+ */
+typedef void *rados_striper_multi_completion_t;
+
+/**
+ * Constructs a multi completion to use with asynchronous operations
+ *
+ * The complete and safe callbacks correspond to operations being
+ * acked and committed, respectively. The callbacks are called in
+ * order of receipt, so the safe callback may be triggered before the
+ * complete callback, and vice versa. This is affected by journalling
+ * on the OSDs.
+ *
+ * @note Read operations only get a complete callback.
+ * @note BUG: this should check for ENOMEM instead of throwing an exception
+ *
+ * @param cb_arg application-defined data passed to the callback functions
+ * @param cb_complete the function to be called when the operation is
+ * in memory on all relpicas
+ * @param cb_safe the function to be called when the operation is on
+ * stable storage on all replicas
+ * @param pc where to store the completion
+ * @returns 0
+ */
+int rados_striper_multi_aio_create_completion(void *cb_arg,
+ rados_callback_t cb_complete,
+ rados_callback_t cb_safe,
+ rados_striper_multi_completion_t *pc);
+
+/**
+ * Block until all operation complete
+ *
+ * This means data is in memory on all replicas.
+ *
+ * @param c operations to wait for
+ * @returns 0
+ */
+void rados_striper_multi_aio_wait_for_complete(rados_striper_multi_completion_t c);
+
+/**
+ * Block until all operation are safe
+ *
+ * This means data is on stable storage on all replicas.
+ *
+ * @param c operations to wait for
+ * @returns 0
+ */
+void rados_striper_multi_aio_wait_for_safe(rados_striper_multi_completion_t c);
+
+/**
+ * Has a multi asynchronous operation completed?
+ *
+ * @warning This does not imply that the complete callback has
+ * finished
+ *
+ * @param c async operations to inspect
+ * @returns whether c is complete
+ */
+int rados_striper_multi_aio_is_complete(rados_striper_multi_completion_t c);
+
+/**
+ * Is a multi asynchronous operation safe?
+ *
+ * @warning This does not imply that the safe callback has
+ * finished
+ *
+ * @param c async operations to inspect
+ * @returns whether c is safe
+ */
+int rados_striper_multi_aio_is_safe(rados_striper_multi_completion_t c);
+
+/**
+ * Block until all operations complete and callback completes
+ *
+ * This means data is in memory on all replicas and can be read.
+ *
+ * @param c operations to wait for
+ * @returns 0
+ */
+void rados_striper_multi_aio_wait_for_complete_and_cb(rados_striper_multi_completion_t c);
+
+/**
+ * Block until all operations are safe and callback has completed
+ *
+ * This means data is on stable storage on all replicas.
+ *
+ * @param c operations to wait for
+ * @returns 0
+ */
+void rados_striper_multi_aio_wait_for_safe_and_cb(rados_striper_multi_completion_t c);
+
+/**
+ * Has a multi asynchronous operation and callback completed
+ *
+ * @param c async operations to inspect
+ * @returns whether c is complete
+ */
+int rados_striper_multi_aio_is_complete_and_cb(rados_striper_multi_completion_t c);
+
+/**
+ * Is a multi asynchronous operation safe and has the callback completed
+ *
+ * @param c async operations to inspect
+ * @returns whether c is safe
+ */
+int rados_striper_multi_aio_is_safe_and_cb(rados_striper_multi_completion_t c);
+
+/**
+ * Get the return value of a multi asychronous operation
+ *
+ * The return value is set when all operations are complete or safe,
+ * whichever comes first.
+ *
+ * @pre The operation is safe or complete
+ *
+ * @note BUG: complete callback may never be called when the safe
+ * message is received before the complete message
+ *
+ * @param c async operations to inspect
+ * @returns aggregated return value of the operations
+ */
+int rados_striper_multi_aio_get_return_value(rados_striper_multi_completion_t c);
+
+/**
+ * Release a multi asynchrnous IO completion
+ *
+ * Call this when you no longer need the completion. It may not be
+ * freed immediately if the operation is not acked and committed.
+ *
+ * @param c multi completion to release
+ */
+void rados_striper_multi_aio_release(rados_striper_multi_completion_t c);
+
+/**
+ * Asynchronously write data to a striped object at the specified offset
+ *
+ * The return value of the completion will be 0 on success, negative
+ * error code on failure.
+ *
+ * @param striper the striper in which the write will occur
+ * @param soid the name of the striped object
+ * @param completion what to do when the write is safe and complete
+ * @param buf data to write
+ * @param len length of the data, in bytes
+ * @param off byte offset in the object to begin writing at
+ * @returns 0 on success, negative error code on
+ * failure
+ */
+int rados_striper_aio_write(rados_striper_t striper,
+ const char *soid,
+ rados_completion_t completion,
+ const char *buf,
+ size_t len,
+ uint64_t off);
+
+/**
+ * Asynchronously appends data to a striped object
+ *
+ * The return value of the completion will be 0 on success, negative
+ * error code on failure.
+ *
+ * @param striper the striper in which the write will occur
+ * @param soid the name of the striped object
+ * @param completion what to do when the write is safe and complete
+ * @param buf data to write
+ * @param len length of the data, in bytes
+ * @returns 0 on success, negative error code on
+ * failure
+ */
+int rados_striper_aio_append(rados_striper_t striper,
+ const char *soid,
+ rados_completion_t completion,
+ const char *buf,
+ size_t len);
+
+/**
+ * Asynchronously fills and object with the provided data.
+ * If the object exists, it is truncated and then written.
+ *
+ * The return value of the completion will be 0 on success, negative
+ * error code on failure.
+ *
+ * @param striper the striper in which the write will occur
+ * @param soid the name of the striped object
+ * @param completion what to do when the write is safe and complete
+ * @param buf data to write
+ * @param len length of the data, in bytes
+ * @returns 0 on success, negative error code on
+ * failure
+ */
+int rados_striper_aio_write_full(rados_striper_t striper,
+ const char *soid,
+ rados_completion_t completion,
+ const char *buf,
+ size_t len);
+
+/**
+ * Asynchronously read data from a striped object at the specified offset
+ *
+ * The return value of the completion will be number of bytes read on
+ * success, negative error code on failure.
+ *
+ * @param striper the striper in which the read will occur
+ * @param soid the name of the striped object
+ * @param completion what to do when the read is safe and complete
+ * @param buf where to store the results
+ * @param len the number of bytes to read
+ * @param off the offset to start reading from in the object
+ * @returns 0 on success, negative error code on
+ * failure
+ */
+int rados_striper_aio_read(rados_striper_t striper,
+ const char *soid,
+ rados_completion_t completion,
+ char *buf,
+ const size_t len,
+ uint64_t off);
+
+/**
+ * Asynchronously removes a striped object
+ *
+ * @note There is no atomicity of the deletion and the striped
+ * object may be left incomplete if an error is returned (metadata
+ * all present, but some stripes missing)
+ * However, there is a atomicity of the metadata deletion and
+ * the deletion can not happen if any I/O is ongoing (it
+ * will return EBUSY). Identically, no I/O will be able to start
+ * during deletion (same EBUSY return code)
+ * @param striper the striper in which the remove will occur
+ * @param soid the name of the striped object
+ * @param completion what to do when the remove is safe and complete
+ * @returns 0 on success, negative error code on failure
+ */
+
+int rados_striper_aio_remove(rados_striper_t striper,
+ const char* soid,
+ rados_completion_t completion);
+
+/**
+ * Block until all pending writes in a striper are safe
+ *
+ * This is not equivalent to calling rados_striper_multi_aio_wait_for_safe() on all
+ * write completions, since this waits for the associated callbacks to
+ * complete as well.
+ *
+ * @param striper the striper in which the flush will occur
+ * @returns 0 on success, negative error code on failure
+*/
+void rados_striper_aio_flush(rados_striper_t striper);
+
+/**
+ * Asynchronously get object stats (size/mtime)
+ *
+ * @param striper the striper in which the stat will occur
+ * @param soid the id of the striped object
+ * @param psize where to store object size
+ * @param pmtime where to store modification time
+ * @param completion what to do when the stats is complete
+ * @returns 0 on success, negative error code on failure
+ */
+int rados_striper_aio_stat(rados_striper_t striper,
+ const char* soid,
+ rados_completion_t completion,
+ uint64_t *psize,
+ time_t *pmtime);
+
+int rados_striper_aio_stat2(rados_striper_t striper,
+ const char* soid,
+ rados_completion_t completion,
+ uint64_t *psize,
+ struct timespec *pmtime);
+/** @} Asynchronous I/O */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/include/radosstriper/libradosstriper.hpp b/src/include/radosstriper/libradosstriper.hpp
new file mode 100644
index 000000000..fb790b0d7
--- /dev/null
+++ b/src/include/radosstriper/libradosstriper.hpp
@@ -0,0 +1,241 @@
+#ifndef __LIBRADOSSTRIPER_HPP
+#define __LIBRADOSSTRIPER_HPP
+
+#include <string.h>
+#include <string>
+#include <map>
+#include "../rados/buffer.h"
+#include "../rados/librados.hpp"
+
+#include "libradosstriper.h"
+
+namespace libradosstriper
+{
+ struct RadosStriperImpl;
+ struct MultiAioCompletionImpl;
+
+ /*
+ * Completion object for multiple asynchronous IO
+ * It allows to internally handle several "requests"
+ */
+ struct MultiAioCompletion {
+ MultiAioCompletion(MultiAioCompletionImpl *pc_) : pc(pc_) {}
+ ~MultiAioCompletion();
+ int set_complete_callback(void *cb_arg, librados::callback_t cb);
+ int set_safe_callback(void *cb_arg, librados::callback_t cb) __attribute__ ((deprecated));
+ void wait_for_complete();
+ void wait_for_safe() __attribute__ ((deprecated));
+ void wait_for_complete_and_cb();
+ void wait_for_safe_and_cb() __attribute__ ((deprecated));
+ bool is_complete();
+ bool is_safe() __attribute__ ((deprecated));
+ bool is_complete_and_cb();
+ bool is_safe_and_cb() __attribute__ ((deprecated));
+ int get_return_value();
+ void release();
+ MultiAioCompletionImpl *pc;
+ };
+
+ /* RadosStriper : This class allows to perform read/writes on striped objects
+ *
+ * Typical use (error checking omitted):
+ *
+ * RadosStriper rs;
+ * RadosStriper.striper_create("my_cluster", rs);
+ * bufferlist bl;
+ * ... put data in bl ...
+ * rs.write(object_name, bl, len, offset);
+ * bufferlist bl2;
+ * rs.read(object_name, &bl2, len, offset);
+ * ...
+ */
+ class RadosStriper
+ {
+ public:
+
+ /*
+ * constructor
+ */
+ RadosStriper();
+
+ /*
+ * builds the C counter part of a RadosStriper
+ */
+ static void to_rados_striper_t(RadosStriper &striper,
+ rados_striper_t *s);
+
+ /*
+ * copy constructor
+ */
+ RadosStriper(const RadosStriper& rs);
+
+ /*
+ * operator=
+ */
+ RadosStriper& operator=(const RadosStriper& rs);
+
+ /*
+ * destructor
+ * Internally calling close() if an object is currently opened
+ */
+ ~RadosStriper();
+
+ /*
+ * create method
+ */
+ static int striper_create(librados::IoCtx& ioctx,
+ RadosStriper *striper);
+
+ /*
+ * set object layout's stripe unit
+ * This layout will be used when new objects are created (by writing to them)
+ * Already existing objects will be opened with their own layout.
+ */
+ int set_object_layout_stripe_unit(unsigned int stripe_unit);
+
+ /*
+ * set object layout's stripe count
+ * This layout will be used when new objects are created (by writing to them)
+ * Already existing objects will be opened with their own layout.
+ */
+ int set_object_layout_stripe_count(unsigned int stripe_count);
+
+ /*
+ * set object layout's object size
+ * This layout will be used when new objects are created (by writing to them)
+ * Already existing objects will be opened with their own layout.
+ */
+ int set_object_layout_object_size(unsigned int object_size);
+
+ /**
+ * Get the value of an extended attribute on a striped object
+ */
+ int getxattr(const std::string& oid, const char *name, ceph::bufferlist& bl);
+
+ /**
+ * Set the value of an extended attribute on a striped object
+ */
+ int setxattr(const std::string& oid, const char *name, ceph::bufferlist& bl);
+
+ /**
+ * Delete an extended attribute from a striped object
+ */
+ int rmxattr(const std::string& oid, const char *name);
+
+ /**
+ * Start iterating over xattrs on a striped object.
+ */
+ int getxattrs(const std::string& oid,
+ std::map<std::string, ceph::bufferlist>& attrset);
+
+ /**
+ * synchronously write to the striped object at the specified offset.
+ * NOTE: this call steals the contents of @param bl.
+ */
+ int write(const std::string& soid, const ceph::bufferlist& bl, size_t len, uint64_t off);
+
+ /**
+ * synchronously fill the striped object with the specified data
+ * NOTE: this call steals the contents of @param bl.
+ */
+ int write_full(const std::string& soid, const ceph::bufferlist& bl);
+
+ /**
+ * synchronously append data to the striped object
+ * NOTE: this call steals the contents of @p bl.
+ */
+ int append(const std::string& soid, const ceph::bufferlist& bl, size_t len);
+
+ /**
+ * asynchronously write to the striped object at the specified offset.
+ * NOTE: this call steals the contents of @p bl.
+ */
+ int aio_write(const std::string& soid, librados::AioCompletion *c, const ceph::bufferlist& bl, size_t len, uint64_t off);
+
+ /**
+ * asynchronously fill the striped object with the specified data
+ * NOTE: this call steals the contents of @p bl.
+ */
+ int aio_write_full(const std::string& soid, librados::AioCompletion *c, const ceph::bufferlist& bl);
+
+ /**
+ * asynchronously append data to the striped object
+ * NOTE: this call steals the contents of @p bl.
+ */
+ int aio_append(const std::string& soid, librados::AioCompletion *c, const ceph::bufferlist& bl, size_t len);
+
+ /**
+ * synchronously read from the striped object at the specified offset.
+ */
+ int read(const std::string& soid, ceph::bufferlist* pbl, size_t len, uint64_t off);
+
+ /**
+ * asynchronously read from the striped object at the specified offset.
+ */
+ int aio_read(const std::string& soid, librados::AioCompletion *c, ceph::bufferlist *pbl, size_t len, uint64_t off);
+
+ /**
+ * synchronously get striped object stats (size/mtime)
+ */
+ int stat(const std::string& soid, uint64_t *psize, time_t *pmtime);
+ int stat2(const std::string& soid, uint64_t *psize, struct timespec *pts);
+
+ /**
+ * asynchronously get striped object stats (size/mtime)
+ */
+ int aio_stat(const std::string& soid, librados::AioCompletion *c,
+ uint64_t *psize, time_t *pmtime);
+ int aio_stat2(const std::string& soid, librados::AioCompletion *c,
+ uint64_t *psize, struct timespec *pts);
+
+ /**
+ * deletes a striped object.
+ * There is no atomicity of the deletion and the striped
+ * object may be left incomplete if an error is returned (metadata
+ * all present, but some stripes missing)
+ * However, there is a atomicity of the metadata deletion and
+ * the deletion can not happen if any I/O is ongoing (it
+ * will return EBUSY). Identically, no I/O will be able to start
+ * during deletion (same EBUSY return code)
+ */
+ int remove(const std::string& soid);
+ int remove(const std::string& soid, int flags);
+
+ /**
+ * asynchronous remove of striped objects
+ * See synchronous version for comments on (lack of) atomicity
+ */
+ int aio_remove(const std::string& soid, librados::AioCompletion *c);
+ int aio_remove(const std::string& soid, librados::AioCompletion *c, int flags);
+
+ /**
+ * Resizes a striped object
+ * the truncation can not happen if any I/O is ongoing (it
+ * will return EBUSY). Identically, no I/O will be able to start
+ * during truncation (same EBUSY return code)
+ */
+ int trunc(const std::string& oid, uint64_t size);
+
+ /**
+ * Wait for all currently pending aio writes to be safe.
+ *
+ * @returns 0 on success, negative error code on failure
+ */
+ int aio_flush();
+
+ /**
+ * creation of multi aio completion objects
+ */
+ static MultiAioCompletion *multi_aio_create_completion();
+ static MultiAioCompletion *multi_aio_create_completion(void *cb_arg,
+ librados::callback_t cb_complete,
+ librados::callback_t cb_safe);
+
+ private:
+ RadosStriperImpl *rados_striper_impl;
+
+ };
+
+}
+
+#endif
diff --git a/src/include/random.h b/src/include/random.h
new file mode 100644
index 000000000..f2e3e37bc
--- /dev/null
+++ b/src/include/random.h
@@ -0,0 +1,301 @@
+// -*- 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) 2017 SUSE LINUX GmbH
+ *
+ * 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_RANDOM_H
+#define CEPH_RANDOM_H 1
+
+#include <mutex>
+#include <random>
+#include <type_traits>
+#include <boost/optional.hpp>
+
+// Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85494
+#ifdef __MINGW32__
+#include <boost/random/random_device.hpp>
+
+using random_device_t = boost::random::random_device;
+#else
+using random_device_t = std::random_device;
+#endif
+
+// Basic random number facility (see N3551 for inspiration):
+namespace ceph::util {
+
+inline namespace version_1_0_3 {
+
+namespace detail {
+
+template <typename T0, typename T1>
+using larger_of = typename std::conditional<
+ sizeof(T0) >= sizeof(T1),
+ T0, T1>
+ ::type;
+
+// avoid mixing floating point and integers:
+template <typename NumberT0, typename NumberT1>
+using has_compatible_numeric_types =
+ std::disjunction<
+ std::conjunction<
+ std::is_floating_point<NumberT0>, std::is_floating_point<NumberT1>
+ >,
+ std::conjunction<
+ std::is_integral<NumberT0>, std::is_integral<NumberT1>
+ >
+ >;
+
+
+// Select the larger of type compatible numeric types:
+template <typename NumberT0, typename NumberT1>
+using select_number_t = std::enable_if_t<detail::has_compatible_numeric_types<NumberT0, NumberT1>::value,
+ detail::larger_of<NumberT0, NumberT1>>;
+
+} // namespace detail
+
+namespace detail {
+
+// Choose default distribution for appropriate types:
+template <typename NumberT,
+ bool IsIntegral>
+struct select_distribution
+{
+ using type = std::uniform_int_distribution<NumberT>;
+};
+
+template <typename NumberT>
+struct select_distribution<NumberT, false>
+{
+ using type = std::uniform_real_distribution<NumberT>;
+};
+
+template <typename NumberT>
+using default_distribution = typename
+ select_distribution<NumberT, std::is_integral<NumberT>::value>::type;
+
+} // namespace detail
+
+namespace detail {
+
+template <typename EngineT>
+EngineT& engine();
+
+template <typename MutexT, typename EngineT,
+ typename SeedT = typename EngineT::result_type>
+void randomize_rng(const SeedT seed, MutexT& m, EngineT& e)
+{
+ std::lock_guard<MutexT> lg(m);
+ e.seed(seed);
+}
+
+template <typename MutexT, typename EngineT>
+void randomize_rng(MutexT& m, EngineT& e)
+{
+ random_device_t rd;
+
+ std::lock_guard<MutexT> lg(m);
+ e.seed(rd());
+}
+
+template <typename EngineT = std::default_random_engine,
+ typename SeedT = typename EngineT::result_type>
+void randomize_rng(const SeedT n)
+{
+ detail::engine<EngineT>().seed(n);
+}
+
+template <typename EngineT = std::default_random_engine>
+void randomize_rng()
+{
+ random_device_t rd;
+ detail::engine<EngineT>().seed(rd());
+}
+
+template <typename EngineT>
+EngineT& engine()
+{
+ thread_local boost::optional<EngineT> rng_engine;
+
+ if (!rng_engine) {
+ rng_engine.emplace(EngineT());
+ randomize_rng<EngineT>();
+ }
+
+ return *rng_engine;
+}
+
+} // namespace detail
+
+namespace detail {
+
+template <typename NumberT,
+ typename DistributionT = detail::default_distribution<NumberT>,
+ typename EngineT>
+NumberT generate_random_number(const NumberT min, const NumberT max,
+ EngineT& e)
+{
+ DistributionT d { min, max };
+
+ using param_type = typename DistributionT::param_type;
+ return d(e, param_type { min, max });
+}
+
+template <typename NumberT,
+ typename MutexT,
+ typename DistributionT = detail::default_distribution<NumberT>,
+ typename EngineT>
+NumberT generate_random_number(const NumberT min, const NumberT max,
+ MutexT& m, EngineT& e)
+{
+ DistributionT d { min, max };
+
+ using param_type = typename DistributionT::param_type;
+
+ std::lock_guard<MutexT> lg(m);
+ return d(e, param_type { min, max });
+}
+
+template <typename NumberT,
+ typename DistributionT = detail::default_distribution<NumberT>,
+ typename EngineT>
+NumberT generate_random_number(const NumberT min, const NumberT max)
+{
+ return detail::generate_random_number<NumberT, DistributionT, EngineT>
+ (min, max, detail::engine<EngineT>());
+}
+
+template <typename MutexT,
+ typename EngineT,
+ typename NumberT = int,
+ typename DistributionT = detail::default_distribution<NumberT>>
+NumberT generate_random_number(MutexT& m, EngineT& e)
+{
+ return detail::generate_random_number<NumberT, MutexT, DistributionT, EngineT>
+ (0, std::numeric_limits<NumberT>::max(), m, e);
+}
+
+template <typename NumberT, typename MutexT, typename EngineT>
+NumberT generate_random_number(const NumberT max, MutexT& m, EngineT& e)
+{
+ return generate_random_number<NumberT>(0, max, m, e);
+}
+
+} // namespace detail
+
+template <typename EngineT = std::default_random_engine>
+void randomize_rng()
+{
+ detail::randomize_rng<EngineT>();
+}
+
+template <typename NumberT = int,
+ typename DistributionT = detail::default_distribution<NumberT>,
+ typename EngineT = std::default_random_engine>
+NumberT generate_random_number()
+{
+ return detail::generate_random_number<NumberT, DistributionT, EngineT>
+ (0, std::numeric_limits<NumberT>::max());
+}
+
+template <typename NumberT0, typename NumberT1,
+ typename NumberT = detail::select_number_t<NumberT0, NumberT1>
+ >
+NumberT generate_random_number(const NumberT0 min, const NumberT1 max)
+{
+ return detail::generate_random_number<NumberT,
+ detail::default_distribution<NumberT>,
+ std::default_random_engine>
+ (static_cast<NumberT>(min), static_cast<NumberT>(max));
+}
+
+template <typename NumberT0, typename NumberT1,
+ typename DistributionT,
+ typename EngineT,
+ typename NumberT = detail::select_number_t<NumberT0, NumberT1>
+ >
+NumberT generate_random_number(const NumberT min, const NumberT max,
+ EngineT& e)
+{
+ return detail::generate_random_number<NumberT,
+ DistributionT,
+ EngineT>(static_cast<NumberT>(min), static_cast<NumberT>(max), e);
+}
+
+template <typename NumberT>
+NumberT generate_random_number(const NumberT max)
+{
+ return generate_random_number<NumberT>(0, max);
+}
+
+// Function object:
+template <typename NumberT>
+class random_number_generator final
+{
+ std::mutex l;
+ random_device_t rd;
+ std::default_random_engine e;
+
+ using seed_type = typename decltype(e)::result_type;
+
+ public:
+ using number_type = NumberT;
+ using random_engine_type = decltype(e);
+ using random_device_type = decltype(rd);
+
+ public:
+ random_device_type& random_device() noexcept { return rd; }
+ random_engine_type& random_engine() noexcept { return e; }
+
+ public:
+ random_number_generator() {
+ detail::randomize_rng(l, e);
+ }
+
+ explicit random_number_generator(const seed_type seed) {
+ detail::randomize_rng(seed, l, e);
+ }
+
+ random_number_generator(random_number_generator&& rhs)
+ : e(std::move(rhs.e))
+ {}
+
+ public:
+ random_number_generator(const random_number_generator&) = delete;
+ random_number_generator& operator=(const random_number_generator&) = delete;
+
+ public:
+ NumberT operator()() {
+ return detail::generate_random_number(l, e);
+ }
+
+ NumberT operator()(const NumberT max) {
+ return detail::generate_random_number<NumberT>(max, l, e);
+ }
+
+ NumberT operator()(const NumberT min, const NumberT max) {
+ return detail::generate_random_number<NumberT>(min, max, l, e);
+ }
+
+ public:
+ void seed(const seed_type n) {
+ detail::randomize_rng(n, l, e);
+ }
+};
+
+template <typename NumberT>
+random_number_generator(const NumberT max) -> random_number_generator<NumberT>;
+
+} // inline namespace version_*
+
+} // namespace ceph::util
+
+#endif
diff --git a/src/include/rangeset.h b/src/include/rangeset.h
new file mode 100644
index 000000000..e7e3d047c
--- /dev/null
+++ b/src/include/rangeset.h
@@ -0,0 +1,250 @@
+// -*- 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-2006 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_RANGESET_H
+#define CEPH_RANGESET_H
+
+/*
+ *
+ * my first container with iterator! it's pretty ugly.
+ *
+ */
+
+#include <map>
+
+//typedef int T;
+
+template <class T>
+struct _rangeset_base {
+ map<T,T> ranges; // pair(first,last) (inclusive, e.g. [first,last])
+
+ typedef typename map<T,T>::iterator mapit;
+
+ // get iterator for range including val. or ranges.end().
+ mapit get_range_for(T val) {
+ mapit it = ranges.lower_bound(val);
+ if (it == ranges.end()) {
+ // search backwards
+ typename map<T,T>::reverse_iterator it = ranges.rbegin();
+ if (it == ranges.rend()) return ranges.end();
+ if (it->first <= val && it->second >= val)
+ return ranges.find(it->first);
+ return ranges.end();
+ } else {
+ if (it->first == val) return
+ it--;
+ if (it->first <= val && it->second >= val)
+ return it;
+ return ranges.end();
+ }
+ }
+
+};
+
+
+template <class T>
+class rangeset_iterator :
+ public std::iterator<std::input_iterator_tag, T>
+{
+ //typedef typename map<T,T>::iterator mapit;
+
+ map<T,T> ranges;
+ typename map<T,T>::iterator it;
+ T current;
+
+public:
+ // cons
+ rangeset_iterator() {}
+
+ rangeset_iterator(typename map<T,T>::iterator& it, map<T,T>& ranges) {
+ this->ranges = ranges;
+ this->it = it;
+ if (this->it != ranges.end())
+ current = it->first;
+ }
+
+ bool operator==(rangeset_iterator<T> rit) {
+ return (it == rit.it && rit.current == current);
+ }
+ bool operator!=(rangeset_iterator<T> rit) {
+ return (it != rit.it) || (rit.current != current);
+ }
+
+ T& operator*() {
+ return current;
+ }
+
+ rangeset_iterator<T> operator++(int) {
+ if (current < it->second)
+ current++;
+ else {
+ it++;
+ if (it != ranges.end())
+ current = it->first;
+ }
+
+ return *this;
+ }
+};
+
+
+template <class T>
+class rangeset
+{
+ typedef typename map<T,T>::iterator map_iterator;
+
+ _rangeset_base<T> theset;
+ inodeno_t _size;
+
+public:
+ rangeset() { _size = 0; }
+ typedef rangeset_iterator<T> iterator;
+
+ iterator begin() {
+ map_iterator it = theset.ranges.begin();
+ return iterator(it, theset.ranges);
+ }
+
+ iterator end() {
+ map_iterator it = theset.ranges.end();
+ return iterator(it, theset.ranges);
+ }
+
+ map_iterator map_begin() {
+ return theset.ranges.begin();
+ }
+ map_iterator map_end() {
+ return theset.ranges.end();
+ }
+ int map_size() {
+ return theset.ranges.size();
+ }
+
+ void map_insert(T v1, T v2) {
+ theset.ranges.insert(pair<T,T>(v1,v2));
+ _size += v2 - v1+1;
+ }
+
+
+ // ...
+ bool contains(T val) {
+ if (theset.get_range_for(val) == theset.ranges.end()) return false;
+ ceph_assert(!empty());
+ return true;
+ }
+
+ void insert(T val) {
+ ceph_assert(!contains(val));
+
+ map_iterator left = theset.get_range_for(val-1);
+ map_iterator right = theset.get_range_for(val+1);
+
+ if (left != theset.ranges.end() &&
+ right != theset.ranges.end()) {
+ // join!
+ left->second = right->second;
+ theset.ranges.erase(right);
+ _size++;
+ return;
+ }
+
+ if (left != theset.ranges.end()) {
+ // add to left range
+ left->second = val;
+ _size++;
+ return;
+ }
+
+ if (right != theset.ranges.end()) {
+ // add to right range
+ theset.ranges.insert(pair<T,T>(val, right->second));
+ theset.ranges.erase(val+1);
+ _size++;
+ return;
+ }
+
+ // new range
+ theset.ranges.insert(pair<T,T>(val,val));
+ _size++;
+ return;
+ }
+
+ unsigned size() {
+ return size();
+ }
+
+ bool empty() {
+ if (theset.ranges.empty()) {
+ ceph_assert(_size == 0);
+ return true;
+ }
+ ceph_assert(_size>0);
+ return false;
+ }
+
+
+ T first() {
+ ceph_assert(!empty());
+ map_iterator it = theset.ranges.begin();
+ return it->first;
+ }
+
+ void erase(T val) {
+ ceph_assert(contains(val));
+ map_iterator it = theset.get_range_for(val);
+ ceph_assert(it != theset.ranges.end());
+
+ // entire range
+ if (val == it->first && val == it->second) {
+ theset.ranges.erase(it);
+ _size--;
+ return;
+ }
+
+ // beginning
+ if (val == it->first) {
+ theset.ranges.insert(pair<T,T>(val+1, it->second));
+ theset.ranges.erase(it);
+ _size--;
+ return;
+ }
+
+ // end
+ if (val == it->second) {
+ it->second = val-1;
+ _size--;
+ return;
+ }
+
+ // middle split
+ theset.ranges.insert(pair<T,T>(it->first, val-1));
+ theset.ranges.insert(pair<T,T>(val+1, it->second));
+ theset.ranges.erase(it);
+ _size--;
+ return;
+ }
+
+ void dump() {
+ for (typename map<T,T>::iterator it = theset.ranges.begin();
+ it != theset.ranges.end();
+ it++) {
+ cout << " " << it->first << "-" << it->second << endl;
+ }
+ }
+
+};
+
+
+#endif
diff --git a/src/include/rbd/features.h b/src/include/rbd/features.h
new file mode 100644
index 000000000..31c73b38f
--- /dev/null
+++ b/src/include/rbd/features.h
@@ -0,0 +1,121 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_RBD_FEATURES_H
+#define CEPH_RBD_FEATURES_H
+
+#define RBD_FEATURE_LAYERING (1ULL<<0)
+#define RBD_FEATURE_STRIPINGV2 (1ULL<<1)
+#define RBD_FEATURE_EXCLUSIVE_LOCK (1ULL<<2)
+#define RBD_FEATURE_OBJECT_MAP (1ULL<<3)
+#define RBD_FEATURE_FAST_DIFF (1ULL<<4)
+#define RBD_FEATURE_DEEP_FLATTEN (1ULL<<5)
+#define RBD_FEATURE_JOURNALING (1ULL<<6)
+#define RBD_FEATURE_DATA_POOL (1ULL<<7)
+#define RBD_FEATURE_OPERATIONS (1ULL<<8)
+#define RBD_FEATURE_MIGRATING (1ULL<<9)
+#define RBD_FEATURE_NON_PRIMARY (1ULL<<10)
+#define RBD_FEATURE_DIRTY_CACHE (1ULL<<11)
+
+#define RBD_FEATURES_DEFAULT (RBD_FEATURE_LAYERING | \
+ RBD_FEATURE_EXCLUSIVE_LOCK | \
+ RBD_FEATURE_OBJECT_MAP | \
+ RBD_FEATURE_FAST_DIFF | \
+ RBD_FEATURE_DEEP_FLATTEN)
+
+#define RBD_FEATURE_NAME_LAYERING "layering"
+#define RBD_FEATURE_NAME_STRIPINGV2 "striping"
+#define RBD_FEATURE_NAME_EXCLUSIVE_LOCK "exclusive-lock"
+#define RBD_FEATURE_NAME_OBJECT_MAP "object-map"
+#define RBD_FEATURE_NAME_FAST_DIFF "fast-diff"
+#define RBD_FEATURE_NAME_DEEP_FLATTEN "deep-flatten"
+#define RBD_FEATURE_NAME_JOURNALING "journaling"
+#define RBD_FEATURE_NAME_DATA_POOL "data-pool"
+#define RBD_FEATURE_NAME_OPERATIONS "operations"
+#define RBD_FEATURE_NAME_MIGRATING "migrating"
+#define RBD_FEATURE_NAME_NON_PRIMARY "non-primary"
+#define RBD_FEATURE_NAME_DIRTY_CACHE "dirty-cache"
+
+/// features that make an image inaccessible for read or write by
+/// clients that don't understand them
+#define RBD_FEATURES_INCOMPATIBLE (RBD_FEATURE_LAYERING | \
+ RBD_FEATURE_STRIPINGV2 | \
+ RBD_FEATURE_DATA_POOL | \
+ RBD_FEATURE_DIRTY_CACHE)
+
+/// features that make an image unwritable by clients that don't understand them
+#define RBD_FEATURES_RW_INCOMPATIBLE (RBD_FEATURES_INCOMPATIBLE | \
+ RBD_FEATURE_EXCLUSIVE_LOCK | \
+ RBD_FEATURE_OBJECT_MAP | \
+ RBD_FEATURE_FAST_DIFF | \
+ RBD_FEATURE_DEEP_FLATTEN | \
+ RBD_FEATURE_JOURNALING | \
+ RBD_FEATURE_OPERATIONS | \
+ RBD_FEATURE_MIGRATING | \
+ RBD_FEATURE_NON_PRIMARY)
+
+#define RBD_FEATURES_ALL (RBD_FEATURE_LAYERING | \
+ RBD_FEATURE_STRIPINGV2 | \
+ RBD_FEATURE_EXCLUSIVE_LOCK | \
+ RBD_FEATURE_OBJECT_MAP | \
+ RBD_FEATURE_FAST_DIFF | \
+ RBD_FEATURE_DEEP_FLATTEN | \
+ RBD_FEATURE_JOURNALING | \
+ RBD_FEATURE_DATA_POOL | \
+ RBD_FEATURE_OPERATIONS | \
+ RBD_FEATURE_MIGRATING | \
+ RBD_FEATURE_NON_PRIMARY | \
+ RBD_FEATURE_DIRTY_CACHE)
+
+/// features that may be dynamically enabled or disabled
+#define RBD_FEATURES_MUTABLE (RBD_FEATURE_EXCLUSIVE_LOCK | \
+ RBD_FEATURE_OBJECT_MAP | \
+ RBD_FEATURE_FAST_DIFF | \
+ RBD_FEATURE_JOURNALING | \
+ RBD_FEATURE_NON_PRIMARY | \
+ RBD_FEATURE_DIRTY_CACHE)
+
+#define RBD_FEATURES_MUTABLE_INTERNAL (RBD_FEATURE_NON_PRIMARY | \
+ RBD_FEATURE_DIRTY_CACHE)
+
+/// features that may be dynamically disabled
+#define RBD_FEATURES_DISABLE_ONLY (RBD_FEATURE_DEEP_FLATTEN)
+
+/// features that only work when used with a single client
+/// using the image for writes
+#define RBD_FEATURES_SINGLE_CLIENT (RBD_FEATURE_EXCLUSIVE_LOCK | \
+ RBD_FEATURE_OBJECT_MAP | \
+ RBD_FEATURE_FAST_DIFF | \
+ RBD_FEATURE_JOURNALING | \
+ RBD_FEATURE_DIRTY_CACHE)
+
+/// features that will be implicitly enabled
+#define RBD_FEATURES_IMPLICIT_ENABLE (RBD_FEATURE_STRIPINGV2 | \
+ RBD_FEATURE_DATA_POOL | \
+ RBD_FEATURE_FAST_DIFF | \
+ RBD_FEATURE_OPERATIONS | \
+ RBD_FEATURE_MIGRATING | \
+ RBD_FEATURE_NON_PRIMARY | \
+ RBD_FEATURE_DIRTY_CACHE)
+
+/// features that cannot be controlled by the user
+#define RBD_FEATURES_INTERNAL (RBD_FEATURE_OPERATIONS | \
+ RBD_FEATURE_MIGRATING)
+
+#define RBD_OPERATION_FEATURE_CLONE_PARENT (1ULL<<0)
+#define RBD_OPERATION_FEATURE_CLONE_CHILD (1ULL<<1)
+#define RBD_OPERATION_FEATURE_GROUP (1ULL<<2)
+#define RBD_OPERATION_FEATURE_SNAP_TRASH (1ULL<<3)
+
+#define RBD_OPERATION_FEATURE_NAME_CLONE_PARENT "clone-parent"
+#define RBD_OPERATION_FEATURE_NAME_CLONE_CHILD "clone-child"
+#define RBD_OPERATION_FEATURE_NAME_GROUP "group"
+#define RBD_OPERATION_FEATURE_NAME_SNAP_TRASH "snap-trash"
+
+/// all valid operation features
+#define RBD_OPERATION_FEATURES_ALL (RBD_OPERATION_FEATURE_CLONE_PARENT | \
+ RBD_OPERATION_FEATURE_CLONE_CHILD | \
+ RBD_OPERATION_FEATURE_GROUP | \
+ RBD_OPERATION_FEATURE_SNAP_TRASH)
+
+#endif
diff --git a/src/include/rbd/librbd.h b/src/include/rbd/librbd.h
new file mode 100644
index 000000000..7ae20e4dd
--- /dev/null
+++ b/src/include/rbd/librbd.h
@@ -0,0 +1,1549 @@
+// -*- 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) 2011 New Dream Network
+ *
+ * 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_LIBRBD_H
+#define CEPH_LIBRBD_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <netinet/in.h>
+#if defined(__linux__)
+#include <linux/types.h>
+#elif defined(__FreeBSD__)
+#include <sys/types.h>
+#endif
+#include <stdbool.h>
+#include <string.h>
+#include <sys/uio.h>
+#include "../rados/librados.h"
+#include "features.h"
+
+#define LIBRBD_VER_MAJOR 1
+#define LIBRBD_VER_MINOR 18
+#define LIBRBD_VER_EXTRA 0
+
+#define LIBRBD_VERSION(maj, min, extra) ((maj << 16) + (min << 8) + extra)
+
+#define LIBRBD_VERSION_CODE LIBRBD_VERSION(LIBRBD_VER_MAJOR, LIBRBD_VER_MINOR, LIBRBD_VER_EXTRA)
+
+#define LIBRBD_SUPPORTS_AIO_FLUSH 1
+#define LIBRBD_SUPPORTS_AIO_OPEN 1
+#define LIBRBD_SUPPORTS_COMPARE_AND_WRITE 1
+#define LIBRBD_SUPPORTS_COMPARE_AND_WRITE_IOVEC 1
+#define LIBRBD_SUPPORTS_LOCKING 1
+#define LIBRBD_SUPPORTS_INVALIDATE 1
+#define LIBRBD_SUPPORTS_IOVEC 1
+#define LIBRBD_SUPPORTS_WATCH 0
+#define LIBRBD_SUPPORTS_WRITESAME 1
+#define LIBRBD_SUPPORTS_WRITE_ZEROES 1
+#define LIBRBD_SUPPORTS_ENCRYPTION 1
+#define LIBRBD_SUPPORTS_ENCRYPTION_LOAD2 1
+
+#if __GNUC__ >= 4
+ #define CEPH_RBD_API __attribute__ ((visibility ("default")))
+ #define CEPH_RBD_DEPRECATED __attribute__((deprecated))
+ #pragma GCC diagnostic push
+ #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#else
+ #define CEPH_RBD_API
+ #define CEPH_RBD_DEPRECATED
+#endif
+
+#define RBD_FLAG_OBJECT_MAP_INVALID (1<<0)
+#define RBD_FLAG_FAST_DIFF_INVALID (1<<1)
+
+#define RBD_MIRROR_IMAGE_STATUS_LOCAL_MIRROR_UUID ""
+
+typedef void *rbd_image_t;
+typedef void *rbd_image_options_t;
+typedef void *rbd_pool_stats_t;
+
+typedef void *rbd_completion_t;
+typedef void (*rbd_callback_t)(rbd_completion_t cb, void *arg);
+
+typedef int (*librbd_progress_fn_t)(uint64_t offset, uint64_t total, void *ptr);
+
+typedef void (*rbd_update_callback_t)(void *arg);
+
+typedef enum {
+ RBD_SNAP_NAMESPACE_TYPE_USER = 0,
+ RBD_SNAP_NAMESPACE_TYPE_GROUP = 1,
+ RBD_SNAP_NAMESPACE_TYPE_TRASH = 2,
+ RBD_SNAP_NAMESPACE_TYPE_MIRROR = 3,
+} rbd_snap_namespace_type_t;
+
+typedef struct {
+ char *id;
+ char *name;
+} rbd_image_spec_t;
+
+typedef struct {
+ int64_t pool_id;
+ char *pool_name;
+ char *pool_namespace;
+ char *image_id;
+ char *image_name;
+ bool trash;
+} rbd_linked_image_spec_t;
+
+typedef struct {
+ uint64_t id;
+ rbd_snap_namespace_type_t namespace_type;
+ char *name;
+} rbd_snap_spec_t;
+
+typedef struct {
+ uint64_t id;
+ uint64_t size;
+ const char *name;
+} rbd_snap_info_t;
+
+typedef struct {
+ const char *pool_name;
+ const char *image_name;
+ const char *image_id;
+ bool trash;
+} rbd_child_info_t;
+
+#define RBD_MAX_IMAGE_NAME_SIZE 96
+#define RBD_MAX_BLOCK_NAME_SIZE 24
+
+#define RBD_SNAP_CREATE_SKIP_QUIESCE (1 << 0)
+#define RBD_SNAP_CREATE_IGNORE_QUIESCE_ERROR (1 << 1)
+
+#define RBD_SNAP_REMOVE_UNPROTECT (1 << 0)
+#define RBD_SNAP_REMOVE_FLATTEN (1 << 1)
+#define RBD_SNAP_REMOVE_FORCE (RBD_SNAP_REMOVE_UNPROTECT | RBD_SNAP_REMOVE_FLATTEN)
+
+/**
+ * These types used to in set_image_notification to indicate the type of event
+ * socket passed in.
+ */
+enum {
+ EVENT_TYPE_PIPE = 1,
+ EVENT_TYPE_EVENTFD = 2
+};
+
+typedef struct {
+ uint64_t size;
+ uint64_t obj_size;
+ uint64_t num_objs;
+ int order;
+ char block_name_prefix[RBD_MAX_BLOCK_NAME_SIZE]; /* deprecated */
+ int64_t parent_pool; /* deprecated */
+ char parent_name[RBD_MAX_IMAGE_NAME_SIZE]; /* deprecated */
+} rbd_image_info_t;
+
+typedef enum {
+ RBD_MIRROR_MODE_DISABLED, /* mirroring is disabled */
+ RBD_MIRROR_MODE_IMAGE, /* mirroring enabled on a per-image basis */
+ RBD_MIRROR_MODE_POOL /* mirroring enabled on all journaled images */
+} rbd_mirror_mode_t;
+
+typedef enum {
+ RBD_MIRROR_PEER_DIRECTION_RX = 0,
+ RBD_MIRROR_PEER_DIRECTION_TX = 1,
+ RBD_MIRROR_PEER_DIRECTION_RX_TX = 2
+} rbd_mirror_peer_direction_t;
+
+typedef struct {
+ char *uuid;
+ char *cluster_name;
+ char *client_name;
+} rbd_mirror_peer_t CEPH_RBD_DEPRECATED;
+
+typedef struct {
+ char *uuid;
+ rbd_mirror_peer_direction_t direction;
+ char *site_name;
+ char *mirror_uuid;
+ char *client_name;
+ time_t last_seen;
+} rbd_mirror_peer_site_t;
+
+#define RBD_MIRROR_PEER_ATTRIBUTE_NAME_MON_HOST "mon_host"
+#define RBD_MIRROR_PEER_ATTRIBUTE_NAME_KEY "key"
+
+typedef enum {
+ RBD_MIRROR_IMAGE_MODE_JOURNAL = 0,
+ RBD_MIRROR_IMAGE_MODE_SNAPSHOT = 1,
+} rbd_mirror_image_mode_t;
+
+typedef enum {
+ RBD_MIRROR_IMAGE_DISABLING = 0,
+ RBD_MIRROR_IMAGE_ENABLED = 1,
+ RBD_MIRROR_IMAGE_DISABLED = 2
+} rbd_mirror_image_state_t;
+
+typedef struct {
+ char *global_id;
+ rbd_mirror_image_state_t state;
+ bool primary;
+} rbd_mirror_image_info_t;
+
+typedef enum {
+ MIRROR_IMAGE_STATUS_STATE_UNKNOWN = 0,
+ MIRROR_IMAGE_STATUS_STATE_ERROR = 1,
+ MIRROR_IMAGE_STATUS_STATE_SYNCING = 2,
+ MIRROR_IMAGE_STATUS_STATE_STARTING_REPLAY = 3,
+ MIRROR_IMAGE_STATUS_STATE_REPLAYING = 4,
+ MIRROR_IMAGE_STATUS_STATE_STOPPING_REPLAY = 5,
+ MIRROR_IMAGE_STATUS_STATE_STOPPED = 6,
+} rbd_mirror_image_status_state_t;
+
+typedef struct {
+ char *name;
+ rbd_mirror_image_info_t info;
+ rbd_mirror_image_status_state_t state;
+ char *description;
+ time_t last_update;
+ bool up;
+} rbd_mirror_image_status_t CEPH_RBD_DEPRECATED;
+
+typedef struct {
+ char *mirror_uuid;
+ rbd_mirror_image_status_state_t state;
+ char *description;
+ time_t last_update;
+ bool up;
+} rbd_mirror_image_site_status_t;
+
+typedef struct {
+ char *name;
+ rbd_mirror_image_info_t info;
+ uint32_t site_statuses_count;
+ rbd_mirror_image_site_status_t *site_statuses;
+} rbd_mirror_image_global_status_t;
+
+typedef enum {
+ RBD_GROUP_IMAGE_STATE_ATTACHED,
+ RBD_GROUP_IMAGE_STATE_INCOMPLETE
+} rbd_group_image_state_t;
+
+typedef struct {
+ char *name;
+ int64_t pool;
+ rbd_group_image_state_t state;
+} rbd_group_image_info_t;
+
+typedef struct {
+ char *name;
+ int64_t pool;
+} rbd_group_info_t;
+
+typedef enum {
+ RBD_GROUP_SNAP_STATE_INCOMPLETE,
+ RBD_GROUP_SNAP_STATE_COMPLETE
+} rbd_group_snap_state_t;
+
+typedef struct {
+ char *name;
+ rbd_group_snap_state_t state;
+} rbd_group_snap_info_t;
+
+typedef struct {
+ int64_t group_pool;
+ char *group_name;
+ char *group_snap_name;
+} rbd_snap_group_namespace_t;
+
+typedef enum {
+ RBD_SNAP_MIRROR_STATE_PRIMARY,
+ RBD_SNAP_MIRROR_STATE_PRIMARY_DEMOTED,
+ RBD_SNAP_MIRROR_STATE_NON_PRIMARY,
+ RBD_SNAP_MIRROR_STATE_NON_PRIMARY_DEMOTED
+} rbd_snap_mirror_state_t;
+
+typedef struct {
+ rbd_snap_mirror_state_t state;
+ size_t mirror_peer_uuids_count;
+ char *mirror_peer_uuids;
+ bool complete;
+ char *primary_mirror_uuid;
+ uint64_t primary_snap_id;
+ uint64_t last_copied_object_number;
+} rbd_snap_mirror_namespace_t;
+
+typedef enum {
+ RBD_LOCK_MODE_EXCLUSIVE = 0,
+ RBD_LOCK_MODE_SHARED = 1,
+} rbd_lock_mode_t;
+
+CEPH_RBD_API void rbd_version(int *major, int *minor, int *extra);
+
+/* image options */
+enum {
+ RBD_IMAGE_OPTION_FORMAT = 0,
+ RBD_IMAGE_OPTION_FEATURES = 1,
+ RBD_IMAGE_OPTION_ORDER = 2,
+ RBD_IMAGE_OPTION_STRIPE_UNIT = 3,
+ RBD_IMAGE_OPTION_STRIPE_COUNT = 4,
+ RBD_IMAGE_OPTION_JOURNAL_ORDER = 5,
+ RBD_IMAGE_OPTION_JOURNAL_SPLAY_WIDTH = 6,
+ RBD_IMAGE_OPTION_JOURNAL_POOL = 7,
+ RBD_IMAGE_OPTION_FEATURES_SET = 8,
+ RBD_IMAGE_OPTION_FEATURES_CLEAR = 9,
+ RBD_IMAGE_OPTION_DATA_POOL = 10,
+ RBD_IMAGE_OPTION_FLATTEN = 11,
+ RBD_IMAGE_OPTION_CLONE_FORMAT = 12,
+ RBD_IMAGE_OPTION_MIRROR_IMAGE_MODE = 13,
+};
+
+typedef enum {
+ RBD_TRASH_IMAGE_SOURCE_USER = 0,
+ RBD_TRASH_IMAGE_SOURCE_MIRRORING = 1,
+ RBD_TRASH_IMAGE_SOURCE_MIGRATION = 2,
+ RBD_TRASH_IMAGE_SOURCE_REMOVING = 3,
+ RBD_TRASH_IMAGE_SOURCE_USER_PARENT = 4,
+} rbd_trash_image_source_t;
+
+typedef struct {
+ char *id;
+ char *name;
+ rbd_trash_image_source_t source;
+ time_t deletion_time;
+ time_t deferment_end_time;
+} rbd_trash_image_info_t;
+
+typedef struct {
+ char *addr;
+ int64_t id;
+ uint64_t cookie;
+} rbd_image_watcher_t;
+
+typedef enum {
+ RBD_IMAGE_MIGRATION_STATE_UNKNOWN = -1,
+ RBD_IMAGE_MIGRATION_STATE_ERROR = 0,
+ RBD_IMAGE_MIGRATION_STATE_PREPARING = 1,
+ RBD_IMAGE_MIGRATION_STATE_PREPARED = 2,
+ RBD_IMAGE_MIGRATION_STATE_EXECUTING = 3,
+ RBD_IMAGE_MIGRATION_STATE_EXECUTED = 4,
+ RBD_IMAGE_MIGRATION_STATE_ABORTING = 5,
+} rbd_image_migration_state_t;
+
+typedef struct {
+ int64_t source_pool_id;
+ char *source_pool_namespace;
+ char *source_image_name;
+ char *source_image_id;
+ int64_t dest_pool_id;
+ char *dest_pool_namespace;
+ char *dest_image_name;
+ char *dest_image_id;
+ rbd_image_migration_state_t state;
+ char *state_description;
+} rbd_image_migration_status_t;
+
+typedef enum {
+ RBD_CONFIG_SOURCE_CONFIG = 0,
+ RBD_CONFIG_SOURCE_POOL = 1,
+ RBD_CONFIG_SOURCE_IMAGE = 2,
+} rbd_config_source_t;
+
+typedef struct {
+ char *name;
+ char *value;
+ rbd_config_source_t source;
+} rbd_config_option_t;
+
+typedef enum {
+ RBD_POOL_STAT_OPTION_IMAGES,
+ RBD_POOL_STAT_OPTION_IMAGE_PROVISIONED_BYTES,
+ RBD_POOL_STAT_OPTION_IMAGE_MAX_PROVISIONED_BYTES,
+ RBD_POOL_STAT_OPTION_IMAGE_SNAPSHOTS,
+ RBD_POOL_STAT_OPTION_TRASH_IMAGES,
+ RBD_POOL_STAT_OPTION_TRASH_PROVISIONED_BYTES,
+ RBD_POOL_STAT_OPTION_TRASH_MAX_PROVISIONED_BYTES,
+ RBD_POOL_STAT_OPTION_TRASH_SNAPSHOTS
+} rbd_pool_stat_option_t;
+
+/* rbd_write_zeroes / rbd_aio_write_zeroes flags */
+enum {
+ RBD_WRITE_ZEROES_FLAG_THICK_PROVISION = (1U<<0), /* fully allocated zeroed extent */
+};
+
+typedef enum {
+ RBD_ENCRYPTION_FORMAT_LUKS1 = 0,
+ RBD_ENCRYPTION_FORMAT_LUKS2 = 1,
+ RBD_ENCRYPTION_FORMAT_LUKS = 2
+} rbd_encryption_format_t;
+
+typedef enum {
+ RBD_ENCRYPTION_ALGORITHM_AES128 = 0,
+ RBD_ENCRYPTION_ALGORITHM_AES256 = 1
+} rbd_encryption_algorithm_t;
+
+typedef void *rbd_encryption_options_t;
+
+typedef struct {
+ rbd_encryption_format_t format;
+ rbd_encryption_options_t opts;
+ size_t opts_size;
+} rbd_encryption_spec_t;
+
+typedef struct {
+ rbd_encryption_algorithm_t alg;
+ const char* passphrase;
+ size_t passphrase_size;
+} rbd_encryption_luks1_format_options_t;
+
+typedef struct {
+ rbd_encryption_algorithm_t alg;
+ const char* passphrase;
+ size_t passphrase_size;
+} rbd_encryption_luks2_format_options_t;
+
+typedef struct {
+ const char* passphrase;
+ size_t passphrase_size;
+} rbd_encryption_luks_format_options_t;
+
+CEPH_RBD_API void rbd_image_options_create(rbd_image_options_t* opts);
+CEPH_RBD_API void rbd_image_options_destroy(rbd_image_options_t opts);
+CEPH_RBD_API int rbd_image_options_set_string(rbd_image_options_t opts,
+ int optname, const char* optval);
+CEPH_RBD_API int rbd_image_options_set_uint64(rbd_image_options_t opts,
+ int optname, uint64_t optval);
+CEPH_RBD_API int rbd_image_options_get_string(rbd_image_options_t opts,
+ int optname, char* optval,
+ size_t maxlen);
+CEPH_RBD_API int rbd_image_options_get_uint64(rbd_image_options_t opts,
+ int optname, uint64_t* optval);
+CEPH_RBD_API int rbd_image_options_is_set(rbd_image_options_t opts,
+ int optname, bool* is_set);
+CEPH_RBD_API int rbd_image_options_unset(rbd_image_options_t opts, int optname);
+CEPH_RBD_API void rbd_image_options_clear(rbd_image_options_t opts);
+CEPH_RBD_API int rbd_image_options_is_empty(rbd_image_options_t opts);
+
+/* helpers */
+CEPH_RBD_API void rbd_image_spec_cleanup(rbd_image_spec_t *image);
+CEPH_RBD_API void rbd_image_spec_list_cleanup(rbd_image_spec_t *images,
+ size_t num_images);
+CEPH_RBD_API void rbd_linked_image_spec_cleanup(rbd_linked_image_spec_t *image);
+CEPH_RBD_API void rbd_linked_image_spec_list_cleanup(
+ rbd_linked_image_spec_t *images, size_t num_images);
+CEPH_RBD_API void rbd_snap_spec_cleanup(rbd_snap_spec_t *snap);
+
+/* images */
+CEPH_RBD_API int rbd_list(rados_ioctx_t io, char *names, size_t *size)
+ CEPH_RBD_DEPRECATED;
+CEPH_RBD_API int rbd_list2(rados_ioctx_t io, rbd_image_spec_t* images,
+ size_t *max_images);
+
+CEPH_RBD_API int rbd_create(rados_ioctx_t io, const char *name, uint64_t size,
+ int *order);
+CEPH_RBD_API int rbd_create2(rados_ioctx_t io, const char *name, uint64_t size,
+ uint64_t features, int *order);
+/**
+ * create new rbd image
+ *
+ * The stripe_unit must be a factor of the object size (1 << order).
+ * The stripe_count can be one (no intra-object striping) or greater
+ * than one. The RBD_FEATURE_STRIPINGV2 must be specified if the
+ * stripe_unit != the object size and the stripe_count is != 1.
+ *
+ * @param io ioctx
+ * @param name image name
+ * @param size image size in bytes
+ * @param features initial feature bits
+ * @param order object/block size, as a power of two (object size == 1 << order)
+ * @param stripe_unit stripe unit size, in bytes.
+ * @param stripe_count number of objects to stripe over before looping
+ * @return 0 on success, or negative error code
+ */
+CEPH_RBD_API int rbd_create3(rados_ioctx_t io, const char *name, uint64_t size,
+ uint64_t features, int *order,
+ uint64_t stripe_unit, uint64_t stripe_count);
+CEPH_RBD_API int rbd_create4(rados_ioctx_t io, const char *name, uint64_t size,
+ rbd_image_options_t opts);
+CEPH_RBD_API int rbd_clone(rados_ioctx_t p_ioctx, const char *p_name,
+ const char *p_snapname, rados_ioctx_t c_ioctx,
+ const char *c_name, uint64_t features, int *c_order);
+CEPH_RBD_API int rbd_clone2(rados_ioctx_t p_ioctx, const char *p_name,
+ const char *p_snapname, rados_ioctx_t c_ioctx,
+ const char *c_name, uint64_t features, int *c_order,
+ uint64_t stripe_unit, int stripe_count);
+CEPH_RBD_API int rbd_clone3(rados_ioctx_t p_ioctx, const char *p_name,
+ const char *p_snapname, rados_ioctx_t c_ioctx,
+ const char *c_name, rbd_image_options_t c_opts);
+CEPH_RBD_API int rbd_remove(rados_ioctx_t io, const char *name);
+CEPH_RBD_API int rbd_remove_with_progress(rados_ioctx_t io, const char *name,
+ librbd_progress_fn_t cb,
+ void *cbdata);
+CEPH_RBD_API int rbd_rename(rados_ioctx_t src_io_ctx, const char *srcname,
+ const char *destname);
+
+CEPH_RBD_API int rbd_trash_move(rados_ioctx_t io, const char *name,
+ uint64_t delay);
+CEPH_RBD_API int rbd_trash_get(rados_ioctx_t io, const char *id,
+ rbd_trash_image_info_t *info);
+CEPH_RBD_API void rbd_trash_get_cleanup(rbd_trash_image_info_t *info);
+CEPH_RBD_API int rbd_trash_list(rados_ioctx_t io,
+ rbd_trash_image_info_t *trash_entries,
+ size_t *num_entries);
+CEPH_RBD_API void rbd_trash_list_cleanup(rbd_trash_image_info_t *trash_entries,
+ size_t num_entries);
+CEPH_RBD_API int rbd_trash_purge(rados_ioctx_t io, time_t expire_ts, float threshold);
+CEPH_RBD_API int rbd_trash_purge_with_progress(rados_ioctx_t io, time_t expire_ts,
+ float threshold, librbd_progress_fn_t cb,
+ void* cbdata);
+CEPH_RBD_API int rbd_trash_remove(rados_ioctx_t io, const char *id, bool force);
+CEPH_RBD_API int rbd_trash_remove_with_progress(rados_ioctx_t io,
+ const char *id,
+ bool force,
+ librbd_progress_fn_t cb,
+ void *cbdata);
+CEPH_RBD_API int rbd_trash_restore(rados_ioctx_t io, const char *id,
+ const char *name);
+
+/* migration */
+CEPH_RBD_API int rbd_migration_prepare(rados_ioctx_t ioctx,
+ const char *image_name,
+ rados_ioctx_t dest_ioctx,
+ const char *dest_image_name,
+ rbd_image_options_t opts);
+CEPH_RBD_API int rbd_migration_prepare_import(
+ const char *source_spec, rados_ioctx_t dest_ioctx,
+ const char *dest_image_name, rbd_image_options_t opts);
+CEPH_RBD_API int rbd_migration_execute(rados_ioctx_t ioctx,
+ const char *image_name);
+CEPH_RBD_API int rbd_migration_execute_with_progress(rados_ioctx_t ioctx,
+ const char *image_name,
+ librbd_progress_fn_t cb,
+ void *cbdata);
+CEPH_RBD_API int rbd_migration_abort(rados_ioctx_t ioctx,
+ const char *image_name);
+CEPH_RBD_API int rbd_migration_abort_with_progress(rados_ioctx_t ioctx,
+ const char *image_name,
+ librbd_progress_fn_t cb,
+ void *cbdata);
+CEPH_RBD_API int rbd_migration_commit(rados_ioctx_t ioctx,
+ const char *image_name);
+CEPH_RBD_API int rbd_migration_commit_with_progress(rados_ioctx_t ioctx,
+ const char *image_name,
+ librbd_progress_fn_t cb,
+ void *cbdata);
+CEPH_RBD_API int rbd_migration_status(rados_ioctx_t ioctx,
+ const char *image_name,
+ rbd_image_migration_status_t *status,
+ size_t status_size);
+CEPH_RBD_API void rbd_migration_status_cleanup(
+ rbd_image_migration_status_t *status);
+
+/* pool mirroring */
+CEPH_RBD_API int rbd_mirror_site_name_get(rados_t cluster,
+ char *name, size_t *max_len);
+CEPH_RBD_API int rbd_mirror_site_name_set(rados_t cluster,
+ const char *name);
+
+CEPH_RBD_API int rbd_mirror_mode_get(rados_ioctx_t io_ctx,
+ rbd_mirror_mode_t *mirror_mode);
+CEPH_RBD_API int rbd_mirror_mode_set(rados_ioctx_t io_ctx,
+ rbd_mirror_mode_t mirror_mode);
+
+CEPH_RBD_API int rbd_mirror_uuid_get(rados_ioctx_t io_ctx,
+ char *uuid, size_t *max_len);
+
+CEPH_RBD_API int rbd_mirror_peer_bootstrap_create(
+ rados_ioctx_t io_ctx, char *token, size_t *max_len);
+CEPH_RBD_API int rbd_mirror_peer_bootstrap_import(
+ rados_ioctx_t io_ctx, rbd_mirror_peer_direction_t direction,
+ const char *token);
+
+CEPH_RBD_API int rbd_mirror_peer_site_add(
+ rados_ioctx_t io_ctx, char *uuid, size_t uuid_max_length,
+ rbd_mirror_peer_direction_t direction, const char *site_name,
+ const char *client_name);
+CEPH_RBD_API int rbd_mirror_peer_site_set_name(
+ rados_ioctx_t io_ctx, const char *uuid, const char *site_name);
+CEPH_RBD_API int rbd_mirror_peer_site_set_client_name(
+ rados_ioctx_t io_ctx, const char *uuid, const char *client_name);
+CEPH_RBD_API int rbd_mirror_peer_site_set_direction(
+ rados_ioctx_t io_ctx, const char *uuid,
+ rbd_mirror_peer_direction_t direction);
+CEPH_RBD_API int rbd_mirror_peer_site_remove(
+ rados_ioctx_t io_ctx, const char *uuid);
+CEPH_RBD_API int rbd_mirror_peer_site_list(
+ rados_ioctx_t io_ctx, rbd_mirror_peer_site_t *peers, int *max_peers);
+CEPH_RBD_API void rbd_mirror_peer_site_list_cleanup(
+ rbd_mirror_peer_site_t *peers, int max_peers);
+CEPH_RBD_API int rbd_mirror_peer_site_get_attributes(
+ rados_ioctx_t p, const char *uuid, char *keys, size_t *max_key_len,
+ char *values, size_t *max_value_len, size_t *key_value_count);
+CEPH_RBD_API int rbd_mirror_peer_site_set_attributes(
+ rados_ioctx_t p, const char *uuid, const char *keys, const char *values,
+ size_t key_value_count);
+
+CEPH_RBD_API int rbd_mirror_image_global_status_list(
+ rados_ioctx_t io_ctx, const char *start_id, size_t max, char **image_ids,
+ rbd_mirror_image_global_status_t *images, size_t *len);
+CEPH_RBD_API void rbd_mirror_image_global_status_list_cleanup(
+ char **image_ids, rbd_mirror_image_global_status_t *images, size_t len);
+
+/* rbd_mirror_peer_ commands are deprecated to rbd_mirror_peer_site_
+ * equivalents */
+CEPH_RBD_API int rbd_mirror_peer_add(
+ rados_ioctx_t io_ctx, char *uuid, size_t uuid_max_length,
+ const char *cluster_name, const char *client_name)
+ CEPH_RBD_DEPRECATED;
+CEPH_RBD_API int rbd_mirror_peer_remove(
+ rados_ioctx_t io_ctx, const char *uuid)
+ CEPH_RBD_DEPRECATED;
+CEPH_RBD_API int rbd_mirror_peer_list(
+ rados_ioctx_t io_ctx, rbd_mirror_peer_t *peers, int *max_peers)
+ CEPH_RBD_DEPRECATED;
+CEPH_RBD_API void rbd_mirror_peer_list_cleanup(
+ rbd_mirror_peer_t *peers, int max_peers)
+ CEPH_RBD_DEPRECATED;
+CEPH_RBD_API int rbd_mirror_peer_set_client(
+ rados_ioctx_t io_ctx, const char *uuid, const char *client_name)
+ CEPH_RBD_DEPRECATED;
+CEPH_RBD_API int rbd_mirror_peer_set_cluster(
+ rados_ioctx_t io_ctx, const char *uuid, const char *cluster_name)
+ CEPH_RBD_DEPRECATED;
+CEPH_RBD_API int rbd_mirror_peer_get_attributes(
+ rados_ioctx_t p, const char *uuid, char *keys, size_t *max_key_len,
+ char *values, size_t *max_value_len, size_t *key_value_count)
+ CEPH_RBD_DEPRECATED;
+CEPH_RBD_API int rbd_mirror_peer_set_attributes(
+ rados_ioctx_t p, const char *uuid, const char *keys, const char *values,
+ size_t key_value_count)
+ CEPH_RBD_DEPRECATED;
+
+/* rbd_mirror_image_status_list_ commands are deprecard to
+ * rbd_mirror_image_global_status_list_ commands */
+
+CEPH_RBD_API int rbd_mirror_image_status_list(
+ rados_ioctx_t io_ctx, const char *start_id, size_t max, char **image_ids,
+ rbd_mirror_image_status_t *images, size_t *len)
+ CEPH_RBD_DEPRECATED;
+CEPH_RBD_API void rbd_mirror_image_status_list_cleanup(
+ char **image_ids, rbd_mirror_image_status_t *images, size_t len)
+ CEPH_RBD_DEPRECATED;
+
+CEPH_RBD_API int rbd_mirror_image_status_summary(
+ rados_ioctx_t io_ctx, rbd_mirror_image_status_state_t *states, int *counts,
+ size_t *maxlen);
+
+CEPH_RBD_API int rbd_mirror_image_instance_id_list(rados_ioctx_t io_ctx,
+ const char *start_id,
+ size_t max, char **image_ids,
+ char **instance_ids,
+ size_t *len);
+CEPH_RBD_API void rbd_mirror_image_instance_id_list_cleanup(char **image_ids,
+ char **instance_ids,
+ size_t len);
+CEPH_RBD_API int rbd_mirror_image_info_list(
+ rados_ioctx_t io_ctx, rbd_mirror_image_mode_t *mode_filter,
+ const char *start_id, size_t max, char **image_ids,
+ rbd_mirror_image_mode_t *mode_entries,
+ rbd_mirror_image_info_t *info_entries, size_t *num_entries);
+CEPH_RBD_API void rbd_mirror_image_info_list_cleanup(
+ char **image_ids, rbd_mirror_image_info_t *info_entries,
+ size_t num_entries);
+
+/* pool metadata */
+CEPH_RBD_API int rbd_pool_metadata_get(rados_ioctx_t io_ctx, const char *key,
+ char *value, size_t *val_len);
+CEPH_RBD_API int rbd_pool_metadata_set(rados_ioctx_t io_ctx, const char *key,
+ const char *value);
+CEPH_RBD_API int rbd_pool_metadata_remove(rados_ioctx_t io_ctx,
+ const char *key);
+CEPH_RBD_API int rbd_pool_metadata_list(rados_ioctx_t io_ctx, const char *start,
+ uint64_t max, char *keys,
+ size_t *key_len, char *values,
+ size_t *vals_len);
+
+CEPH_RBD_API int rbd_config_pool_list(rados_ioctx_t io_ctx,
+ rbd_config_option_t *options,
+ int *max_options);
+CEPH_RBD_API void rbd_config_pool_list_cleanup(rbd_config_option_t *options,
+ int max_options);
+
+CEPH_RBD_API int rbd_open(rados_ioctx_t io, const char *name,
+ rbd_image_t *image, const char *snap_name);
+CEPH_RBD_API int rbd_open_by_id(rados_ioctx_t io, const char *id,
+ rbd_image_t *image, const char *snap_name);
+
+CEPH_RBD_API int rbd_aio_open(rados_ioctx_t io, const char *name,
+ rbd_image_t *image, const char *snap_name,
+ rbd_completion_t c);
+CEPH_RBD_API int rbd_aio_open_by_id(rados_ioctx_t io, const char *id,
+ rbd_image_t *image, const char *snap_name,
+ rbd_completion_t c);
+
+/**
+ * Open an image in read-only mode.
+ *
+ * This is intended for use by clients that cannot write to a block
+ * device due to cephx restrictions. There will be no watch
+ * established on the header object, since a watch is a write. This
+ * means the metadata reported about this image (parents, snapshots,
+ * size, etc.) may become stale. This should not be used for
+ * long-running operations, unless you can be sure that one of these
+ * properties changing is safe.
+ *
+ * Attempting to write to a read-only image will return -EROFS.
+ *
+ * @param io ioctx to determine the pool the image is in
+ * @param name image name
+ * @param image where to store newly opened image handle
+ * @param snap_name name of snapshot to open at, or NULL for no snapshot
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RBD_API int rbd_open_read_only(rados_ioctx_t io, const char *name,
+ rbd_image_t *image, const char *snap_name);
+CEPH_RBD_API int rbd_open_by_id_read_only(rados_ioctx_t io, const char *id,
+ rbd_image_t *image, const char *snap_name);
+CEPH_RBD_API int rbd_aio_open_read_only(rados_ioctx_t io, const char *name,
+ rbd_image_t *image, const char *snap_name,
+ rbd_completion_t c);
+CEPH_RBD_API int rbd_aio_open_by_id_read_only(rados_ioctx_t io, const char *id,
+ rbd_image_t *image, const char *snap_name,
+ rbd_completion_t c);
+CEPH_RBD_API int rbd_features_to_string(uint64_t features, char *str_features,
+ size_t *size);
+CEPH_RBD_API int rbd_features_from_string(const char *str_features, uint64_t *features);
+CEPH_RBD_API int rbd_close(rbd_image_t image);
+CEPH_RBD_API int rbd_aio_close(rbd_image_t image, rbd_completion_t c);
+CEPH_RBD_API int rbd_resize(rbd_image_t image, uint64_t size);
+CEPH_RBD_API int rbd_resize2(rbd_image_t image, uint64_t size, bool allow_shrink,
+ librbd_progress_fn_t cb, void *cbdata);
+CEPH_RBD_API int rbd_resize_with_progress(rbd_image_t image, uint64_t size,
+ librbd_progress_fn_t cb, void *cbdata);
+CEPH_RBD_API int rbd_stat(rbd_image_t image, rbd_image_info_t *info,
+ size_t infosize);
+CEPH_RBD_API int rbd_get_old_format(rbd_image_t image, uint8_t *old);
+CEPH_RBD_API int rbd_get_size(rbd_image_t image, uint64_t *size);
+CEPH_RBD_API int rbd_get_features(rbd_image_t image, uint64_t *features);
+CEPH_RBD_API int rbd_update_features(rbd_image_t image, uint64_t features,
+ uint8_t enabled);
+CEPH_RBD_API int rbd_get_op_features(rbd_image_t image, uint64_t *op_features);
+CEPH_RBD_API int rbd_get_stripe_unit(rbd_image_t image, uint64_t *stripe_unit);
+CEPH_RBD_API int rbd_get_stripe_count(rbd_image_t image,
+ uint64_t *stripe_count);
+
+CEPH_RBD_API int rbd_get_create_timestamp(rbd_image_t image,
+ struct timespec *timestamp);
+CEPH_RBD_API int rbd_get_access_timestamp(rbd_image_t image,
+ struct timespec *timestamp);
+CEPH_RBD_API int rbd_get_modify_timestamp(rbd_image_t image,
+ struct timespec *timestamp);
+
+CEPH_RBD_API int rbd_get_overlap(rbd_image_t image, uint64_t *overlap);
+CEPH_RBD_API int rbd_get_name(rbd_image_t image, char *name, size_t *name_len);
+CEPH_RBD_API int rbd_get_id(rbd_image_t image, char *id, size_t id_len);
+CEPH_RBD_API int rbd_get_block_name_prefix(rbd_image_t image,
+ char *prefix, size_t prefix_len);
+CEPH_RBD_API int64_t rbd_get_data_pool_id(rbd_image_t image);
+
+CEPH_RBD_API int rbd_get_parent_info(rbd_image_t image,
+ char *parent_poolname, size_t ppoolnamelen,
+ char *parent_name, size_t pnamelen,
+ char *parent_snapname,
+ size_t psnapnamelen)
+ CEPH_RBD_DEPRECATED;
+CEPH_RBD_API int rbd_get_parent_info2(rbd_image_t image,
+ char *parent_poolname,
+ size_t ppoolnamelen,
+ char *parent_name, size_t pnamelen,
+ char *parent_id, size_t pidlen,
+ char *parent_snapname,
+ size_t psnapnamelen)
+ CEPH_RBD_DEPRECATED;
+CEPH_RBD_API int rbd_get_parent(rbd_image_t image,
+ rbd_linked_image_spec_t *parent_image,
+ rbd_snap_spec_t *parent_snap);
+
+CEPH_RBD_API int rbd_get_migration_source_spec(rbd_image_t image,
+ char* source_spec,
+ size_t* max_len);
+
+CEPH_RBD_API int rbd_get_flags(rbd_image_t image, uint64_t *flags);
+CEPH_RBD_API int rbd_get_group(rbd_image_t image, rbd_group_info_t *group_info,
+ size_t group_info_size);
+CEPH_RBD_API int rbd_set_image_notification(rbd_image_t image, int fd, int type);
+
+/* exclusive lock feature */
+CEPH_RBD_API int rbd_is_exclusive_lock_owner(rbd_image_t image, int *is_owner);
+CEPH_RBD_API int rbd_lock_acquire(rbd_image_t image, rbd_lock_mode_t lock_mode);
+CEPH_RBD_API int rbd_lock_release(rbd_image_t image);
+CEPH_RBD_API int rbd_lock_get_owners(rbd_image_t image,
+ rbd_lock_mode_t *lock_mode,
+ char **lock_owners,
+ size_t *max_lock_owners);
+CEPH_RBD_API void rbd_lock_get_owners_cleanup(char **lock_owners,
+ size_t lock_owner_count);
+CEPH_RBD_API int rbd_lock_break(rbd_image_t image, rbd_lock_mode_t lock_mode,
+ const char *lock_owner);
+
+/* object map feature */
+CEPH_RBD_API int rbd_rebuild_object_map(rbd_image_t image,
+ librbd_progress_fn_t cb, void *cbdata);
+
+CEPH_RBD_API int rbd_copy(rbd_image_t image, rados_ioctx_t dest_io_ctx,
+ const char *destname);
+CEPH_RBD_API int rbd_copy2(rbd_image_t src, rbd_image_t dest);
+CEPH_RBD_API int rbd_copy3(rbd_image_t src, rados_ioctx_t dest_io_ctx,
+ const char *destname, rbd_image_options_t dest_opts);
+CEPH_RBD_API int rbd_copy4(rbd_image_t src, rados_ioctx_t dest_io_ctx,
+ const char *destname, rbd_image_options_t dest_opts,
+ size_t sparse_size);
+CEPH_RBD_API int rbd_copy_with_progress(rbd_image_t image, rados_ioctx_t dest_p,
+ const char *destname,
+ librbd_progress_fn_t cb, void *cbdata);
+CEPH_RBD_API int rbd_copy_with_progress2(rbd_image_t src, rbd_image_t dest,
+ librbd_progress_fn_t cb, void *cbdata);
+CEPH_RBD_API int rbd_copy_with_progress3(rbd_image_t image,
+ rados_ioctx_t dest_p,
+ const char *destname,
+ rbd_image_options_t dest_opts,
+ librbd_progress_fn_t cb, void *cbdata);
+CEPH_RBD_API int rbd_copy_with_progress4(rbd_image_t image,
+ rados_ioctx_t dest_p,
+ const char *destname,
+ rbd_image_options_t dest_opts,
+ librbd_progress_fn_t cb, void *cbdata,
+ size_t sparse_size);
+
+/* deep copy */
+CEPH_RBD_API int rbd_deep_copy(rbd_image_t src, rados_ioctx_t dest_io_ctx,
+ const char *destname,
+ rbd_image_options_t dest_opts);
+CEPH_RBD_API int rbd_deep_copy_with_progress(rbd_image_t image,
+ rados_ioctx_t dest_io_ctx,
+ const char *destname,
+ rbd_image_options_t dest_opts,
+ librbd_progress_fn_t cb,
+ void *cbdata);
+
+/* encryption */
+
+/*
+ * Format the image using the encryption spec specified by
+ * (format, opts, opts_size) tuple.
+ *
+ * For a flat (i.e. non-cloned) image, the new encryption is loaded
+ * implicitly, calling rbd_encryption_load() afterwards is not needed.
+ * If existing encryption is already loaded, it is automatically
+ * replaced with the new encryption.
+ *
+ * For a cloned image, the new encryption must be loaded explicitly.
+ * Existing encryption (if any) must not be loaded.
+ */
+CEPH_RBD_API int rbd_encryption_format(rbd_image_t image,
+ rbd_encryption_format_t format,
+ rbd_encryption_options_t opts,
+ size_t opts_size);
+/*
+ * Load the encryption spec specified by (format, opts, opts_size)
+ * tuple for the image and all ancestor images. If an ancestor image
+ * which does not match any encryption format known to librbd is
+ * encountered, it - along with remaining ancestor images - is
+ * interpreted as plaintext.
+ */
+CEPH_RBD_API int rbd_encryption_load(rbd_image_t image,
+ rbd_encryption_format_t format,
+ rbd_encryption_options_t opts,
+ size_t opts_size);
+/*
+ * Load encryption specs. The first spec in the passed array is
+ * applied to the image itself, the second spec is applied to its
+ * ancestor image, the third spec is applied to the ancestor of
+ * that ancestor image and so on.
+ *
+ * If not enough specs are passed, the last spec is reused exactly as
+ * in rbd_encryption_load(). If an ancestor image for which the last
+ * spec is being reused turns out to not match any encryption format
+ * known to librbd, it - along with remaining ancestor images - is
+ * interpreted as plaintext.
+ */
+CEPH_RBD_API int rbd_encryption_load2(rbd_image_t image,
+ const rbd_encryption_spec_t *specs,
+ size_t spec_count);
+
+/* snapshots */
+CEPH_RBD_API int rbd_snap_list(rbd_image_t image, rbd_snap_info_t *snaps,
+ int *max_snaps);
+CEPH_RBD_API void rbd_snap_list_end(rbd_snap_info_t *snaps);
+CEPH_RBD_API int rbd_snap_exists(rbd_image_t image, const char *snapname, bool *exists);
+CEPH_RBD_API int rbd_snap_create(rbd_image_t image, const char *snapname);
+CEPH_RBD_API int rbd_snap_create2(rbd_image_t image, const char *snap_name,
+ uint32_t flags, librbd_progress_fn_t cb,
+ void *cbdata);
+CEPH_RBD_API int rbd_snap_remove(rbd_image_t image, const char *snapname);
+CEPH_RBD_API int rbd_snap_remove2(rbd_image_t image, const char *snap_name,
+ uint32_t flags, librbd_progress_fn_t cb,
+ void *cbdata);
+CEPH_RBD_API int rbd_snap_remove_by_id(rbd_image_t image, uint64_t snap_id);
+CEPH_RBD_API int rbd_snap_rollback(rbd_image_t image, const char *snapname);
+CEPH_RBD_API int rbd_snap_rollback_with_progress(rbd_image_t image,
+ const char *snapname,
+ librbd_progress_fn_t cb,
+ void *cbdata);
+CEPH_RBD_API int rbd_snap_rename(rbd_image_t image, const char *snapname,
+ const char* dstsnapsname);
+/**
+ * Prevent a snapshot from being deleted until it is unprotected.
+ *
+ * @param snap_name which snapshot to protect
+ * @returns 0 on success, negative error code on failure
+ * @returns -EBUSY if snap is already protected
+ */
+CEPH_RBD_API int rbd_snap_protect(rbd_image_t image, const char *snap_name);
+/**
+ * Allow a snaphshot to be deleted.
+ *
+ * @param snap_name which snapshot to unprotect
+ * @returns 0 on success, negative error code on failure
+ * @returns -EINVAL if snap is not protected
+ */
+CEPH_RBD_API int rbd_snap_unprotect(rbd_image_t image, const char *snap_name);
+/**
+ * Determine whether a snapshot is protected.
+ *
+ * @param snap_name which snapshot query
+ * @param is_protected where to store the result (0 or 1)
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RBD_API int rbd_snap_is_protected(rbd_image_t image, const char *snap_name,
+ int *is_protected);
+/**
+ * Get the current snapshot limit for an image. If no limit is set,
+ * UINT64_MAX is returned.
+ *
+ * @param limit pointer where the limit will be stored on success
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RBD_API int rbd_snap_get_limit(rbd_image_t image, uint64_t *limit);
+
+/**
+ * Set a limit for the number of snapshots that may be taken of an image.
+ *
+ * @param limit the maximum number of snapshots allowed in the future.
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RBD_API int rbd_snap_set_limit(rbd_image_t image, uint64_t limit);
+
+/**
+ * Get the timestamp of a snapshot for an image.
+ *
+ * @param snap_id the snap id of a snapshot of input image.
+ * @param timestamp the timestamp of input snapshot.
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RBD_API int rbd_snap_get_timestamp(rbd_image_t image, uint64_t snap_id, struct timespec *timestamp);
+
+CEPH_RBD_API int rbd_snap_set(rbd_image_t image, const char *snapname);
+CEPH_RBD_API int rbd_snap_set_by_id(rbd_image_t image, uint64_t snap_id);
+CEPH_RBD_API int rbd_snap_get_name(rbd_image_t image, uint64_t snap_id, char *snapname, size_t *name_len);
+CEPH_RBD_API int rbd_snap_get_id(rbd_image_t image, const char *snapname, uint64_t *snap_id);
+
+CEPH_RBD_API int rbd_snap_get_namespace_type(rbd_image_t image,
+ uint64_t snap_id,
+ rbd_snap_namespace_type_t *namespace_type);
+CEPH_RBD_API int rbd_snap_get_group_namespace(rbd_image_t image,
+ uint64_t snap_id,
+ rbd_snap_group_namespace_t *group_snap,
+ size_t group_snap_size);
+CEPH_RBD_API int rbd_snap_group_namespace_cleanup(rbd_snap_group_namespace_t *group_snap,
+ size_t group_snap_size);
+CEPH_RBD_API int rbd_snap_get_trash_namespace(rbd_image_t image,
+ uint64_t snap_id,
+ char* original_name,
+ size_t max_length);
+CEPH_RBD_API int rbd_snap_get_mirror_namespace(
+ rbd_image_t image, uint64_t snap_id,
+ rbd_snap_mirror_namespace_t *mirror_snap, size_t mirror_snap_size);
+CEPH_RBD_API int rbd_snap_mirror_namespace_cleanup(
+ rbd_snap_mirror_namespace_t *mirror_snap, size_t mirror_snap_size);
+
+CEPH_RBD_API int rbd_flatten(rbd_image_t image);
+
+CEPH_RBD_API int rbd_flatten_with_progress(rbd_image_t image,
+ librbd_progress_fn_t cb,
+ void *cbdata);
+
+CEPH_RBD_API int rbd_sparsify(rbd_image_t image, size_t sparse_size);
+
+CEPH_RBD_API int rbd_sparsify_with_progress(rbd_image_t image,
+ size_t sparse_size,
+ librbd_progress_fn_t cb,
+ void *cbdata);
+
+/**
+ * List all images that are cloned from the image at the
+ * snapshot that is set via rbd_snap_set().
+ *
+ * This iterates over all pools, so it should be run by a user with
+ * read access to all of them. pools_len and images_len are filled in
+ * with the number of bytes put into the pools and images buffers.
+ *
+ * If the provided buffers are too short, the required lengths are
+ * still filled in, but the data is not and -ERANGE is returned.
+ * Otherwise, the buffers are filled with the pool and image names
+ * of the children, with a '\0' after each.
+ *
+ * @param image which image (and implicitly snapshot) to list clones of
+ * @param pools buffer in which to store pool names
+ * @param pools_len number of bytes in pools buffer
+ * @param images buffer in which to store image names
+ * @param images_len number of bytes in images buffer
+ * @returns number of children on success, negative error code on failure
+ * @returns -ERANGE if either buffer is too short
+ */
+CEPH_RBD_API ssize_t rbd_list_children(rbd_image_t image, char *pools,
+ size_t *pools_len, char *images,
+ size_t *images_len)
+ CEPH_RBD_DEPRECATED;
+CEPH_RBD_API int rbd_list_children2(rbd_image_t image,
+ rbd_child_info_t *children,
+ int *max_children)
+ CEPH_RBD_DEPRECATED;
+CEPH_RBD_API void rbd_list_child_cleanup(rbd_child_info_t *child)
+ CEPH_RBD_DEPRECATED;
+CEPH_RBD_API void rbd_list_children_cleanup(rbd_child_info_t *children,
+ size_t num_children)
+ CEPH_RBD_DEPRECATED;
+
+CEPH_RBD_API int rbd_list_children3(rbd_image_t image,
+ rbd_linked_image_spec_t *images,
+ size_t *max_images);
+
+CEPH_RBD_API int rbd_list_descendants(rbd_image_t image,
+ rbd_linked_image_spec_t *images,
+ size_t *max_images);
+
+/**
+ * @defgroup librbd_h_locking Advisory Locking
+ *
+ * An rbd image may be locking exclusively, or shared, to facilitate
+ * e.g. live migration where the image may be open in two places at once.
+ * These locks are intended to guard against more than one client
+ * writing to an image without coordination. They don't need to
+ * be used for snapshots, since snapshots are read-only.
+ *
+ * Currently locks only guard against locks being acquired.
+ * They do not prevent anything else.
+ *
+ * A locker is identified by the internal rados client id of the
+ * holder and a user-defined cookie. This (client id, cookie) pair
+ * must be unique for each locker.
+ *
+ * A shared lock also has a user-defined tag associated with it. Each
+ * additional shared lock must specify the same tag or lock
+ * acquisition will fail. This can be used by e.g. groups of hosts
+ * using a clustered filesystem on top of an rbd image to make sure
+ * they're accessing the correct image.
+ *
+ * @{
+ */
+/**
+ * List clients that have locked the image and information about the lock.
+ *
+ * The number of bytes required in each buffer is put in the
+ * corresponding size out parameter. If any of the provided buffers
+ * are too short, -ERANGE is returned after these sizes are filled in.
+ *
+ * @param exclusive where to store whether the lock is exclusive (1) or shared (0)
+ * @param tag where to store the tag associated with the image
+ * @param tag_len number of bytes in tag buffer
+ * @param clients buffer in which locker clients are stored, separated by '\0'
+ * @param clients_len number of bytes in the clients buffer
+ * @param cookies buffer in which locker cookies are stored, separated by '\0'
+ * @param cookies_len number of bytes in the cookies buffer
+ * @param addrs buffer in which locker addresses are stored, separated by '\0'
+ * @param addrs_len number of bytes in the clients buffer
+ * @returns number of lockers on success, negative error code on failure
+ * @returns -ERANGE if any of the buffers are too short
+ */
+CEPH_RBD_API ssize_t rbd_list_lockers(rbd_image_t image, int *exclusive,
+ char *tag, size_t *tag_len,
+ char *clients, size_t *clients_len,
+ char *cookies, size_t *cookies_len,
+ char *addrs, size_t *addrs_len);
+
+/**
+ * Take an exclusive lock on the image.
+ *
+ * @param image the image to lock
+ * @param cookie user-defined identifier for this instance of the lock
+ * @returns 0 on success, negative error code on failure
+ * @returns -EBUSY if the lock is already held by another (client, cookie) pair
+ * @returns -EEXIST if the lock is already held by the same (client, cookie) pair
+ */
+CEPH_RBD_API int rbd_lock_exclusive(rbd_image_t image, const char *cookie);
+
+/**
+ * Take a shared lock on the image.
+ *
+ * Other clients may also take a shared lock, as lock as they use the
+ * same tag.
+ *
+ * @param image the image to lock
+ * @param cookie user-defined identifier for this instance of the lock
+ * @param tag user-defined identifier for this shared use of the lock
+ * @returns 0 on success, negative error code on failure
+ * @returns -EBUSY if the lock is already held by another (client, cookie) pair
+ * @returns -EEXIST if the lock is already held by the same (client, cookie) pair
+ */
+CEPH_RBD_API int rbd_lock_shared(rbd_image_t image, const char *cookie,
+ const char *tag);
+
+/**
+ * Release a shared or exclusive lock on the image.
+ *
+ * @param image the image to unlock
+ * @param cookie user-defined identifier for the instance of the lock
+ * @returns 0 on success, negative error code on failure
+ * @returns -ENOENT if the lock is not held by the specified (client, cookie) pair
+ */
+CEPH_RBD_API int rbd_unlock(rbd_image_t image, const char *cookie);
+
+/**
+ * Release a shared or exclusive lock that was taken by the specified client.
+ *
+ * @param image the image to unlock
+ * @param client the entity holding the lock (as given by rbd_list_lockers())
+ * @param cookie user-defined identifier for the instance of the lock to break
+ * @returns 0 on success, negative error code on failure
+ * @returns -ENOENT if the lock is not held by the specified (client, cookie) pair
+ */
+CEPH_RBD_API int rbd_break_lock(rbd_image_t image, const char *client,
+ const char *cookie);
+
+/** @} locking */
+
+/* I/O */
+CEPH_RBD_API ssize_t rbd_read(rbd_image_t image, uint64_t ofs, size_t len,
+ char *buf);
+/*
+ * @param op_flags: see librados.h constants beginning with LIBRADOS_OP_FLAG
+ */
+CEPH_RBD_API ssize_t rbd_read2(rbd_image_t image, uint64_t ofs, size_t len,
+ char *buf, int op_flags);
+/* DEPRECATED; use rbd_read_iterate2 */
+CEPH_RBD_API int64_t rbd_read_iterate(rbd_image_t image, uint64_t ofs, size_t len,
+ int (*cb)(uint64_t, size_t, const char *, void *),
+ void *arg);
+
+/**
+ * iterate read over an image
+ *
+ * Reads each region of the image and calls the callback. If the
+ * buffer pointer passed to the callback is NULL, the given extent is
+ * defined to be zeros (a hole). Normally the granularity for the
+ * callback is the image stripe size.
+ *
+ * @param image image to read
+ * @param ofs offset to start from
+ * @param len bytes of source image to cover
+ * @param cb callback for each region
+ * @returns 0 success, error otherwise
+ */
+CEPH_RBD_API int rbd_read_iterate2(rbd_image_t image, uint64_t ofs, uint64_t len,
+ int (*cb)(uint64_t, size_t, const char *, void *),
+ void *arg);
+/**
+ * get difference between two versions of an image
+ *
+ * This will return the differences between two versions of an image
+ * via a callback, which gets the offset and length and a flag
+ * indicating whether the extent exists (1), or is known/defined to
+ * be zeros (a hole, 0). If the source snapshot name is NULL, we
+ * interpret that as the beginning of time and return all allocated
+ * regions of the image. The end version is whatever is currently
+ * selected for the image handle (either a snapshot or the writeable
+ * head).
+ *
+ * @param fromsnapname start snapshot name, or NULL
+ * @param ofs start offset
+ * @param len len in bytes of region to report on
+ * @param include_parent 1 if full history diff should include parent
+ * @param whole_object 1 if diff extents should cover whole object
+ * @param cb callback to call for each allocated region
+ * @param arg argument to pass to the callback
+ * @returns 0 on success, or negative error code on error
+ */
+CEPH_RBD_API int rbd_diff_iterate(rbd_image_t image,
+ const char *fromsnapname,
+ uint64_t ofs, uint64_t len,
+ int (*cb)(uint64_t, size_t, int, void *),
+ void *arg);
+CEPH_RBD_API int rbd_diff_iterate2(rbd_image_t image,
+ const char *fromsnapname,
+ uint64_t ofs, uint64_t len,
+ uint8_t include_parent, uint8_t whole_object,
+ int (*cb)(uint64_t, size_t, int, void *),
+ void *arg);
+CEPH_RBD_API ssize_t rbd_write(rbd_image_t image, uint64_t ofs, size_t len,
+ const char *buf);
+/*
+ * @param op_flags: see librados.h constants beginning with LIBRADOS_OP_FLAG
+ */
+CEPH_RBD_API ssize_t rbd_write2(rbd_image_t image, uint64_t ofs, size_t len,
+ const char *buf, int op_flags);
+CEPH_RBD_API int rbd_discard(rbd_image_t image, uint64_t ofs, uint64_t len);
+CEPH_RBD_API ssize_t rbd_writesame(rbd_image_t image, uint64_t ofs, size_t len,
+ const char *buf, size_t data_len,
+ int op_flags);
+CEPH_RBD_API ssize_t rbd_write_zeroes(rbd_image_t image, uint64_t ofs,
+ size_t len, int zero_flags,
+ int op_flags);
+CEPH_RBD_API ssize_t rbd_compare_and_write(rbd_image_t image, uint64_t ofs,
+ size_t len, const char *cmp_buf,
+ const char *buf,
+ uint64_t *mismatch_off,
+ int op_flags);
+
+CEPH_RBD_API int rbd_aio_write(rbd_image_t image, uint64_t off, size_t len,
+ const char *buf, rbd_completion_t c);
+
+/*
+ * @param op_flags: see librados.h constants beginning with LIBRADOS_OP_FLAG
+ */
+CEPH_RBD_API int rbd_aio_write2(rbd_image_t image, uint64_t off, size_t len,
+ const char *buf, rbd_completion_t c,
+ int op_flags);
+CEPH_RBD_API int rbd_aio_writev(rbd_image_t image, const struct iovec *iov,
+ int iovcnt, uint64_t off, rbd_completion_t c);
+CEPH_RBD_API int rbd_aio_read(rbd_image_t image, uint64_t off, size_t len,
+ char *buf, rbd_completion_t c);
+/*
+ * @param op_flags: see librados.h constants beginning with LIBRADOS_OP_FLAG
+ */
+CEPH_RBD_API int rbd_aio_read2(rbd_image_t image, uint64_t off, size_t len,
+ char *buf, rbd_completion_t c, int op_flags);
+CEPH_RBD_API int rbd_aio_readv(rbd_image_t image, const struct iovec *iov,
+ int iovcnt, uint64_t off, rbd_completion_t c);
+CEPH_RBD_API int rbd_aio_discard(rbd_image_t image, uint64_t off, uint64_t len,
+ rbd_completion_t c);
+CEPH_RBD_API int rbd_aio_writesame(rbd_image_t image, uint64_t off, size_t len,
+ const char *buf, size_t data_len,
+ rbd_completion_t c, int op_flags);
+CEPH_RBD_API int rbd_aio_write_zeroes(rbd_image_t image, uint64_t off,
+ size_t len, rbd_completion_t c,
+ int zero_flags, int op_flags);
+CEPH_RBD_API ssize_t rbd_aio_compare_and_write(rbd_image_t image,
+ uint64_t off, size_t len,
+ const char *cmp_buf,
+ const char *buf,
+ rbd_completion_t c,
+ uint64_t *mismatch_off,
+ int op_flags);
+CEPH_RBD_API ssize_t rbd_aio_compare_and_writev(rbd_image_t image,
+ uint64_t off,
+ const struct iovec *cmp_iov,
+ int cmp_iovcnt,
+ const struct iovec *iov,
+ int iovcnt,
+ rbd_completion_t c,
+ uint64_t *mismatch_off,
+ int op_flags);
+
+CEPH_RBD_API int rbd_aio_create_completion(void *cb_arg,
+ rbd_callback_t complete_cb,
+ rbd_completion_t *c);
+CEPH_RBD_API int rbd_aio_is_complete(rbd_completion_t c);
+CEPH_RBD_API int rbd_aio_wait_for_complete(rbd_completion_t c);
+CEPH_RBD_API ssize_t rbd_aio_get_return_value(rbd_completion_t c);
+CEPH_RBD_API void *rbd_aio_get_arg(rbd_completion_t c);
+CEPH_RBD_API void rbd_aio_release(rbd_completion_t c);
+CEPH_RBD_API int rbd_flush(rbd_image_t image);
+/**
+ * Start a flush if caching is enabled. Get a callback when
+ * the currently pending writes are on disk.
+ *
+ * @param image the image to flush writes to
+ * @param c what to call when flushing is complete
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RBD_API int rbd_aio_flush(rbd_image_t image, rbd_completion_t c);
+
+/**
+ * Drop any cached data for an image
+ *
+ * @param image the image to invalidate cached data for
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RBD_API int rbd_invalidate_cache(rbd_image_t image);
+
+CEPH_RBD_API int rbd_poll_io_events(rbd_image_t image, rbd_completion_t *comps, int numcomp);
+
+CEPH_RBD_API int rbd_metadata_get(rbd_image_t image, const char *key, char *value, size_t *val_len);
+CEPH_RBD_API int rbd_metadata_set(rbd_image_t image, const char *key, const char *value);
+CEPH_RBD_API int rbd_metadata_remove(rbd_image_t image, const char *key);
+/**
+ * List all metadatas associated with this image.
+ *
+ * This iterates over all metadatas, key_len and val_len are filled in
+ * with the number of bytes put into the keys and values buffers.
+ *
+ * If the provided buffers are too short, the required lengths are
+ * still filled in, but the data is not and -ERANGE is returned.
+ * Otherwise, the buffers are filled with the keys and values
+ * of the image, with a '\0' after each.
+ *
+ * @param image which image (and implicitly snapshot) to list clones of
+ * @param start_after which name to begin listing after
+ * (use the empty string to start at the beginning)
+ * @param max the maximum number of names to lis(if 0 means no limit)
+ * @param keys buffer in which to store pool names
+ * @param keys_len number of bytes in pools buffer
+ * @param values buffer in which to store image names
+ * @param vals_len number of bytes in images buffer
+ * @returns number of children on success, negative error code on failure
+ * @returns -ERANGE if either buffer is too short
+ */
+CEPH_RBD_API int rbd_metadata_list(rbd_image_t image, const char *start, uint64_t max,
+ char *keys, size_t *key_len, char *values, size_t *vals_len);
+
+// RBD image mirroring support functions
+CEPH_RBD_API int rbd_mirror_image_enable(rbd_image_t image) CEPH_RBD_DEPRECATED;
+CEPH_RBD_API int rbd_mirror_image_enable2(rbd_image_t image,
+ rbd_mirror_image_mode_t mode);
+CEPH_RBD_API int rbd_mirror_image_disable(rbd_image_t image, bool force);
+CEPH_RBD_API int rbd_mirror_image_promote(rbd_image_t image, bool force);
+CEPH_RBD_API int rbd_mirror_image_demote(rbd_image_t image);
+CEPH_RBD_API int rbd_mirror_image_resync(rbd_image_t image);
+CEPH_RBD_API int rbd_mirror_image_create_snapshot(rbd_image_t image,
+ uint64_t *snap_id);
+CEPH_RBD_API int rbd_mirror_image_create_snapshot2(rbd_image_t image,
+ uint32_t flags,
+ uint64_t *snap_id);
+CEPH_RBD_API int rbd_mirror_image_get_info(rbd_image_t image,
+ rbd_mirror_image_info_t *mirror_image_info,
+ size_t info_size);
+CEPH_RBD_API void rbd_mirror_image_get_info_cleanup(
+ rbd_mirror_image_info_t *mirror_image_info);
+CEPH_RBD_API int rbd_mirror_image_get_mode(rbd_image_t image,
+ rbd_mirror_image_mode_t *mode);
+
+CEPH_RBD_API int rbd_mirror_image_get_global_status(
+ rbd_image_t image,
+ rbd_mirror_image_global_status_t *mirror_image_global_status,
+ size_t status_size);
+CEPH_RBD_API void rbd_mirror_image_global_status_cleanup(
+ rbd_mirror_image_global_status_t *mirror_image_global_status);
+
+CEPH_RBD_API int rbd_mirror_image_get_status(
+ rbd_image_t image, rbd_mirror_image_status_t *mirror_image_status,
+ size_t status_size)
+ CEPH_RBD_DEPRECATED;
+
+CEPH_RBD_API int rbd_mirror_image_get_instance_id(rbd_image_t image,
+ char *instance_id,
+ size_t *id_max_length);
+CEPH_RBD_API int rbd_aio_mirror_image_promote(rbd_image_t image, bool force,
+ rbd_completion_t c);
+CEPH_RBD_API int rbd_aio_mirror_image_demote(rbd_image_t image,
+ rbd_completion_t c);
+CEPH_RBD_API int rbd_aio_mirror_image_get_info(rbd_image_t image,
+ rbd_mirror_image_info_t *mirror_image_info,
+ size_t info_size,
+ rbd_completion_t c);
+CEPH_RBD_API int rbd_aio_mirror_image_get_mode(rbd_image_t image,
+ rbd_mirror_image_mode_t *mode,
+ rbd_completion_t c);
+
+CEPH_RBD_API int rbd_aio_mirror_image_get_global_status(
+ rbd_image_t image,
+ rbd_mirror_image_global_status_t *mirror_global_image_status,
+ size_t status_size, rbd_completion_t c);
+CEPH_RBD_API int rbd_aio_mirror_image_get_status(
+ rbd_image_t image, rbd_mirror_image_status_t *mirror_image_status,
+ size_t status_size, rbd_completion_t c)
+ CEPH_RBD_DEPRECATED;
+
+CEPH_RBD_API int rbd_aio_mirror_image_create_snapshot(rbd_image_t image,
+ uint32_t flags,
+ uint64_t *snap_id,
+ rbd_completion_t c);
+
+// RBD groups support functions
+CEPH_RBD_API int rbd_group_create(rados_ioctx_t p, const char *name);
+CEPH_RBD_API int rbd_group_remove(rados_ioctx_t p, const char *name);
+CEPH_RBD_API int rbd_group_list(rados_ioctx_t p, char *names, size_t *size);
+CEPH_RBD_API int rbd_group_rename(rados_ioctx_t p, const char *src_name,
+ const char *dest_name);
+CEPH_RBD_API int rbd_group_info_cleanup(rbd_group_info_t *group_info,
+ size_t group_info_size);
+
+/**
+ * Register an image metadata change watcher.
+ *
+ * @param image the image to watch
+ * @param handle where to store the internal id assigned to this watch
+ * @param watch_cb what to do when a notify is received on this image
+ * @param arg opaque value to pass to the callback
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RBD_API int rbd_update_watch(rbd_image_t image, uint64_t *handle,
+ rbd_update_callback_t watch_cb, void *arg);
+
+/**
+ * Unregister an image watcher.
+ *
+ * @param image the image to unwatch
+ * @param handle which watch to unregister
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RBD_API int rbd_update_unwatch(rbd_image_t image, uint64_t handle);
+
+/**
+ * List any watchers of an image.
+ *
+ * Watchers will be allocated and stored in the passed watchers array. If there
+ * are more watchers than max_watchers, -ERANGE will be returned and the number
+ * of watchers will be stored in max_watchers.
+ *
+ * The caller should call rbd_watchers_list_cleanup when finished with the list
+ * of watchers.
+ *
+ * @param image the image to list watchers for.
+ * @param watchers an array to store watchers in.
+ * @param max_watchers capacity of the watchers array.
+ * @returns 0 on success, negative error code on failure.
+ * @returns -ERANGE if there are too many watchers for the passed array.
+ * @returns the number of watchers in max_watchers.
+ */
+CEPH_RBD_API int rbd_watchers_list(rbd_image_t image,
+ rbd_image_watcher_t *watchers,
+ size_t *max_watchers);
+
+CEPH_RBD_API void rbd_watchers_list_cleanup(rbd_image_watcher_t *watchers,
+ size_t num_watchers);
+
+CEPH_RBD_API int rbd_config_image_list(rbd_image_t image,
+ rbd_config_option_t *options,
+ int *max_options);
+CEPH_RBD_API void rbd_config_image_list_cleanup(rbd_config_option_t *options,
+ int max_options);
+
+CEPH_RBD_API int rbd_group_image_add(rados_ioctx_t group_p,
+ const char *group_name,
+ rados_ioctx_t image_p,
+ const char *image_name);
+CEPH_RBD_API int rbd_group_image_remove(rados_ioctx_t group_p,
+ const char *group_name,
+ rados_ioctx_t image_p,
+ const char *image_name);
+CEPH_RBD_API int rbd_group_image_remove_by_id(rados_ioctx_t group_p,
+ const char *group_name,
+ rados_ioctx_t image_p,
+ const char *image_id);
+CEPH_RBD_API int rbd_group_image_list(rados_ioctx_t group_p,
+ const char *group_name,
+ rbd_group_image_info_t *images,
+ size_t group_image_info_size,
+ size_t *num_entries);
+CEPH_RBD_API int rbd_group_image_list_cleanup(rbd_group_image_info_t *images,
+ size_t group_image_info_size,
+ size_t num_entries);
+
+CEPH_RBD_API int rbd_group_snap_create(rados_ioctx_t group_p,
+ const char *group_name,
+ const char *snap_name);
+CEPH_RBD_API int rbd_group_snap_create2(rados_ioctx_t group_p,
+ const char *group_name,
+ const char *snap_name,
+ uint32_t flags);
+CEPH_RBD_API int rbd_group_snap_remove(rados_ioctx_t group_p,
+ const char *group_name,
+ const char *snap_name);
+CEPH_RBD_API int rbd_group_snap_rename(rados_ioctx_t group_p,
+ const char *group_name,
+ const char *old_snap_name,
+ const char *new_snap_name);
+CEPH_RBD_API int rbd_group_snap_list(rados_ioctx_t group_p,
+ const char *group_name,
+ rbd_group_snap_info_t *snaps,
+ size_t group_snap_info_size,
+ size_t *num_entries);
+CEPH_RBD_API int rbd_group_snap_list_cleanup(rbd_group_snap_info_t *snaps,
+ size_t group_snap_info_size,
+ size_t num_entries);
+CEPH_RBD_API int rbd_group_snap_rollback(rados_ioctx_t group_p,
+ const char *group_name,
+ const char *snap_name);
+CEPH_RBD_API int rbd_group_snap_rollback_with_progress(rados_ioctx_t group_p,
+ const char *group_name,
+ const char *snap_name,
+ librbd_progress_fn_t cb,
+ void *cbdata);
+
+CEPH_RBD_API int rbd_namespace_create(rados_ioctx_t io,
+ const char *namespace_name);
+CEPH_RBD_API int rbd_namespace_remove(rados_ioctx_t io,
+ const char *namespace_name);
+CEPH_RBD_API int rbd_namespace_list(rados_ioctx_t io, char *namespace_names,
+ size_t *size);
+CEPH_RBD_API int rbd_namespace_exists(rados_ioctx_t io,
+ const char *namespace_name,
+ bool *exists);
+
+CEPH_RBD_API int rbd_pool_init(rados_ioctx_t io, bool force);
+
+CEPH_RBD_API void rbd_pool_stats_create(rbd_pool_stats_t *stats);
+CEPH_RBD_API void rbd_pool_stats_destroy(rbd_pool_stats_t stats);
+CEPH_RBD_API int rbd_pool_stats_option_add_uint64(rbd_pool_stats_t stats,
+ int stat_option,
+ uint64_t* stat_val);
+CEPH_RBD_API int rbd_pool_stats_get(rados_ioctx_t io, rbd_pool_stats_t stats);
+
+/**
+ * Register a quiesce/unquiesce watcher.
+ *
+ * @param image the image to watch
+ * @param quiesce_cb what to do when librbd wants to quiesce
+ * @param unquiesce_cb what to do when librbd wants to unquiesce
+ * @param arg opaque value to pass to the callbacks
+ * @param handle where to store the internal id assigned to this watch
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RBD_API int rbd_quiesce_watch(rbd_image_t image,
+ rbd_update_callback_t quiesce_cb,
+ rbd_update_callback_t unquiesce_cb,
+ void *arg, uint64_t *handle);
+
+/**
+ * Notify quiesce is complete
+ *
+ * @param image the image to notify
+ * @param handle which watch is complete
+ * @param r the return code
+ */
+CEPH_RBD_API void rbd_quiesce_complete(rbd_image_t image, uint64_t handle,
+ int r);
+
+/**
+ * Unregister a quiesce/unquiesce watcher.
+ *
+ * @param image the image to unwatch
+ * @param handle which watch to unregister
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RBD_API int rbd_quiesce_unwatch(rbd_image_t image, uint64_t handle);
+
+#if __GNUC__ >= 4
+ #pragma GCC diagnostic pop
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CEPH_LIBRBD_H */
diff --git a/src/include/rbd/librbd.hpp b/src/include/rbd/librbd.hpp
new file mode 100644
index 000000000..5d307cded
--- /dev/null
+++ b/src/include/rbd/librbd.hpp
@@ -0,0 +1,869 @@
+// -*- 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) 2011 New Dream Network
+ *
+ * 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 __LIBRBD_HPP
+#define __LIBRBD_HPP
+
+#include <string>
+#include <list>
+#include <map>
+#include <vector>
+#include "../rados/buffer.h"
+#include "../rados/librados.hpp"
+#include "librbd.h"
+
+#if __GNUC__ >= 4
+ #pragma GCC diagnostic push
+ #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+namespace librbd {
+
+ using librados::IoCtx;
+
+ class Image;
+ class ImageOptions;
+ class PoolStats;
+ typedef void *image_ctx_t;
+ typedef void *completion_t;
+ typedef void (*callback_t)(completion_t cb, void *arg);
+
+ typedef struct {
+ std::string id;
+ std::string name;
+ } image_spec_t;
+
+ typedef struct {
+ int64_t pool_id;
+ std::string pool_name;
+ std::string pool_namespace;
+ std::string image_id;
+ std::string image_name;
+ bool trash;
+ } linked_image_spec_t;
+
+ typedef rbd_snap_namespace_type_t snap_namespace_type_t;
+
+ typedef struct {
+ uint64_t id;
+ snap_namespace_type_t namespace_type;
+ std::string name;
+ } snap_spec_t;
+
+ typedef struct {
+ uint64_t id;
+ uint64_t size;
+ std::string name;
+ } snap_info_t;
+
+ typedef struct {
+ int64_t group_pool;
+ std::string group_name;
+ std::string group_snap_name;
+ } snap_group_namespace_t;
+
+ typedef rbd_snap_mirror_state_t snap_mirror_state_t;
+
+ typedef struct {
+ snap_mirror_state_t state;
+ std::set<std::string> mirror_peer_uuids;
+ bool complete;
+ std::string primary_mirror_uuid;
+ uint64_t primary_snap_id;
+ uint64_t last_copied_object_number;
+ } snap_mirror_namespace_t;
+
+ typedef struct {
+ std::string client;
+ std::string cookie;
+ std::string address;
+ } locker_t;
+
+ typedef rbd_mirror_peer_direction_t mirror_peer_direction_t;
+
+ typedef struct {
+ std::string uuid;
+ std::string cluster_name;
+ std::string client_name;
+ } mirror_peer_t CEPH_RBD_DEPRECATED;
+
+ typedef struct {
+ std::string uuid;
+ mirror_peer_direction_t direction;
+ std::string site_name;
+ std::string mirror_uuid;
+ std::string client_name;
+ time_t last_seen;
+ } mirror_peer_site_t;
+
+ typedef rbd_mirror_image_mode_t mirror_image_mode_t;
+ typedef rbd_mirror_image_state_t mirror_image_state_t;
+
+ typedef struct {
+ std::string global_id;
+ mirror_image_state_t state;
+ bool primary;
+ } mirror_image_info_t;
+
+ typedef rbd_mirror_image_status_state_t mirror_image_status_state_t;
+
+ typedef struct {
+ std::string name;
+ mirror_image_info_t info;
+ mirror_image_status_state_t state;
+ std::string description;
+ time_t last_update;
+ bool up;
+ } mirror_image_status_t CEPH_RBD_DEPRECATED;
+
+ typedef struct {
+ std::string mirror_uuid;
+ mirror_image_status_state_t state;
+ std::string description;
+ time_t last_update;
+ bool up;
+ } mirror_image_site_status_t;
+
+ typedef struct {
+ std::string name;
+ mirror_image_info_t info;
+ std::vector<mirror_image_site_status_t> site_statuses;
+ } mirror_image_global_status_t;
+
+ typedef rbd_group_image_state_t group_image_state_t;
+
+ typedef struct {
+ std::string name;
+ int64_t pool;
+ group_image_state_t state;
+ } group_image_info_t;
+
+ typedef struct {
+ std::string name;
+ int64_t pool;
+ } group_info_t;
+
+ typedef rbd_group_snap_state_t group_snap_state_t;
+
+ typedef struct {
+ std::string name;
+ group_snap_state_t state;
+ } group_snap_info_t;
+
+ typedef rbd_image_info_t image_info_t;
+
+ class CEPH_RBD_API ProgressContext
+ {
+ public:
+ virtual ~ProgressContext();
+ virtual int update_progress(uint64_t offset, uint64_t total) = 0;
+ };
+
+ typedef struct {
+ std::string id;
+ std::string name;
+ rbd_trash_image_source_t source;
+ time_t deletion_time;
+ time_t deferment_end_time;
+ } trash_image_info_t;
+
+ typedef struct {
+ std::string pool_name;
+ std::string image_name;
+ std::string image_id;
+ bool trash;
+ } child_info_t;
+
+ typedef struct {
+ std::string addr;
+ int64_t id;
+ uint64_t cookie;
+ } image_watcher_t;
+
+ typedef rbd_image_migration_state_t image_migration_state_t;
+
+ typedef struct {
+ int64_t source_pool_id;
+ std::string source_pool_namespace;
+ std::string source_image_name;
+ std::string source_image_id;
+ int64_t dest_pool_id;
+ std::string dest_pool_namespace;
+ std::string dest_image_name;
+ std::string dest_image_id;
+ image_migration_state_t state;
+ std::string state_description;
+ } image_migration_status_t;
+
+ typedef rbd_config_source_t config_source_t;
+
+ typedef struct {
+ std::string name;
+ std::string value;
+ config_source_t source;
+ } config_option_t;
+
+ typedef rbd_encryption_format_t encryption_format_t;
+ typedef rbd_encryption_algorithm_t encryption_algorithm_t;
+ typedef rbd_encryption_options_t encryption_options_t;
+ typedef rbd_encryption_spec_t encryption_spec_t;
+
+ typedef struct {
+ encryption_algorithm_t alg;
+ std::string passphrase;
+ } encryption_luks1_format_options_t;
+
+ typedef struct {
+ encryption_algorithm_t alg;
+ std::string passphrase;
+ } encryption_luks2_format_options_t;
+
+ typedef struct {
+ std::string passphrase;
+ } encryption_luks_format_options_t;
+
+class CEPH_RBD_API RBD
+{
+public:
+ RBD();
+ ~RBD();
+
+ // This must be dynamically allocated with new, and
+ // must be released with release().
+ // Do not use delete.
+ struct AioCompletion {
+ void *pc;
+ AioCompletion(void *cb_arg, callback_t complete_cb);
+ bool is_complete();
+ int wait_for_complete();
+ ssize_t get_return_value();
+ void *get_arg();
+ void release();
+ };
+
+ void version(int *major, int *minor, int *extra);
+
+ int open(IoCtx& io_ctx, Image& image, const char *name);
+ int open(IoCtx& io_ctx, Image& image, const char *name, const char *snapname);
+ int open_by_id(IoCtx& io_ctx, Image& image, const char *id);
+ int open_by_id(IoCtx& io_ctx, Image& image, const char *id, const char *snapname);
+ int aio_open(IoCtx& io_ctx, Image& image, const char *name,
+ const char *snapname, RBD::AioCompletion *c);
+ int aio_open_by_id(IoCtx& io_ctx, Image& image, const char *id,
+ const char *snapname, RBD::AioCompletion *c);
+ // see librbd.h
+ int open_read_only(IoCtx& io_ctx, Image& image, const char *name,
+ const char *snapname);
+ int open_by_id_read_only(IoCtx& io_ctx, Image& image, const char *id,
+ const char *snapname);
+ int aio_open_read_only(IoCtx& io_ctx, Image& image, const char *name,
+ const char *snapname, RBD::AioCompletion *c);
+ int aio_open_by_id_read_only(IoCtx& io_ctx, Image& image, const char *id,
+ const char *snapname, RBD::AioCompletion *c);
+ int features_to_string(uint64_t features, std::string *str_features);
+ int features_from_string(const std::string str_features, uint64_t *features);
+
+ int list(IoCtx& io_ctx, std::vector<std::string>& names)
+ CEPH_RBD_DEPRECATED;
+ int list2(IoCtx& io_ctx, std::vector<image_spec_t>* images);
+
+ int create(IoCtx& io_ctx, const char *name, uint64_t size, int *order);
+ int create2(IoCtx& io_ctx, const char *name, uint64_t size,
+ uint64_t features, int *order);
+ int create3(IoCtx& io_ctx, const char *name, uint64_t size,
+ uint64_t features, int *order,
+ uint64_t stripe_unit, uint64_t stripe_count);
+ int create4(IoCtx& io_ctx, const char *name, uint64_t size,
+ ImageOptions& opts);
+ int clone(IoCtx& p_ioctx, const char *p_name, const char *p_snapname,
+ IoCtx& c_ioctx, const char *c_name, uint64_t features,
+ int *c_order);
+ int clone2(IoCtx& p_ioctx, const char *p_name, const char *p_snapname,
+ IoCtx& c_ioctx, const char *c_name, uint64_t features,
+ int *c_order, uint64_t stripe_unit, int stripe_count);
+ int clone3(IoCtx& p_ioctx, const char *p_name, const char *p_snapname,
+ IoCtx& c_ioctx, const char *c_name, ImageOptions& opts);
+ int remove(IoCtx& io_ctx, const char *name);
+ int remove_with_progress(IoCtx& io_ctx, const char *name, ProgressContext& pctx);
+ int rename(IoCtx& src_io_ctx, const char *srcname, const char *destname);
+
+ int trash_move(IoCtx &io_ctx, const char *name, uint64_t delay);
+ int trash_get(IoCtx &io_ctx, const char *id, trash_image_info_t *info);
+ int trash_list(IoCtx &io_ctx, std::vector<trash_image_info_t> &entries);
+ int trash_purge(IoCtx &io_ctx, time_t expire_ts, float threshold);
+ int trash_purge_with_progress(IoCtx &io_ctx, time_t expire_ts, float threshold,
+ ProgressContext &pctx);
+ int trash_remove(IoCtx &io_ctx, const char *image_id, bool force);
+ int trash_remove_with_progress(IoCtx &io_ctx, const char *image_id,
+ bool force, ProgressContext &pctx);
+ int trash_restore(IoCtx &io_ctx, const char *id, const char *name);
+
+ // Migration
+ int migration_prepare(IoCtx& io_ctx, const char *image_name,
+ IoCtx& dest_io_ctx, const char *dest_image_name,
+ ImageOptions& opts);
+ int migration_prepare_import(const char *source_spec, IoCtx& dest_io_ctx,
+ const char *dest_image_name, ImageOptions& opts);
+ int migration_execute(IoCtx& io_ctx, const char *image_name);
+ int migration_execute_with_progress(IoCtx& io_ctx, const char *image_name,
+ ProgressContext &prog_ctx);
+ int migration_abort(IoCtx& io_ctx, const char *image_name);
+ int migration_abort_with_progress(IoCtx& io_ctx, const char *image_name,
+ ProgressContext &prog_ctx);
+ int migration_commit(IoCtx& io_ctx, const char *image_name);
+ int migration_commit_with_progress(IoCtx& io_ctx, const char *image_name,
+ ProgressContext &prog_ctx);
+ int migration_status(IoCtx& io_ctx, const char *image_name,
+ image_migration_status_t *status, size_t status_size);
+
+ // RBD pool mirroring support functions
+ int mirror_site_name_get(librados::Rados& rados, std::string* site_name);
+ int mirror_site_name_set(librados::Rados& rados,
+ const std::string& site_name);
+
+ int mirror_mode_get(IoCtx& io_ctx, rbd_mirror_mode_t *mirror_mode);
+ int mirror_mode_set(IoCtx& io_ctx, rbd_mirror_mode_t mirror_mode);
+
+ int mirror_uuid_get(IoCtx& io_ctx, std::string* mirror_uuid);
+
+ int mirror_peer_bootstrap_create(IoCtx& io_ctx, std::string* token);
+ int mirror_peer_bootstrap_import(IoCtx& io_ctx,
+ mirror_peer_direction_t direction,
+ const std::string &token);
+
+ int mirror_peer_site_add(IoCtx& io_ctx, std::string *uuid,
+ mirror_peer_direction_t direction,
+ const std::string &site_name,
+ const std::string &client_name);
+ int mirror_peer_site_set_name(IoCtx& io_ctx, const std::string& uuid,
+ const std::string &site_name);
+ int mirror_peer_site_set_client_name(IoCtx& io_ctx, const std::string& uuid,
+ const std::string &client_name);
+ int mirror_peer_site_set_direction(IoCtx& io_ctx, const std::string& uuid,
+ mirror_peer_direction_t direction);
+ int mirror_peer_site_remove(IoCtx& io_ctx, const std::string& uuid);
+ int mirror_peer_site_list(IoCtx& io_ctx,
+ std::vector<mirror_peer_site_t> *peers);
+ int mirror_peer_site_get_attributes(
+ IoCtx& io_ctx, const std::string &uuid,
+ std::map<std::string, std::string> *key_vals);
+ int mirror_peer_site_set_attributes(
+ IoCtx& io_ctx, const std::string &uuid,
+ const std::map<std::string, std::string>& key_vals);
+
+ int mirror_image_global_status_list(
+ IoCtx& io_ctx, const std::string &start_id, size_t max,
+ std::map<std::string, mirror_image_global_status_t> *images);
+ int mirror_image_status_summary(IoCtx& io_ctx,
+ std::map<mirror_image_status_state_t, int> *states);
+ int mirror_image_instance_id_list(IoCtx& io_ctx, const std::string &start_id,
+ size_t max, std::map<std::string, std::string> *sevice_ids);
+ int mirror_image_info_list(IoCtx& io_ctx, mirror_image_mode_t *mode_filter,
+ const std::string &start_id, size_t max,
+ std::map<std::string, std::pair<mirror_image_mode_t,
+ mirror_image_info_t>> *entries);
+
+ /// mirror_peer_ commands are deprecated to mirror_peer_site_ equivalents
+ int mirror_peer_add(IoCtx& io_ctx, std::string *uuid,
+ const std::string &cluster_name,
+ const std::string &client_name)
+ CEPH_RBD_DEPRECATED;
+ int mirror_peer_remove(IoCtx& io_ctx, const std::string &uuid)
+ CEPH_RBD_DEPRECATED;
+ int mirror_peer_list(IoCtx& io_ctx, std::vector<mirror_peer_t> *peers)
+ CEPH_RBD_DEPRECATED;
+ int mirror_peer_set_client(IoCtx& io_ctx, const std::string &uuid,
+ const std::string &client_name)
+ CEPH_RBD_DEPRECATED;
+ int mirror_peer_set_cluster(IoCtx& io_ctx, const std::string &uuid,
+ const std::string &cluster_name)
+ CEPH_RBD_DEPRECATED;
+ int mirror_peer_get_attributes(
+ IoCtx& io_ctx, const std::string &uuid,
+ std::map<std::string, std::string> *key_vals)
+ CEPH_RBD_DEPRECATED;
+ int mirror_peer_set_attributes(
+ IoCtx& io_ctx, const std::string &uuid,
+ const std::map<std::string, std::string>& key_vals)
+ CEPH_RBD_DEPRECATED;
+
+ /// mirror_image_status_list command is deprecated to
+ /// mirror_image_global_status_list
+
+ int mirror_image_status_list(
+ IoCtx& io_ctx, const std::string &start_id, size_t max,
+ std::map<std::string, mirror_image_status_t> *images)
+ CEPH_RBD_DEPRECATED;
+
+ // RBD groups support functions
+ int group_create(IoCtx& io_ctx, const char *group_name);
+ int group_remove(IoCtx& io_ctx, const char *group_name);
+ int group_list(IoCtx& io_ctx, std::vector<std::string> *names);
+ int group_rename(IoCtx& io_ctx, const char *src_group_name,
+ const char *dest_group_name);
+
+ int group_image_add(IoCtx& io_ctx, const char *group_name,
+ IoCtx& image_io_ctx, const char *image_name);
+ int group_image_remove(IoCtx& io_ctx, const char *group_name,
+ IoCtx& image_io_ctx, const char *image_name);
+ int group_image_remove_by_id(IoCtx& io_ctx, const char *group_name,
+ IoCtx& image_io_ctx, const char *image_id);
+ int group_image_list(IoCtx& io_ctx, const char *group_name,
+ std::vector<group_image_info_t> *images,
+ size_t group_image_info_size);
+
+ int group_snap_create(IoCtx& io_ctx, const char *group_name,
+ const char *snap_name);
+ int group_snap_create2(IoCtx& io_ctx, const char *group_name,
+ const char *snap_name, uint32_t flags);
+ int group_snap_remove(IoCtx& io_ctx, const char *group_name,
+ const char *snap_name);
+ int group_snap_rename(IoCtx& group_ioctx, const char *group_name,
+ const char *old_snap_name, const char *new_snap_name);
+ int group_snap_list(IoCtx& group_ioctx, const char *group_name,
+ std::vector<group_snap_info_t> *snaps,
+ size_t group_snap_info_size);
+ int group_snap_rollback(IoCtx& io_ctx, const char *group_name,
+ const char *snap_name);
+ int group_snap_rollback_with_progress(IoCtx& io_ctx, const char *group_name,
+ const char *snap_name,
+ ProgressContext& pctx);
+
+ int namespace_create(IoCtx& ioctx, const char *namespace_name);
+ int namespace_remove(IoCtx& ioctx, const char *namespace_name);
+ int namespace_list(IoCtx& io_ctx, std::vector<std::string>* namespace_names);
+ int namespace_exists(IoCtx& io_ctx, const char *namespace_name, bool *exists);
+
+ int pool_init(IoCtx& io_ctx, bool force);
+ int pool_stats_get(IoCtx& io_ctx, PoolStats *pool_stats);
+
+ int pool_metadata_get(IoCtx &io_ctx, const std::string &key,
+ std::string *value);
+ int pool_metadata_set(IoCtx &io_ctx, const std::string &key,
+ const std::string &value);
+ int pool_metadata_remove(IoCtx &io_ctx, const std::string &key);
+ int pool_metadata_list(IoCtx &io_ctx, const std::string &start, uint64_t max,
+ std::map<std::string, ceph::bufferlist> *pairs);
+
+ int config_list(IoCtx& io_ctx, std::vector<config_option_t> *options);
+
+private:
+ /* We don't allow assignment or copying */
+ RBD(const RBD& rhs);
+ const RBD& operator=(const RBD& rhs);
+};
+
+class CEPH_RBD_API ImageOptions {
+public:
+ ImageOptions();
+ ImageOptions(rbd_image_options_t opts);
+ ImageOptions(const ImageOptions &imgopts);
+ ~ImageOptions();
+
+ int set(int optname, const std::string& optval);
+ int set(int optname, uint64_t optval);
+ int get(int optname, std::string* optval) const;
+ int get(int optname, uint64_t* optval) const;
+ int is_set(int optname, bool* is_set);
+ int unset(int optname);
+ void clear();
+ bool empty() const;
+
+private:
+ friend class RBD;
+ friend class Image;
+
+ rbd_image_options_t opts;
+};
+
+class CEPH_RBD_API PoolStats {
+public:
+ PoolStats();
+ ~PoolStats();
+
+ PoolStats(const PoolStats&) = delete;
+ PoolStats& operator=(const PoolStats&) = delete;
+
+ int add(rbd_pool_stat_option_t option, uint64_t* opt_val);
+
+private:
+ friend class RBD;
+
+ rbd_pool_stats_t pool_stats;
+};
+
+class CEPH_RBD_API UpdateWatchCtx {
+public:
+ virtual ~UpdateWatchCtx() {}
+ /**
+ * Callback activated when we receive a notify event.
+ */
+ virtual void handle_notify() = 0;
+};
+
+class CEPH_RBD_API QuiesceWatchCtx {
+public:
+ virtual ~QuiesceWatchCtx() {}
+ /**
+ * Callback activated when we want to quiesce.
+ */
+ virtual void handle_quiesce() = 0;
+
+ /**
+ * Callback activated when we want to unquiesce.
+ */
+ virtual void handle_unquiesce() = 0;
+};
+
+class CEPH_RBD_API Image
+{
+public:
+ Image();
+ ~Image();
+
+ int close();
+ int aio_close(RBD::AioCompletion *c);
+
+ int resize(uint64_t size);
+ int resize2(uint64_t size, bool allow_shrink, ProgressContext& pctx);
+ int resize_with_progress(uint64_t size, ProgressContext& pctx);
+ int stat(image_info_t &info, size_t infosize);
+ int get_name(std::string *name);
+ int get_id(std::string *id);
+ std::string get_block_name_prefix();
+ int64_t get_data_pool_id();
+ int parent_info(std::string *parent_poolname, std::string *parent_name,
+ std::string *parent_snapname)
+ CEPH_RBD_DEPRECATED;
+ int parent_info2(std::string *parent_poolname, std::string *parent_name,
+ std::string *parent_id, std::string *parent_snapname)
+ CEPH_RBD_DEPRECATED;
+ int get_parent(linked_image_spec_t *parent_image, snap_spec_t *parent_snap);
+
+ int get_migration_source_spec(std::string* source_spec);
+
+ int old_format(uint8_t *old);
+ int size(uint64_t *size);
+ int get_group(group_info_t *group_info, size_t group_info_size);
+ int features(uint64_t *features);
+ int update_features(uint64_t features, bool enabled);
+ int get_op_features(uint64_t *op_features);
+ int overlap(uint64_t *overlap);
+ int get_flags(uint64_t *flags);
+ int set_image_notification(int fd, int type);
+
+ /* exclusive lock feature */
+ int is_exclusive_lock_owner(bool *is_owner);
+ int lock_acquire(rbd_lock_mode_t lock_mode);
+ int lock_release();
+ int lock_get_owners(rbd_lock_mode_t *lock_mode,
+ std::list<std::string> *lock_owners);
+ int lock_break(rbd_lock_mode_t lock_mode, const std::string &lock_owner);
+
+ /* object map feature */
+ int rebuild_object_map(ProgressContext &prog_ctx);
+
+ int check_object_map(ProgressContext &prog_ctx);
+
+ int copy(IoCtx& dest_io_ctx, const char *destname);
+ int copy2(Image& dest);
+ int copy3(IoCtx& dest_io_ctx, const char *destname, ImageOptions& opts);
+ int copy4(IoCtx& dest_io_ctx, const char *destname, ImageOptions& opts,
+ size_t sparse_size);
+ int copy_with_progress(IoCtx& dest_io_ctx, const char *destname,
+ ProgressContext &prog_ctx);
+ int copy_with_progress2(Image& dest, ProgressContext &prog_ctx);
+ int copy_with_progress3(IoCtx& dest_io_ctx, const char *destname,
+ ImageOptions& opts, ProgressContext &prog_ctx);
+ int copy_with_progress4(IoCtx& dest_io_ctx, const char *destname,
+ ImageOptions& opts, ProgressContext &prog_ctx,
+ size_t sparse_size);
+
+ /* deep copy */
+ int deep_copy(IoCtx& dest_io_ctx, const char *destname, ImageOptions& opts);
+ int deep_copy_with_progress(IoCtx& dest_io_ctx, const char *destname,
+ ImageOptions& opts, ProgressContext &prog_ctx);
+
+ /* encryption */
+ int encryption_format(encryption_format_t format, encryption_options_t opts,
+ size_t opts_size);
+ int encryption_load(encryption_format_t format, encryption_options_t opts,
+ size_t opts_size);
+ int encryption_load2(const encryption_spec_t *specs, size_t spec_count);
+
+ /* striping */
+ uint64_t get_stripe_unit() const;
+ uint64_t get_stripe_count() const;
+
+ int get_create_timestamp(struct timespec *timestamp);
+ int get_access_timestamp(struct timespec *timestamp);
+ int get_modify_timestamp(struct timespec *timestamp);
+
+ int flatten();
+ int flatten_with_progress(ProgressContext &prog_ctx);
+
+ int sparsify(size_t sparse_size);
+ int sparsify_with_progress(size_t sparse_size, ProgressContext &prog_ctx);
+ /**
+ * Returns a pair of poolname, imagename for each clone
+ * of this image at the currently set snapshot.
+ */
+ int list_children(std::set<std::pair<std::string, std::string> > *children)
+ CEPH_RBD_DEPRECATED;
+ /**
+ * Returns a structure of poolname, imagename, imageid and trash flag
+ * for each clone of this image at the currently set snapshot.
+ */
+ int list_children2(std::vector<librbd::child_info_t> *children)
+ CEPH_RBD_DEPRECATED;
+ int list_children3(std::vector<linked_image_spec_t> *images);
+ int list_descendants(std::vector<linked_image_spec_t> *images);
+
+ /* advisory locking (see librbd.h for details) */
+ int list_lockers(std::list<locker_t> *lockers,
+ bool *exclusive, std::string *tag);
+ int lock_exclusive(const std::string& cookie);
+ int lock_shared(const std::string& cookie, const std::string& tag);
+ int unlock(const std::string& cookie);
+ int break_lock(const std::string& client, const std::string& cookie);
+
+ /* snapshots */
+ int snap_list(std::vector<snap_info_t>& snaps);
+ /* DEPRECATED; use snap_exists2 */
+ bool snap_exists(const char *snapname) CEPH_RBD_DEPRECATED;
+ int snap_exists2(const char *snapname, bool *exists);
+ int snap_create(const char *snapname);
+ int snap_create2(const char *snapname, uint32_t flags, ProgressContext& pctx);
+ int snap_remove(const char *snapname);
+ int snap_remove2(const char *snapname, uint32_t flags, ProgressContext& pctx);
+ int snap_remove_by_id(uint64_t snap_id);
+ int snap_rollback(const char *snap_name);
+ int snap_rollback_with_progress(const char *snap_name, ProgressContext& pctx);
+ int snap_protect(const char *snap_name);
+ int snap_unprotect(const char *snap_name);
+ int snap_is_protected(const char *snap_name, bool *is_protected);
+ int snap_set(const char *snap_name);
+ int snap_set_by_id(uint64_t snap_id);
+ int snap_get_name(uint64_t snap_id, std::string *snap_name);
+ int snap_get_id(const std::string snap_name, uint64_t *snap_id);
+ int snap_rename(const char *srcname, const char *dstname);
+ int snap_get_limit(uint64_t *limit);
+ int snap_set_limit(uint64_t limit);
+ int snap_get_timestamp(uint64_t snap_id, struct timespec *timestamp);
+ int snap_get_namespace_type(uint64_t snap_id,
+ snap_namespace_type_t *namespace_type);
+ int snap_get_group_namespace(uint64_t snap_id,
+ snap_group_namespace_t *group_namespace,
+ size_t snap_group_namespace_size);
+ int snap_get_trash_namespace(uint64_t snap_id, std::string* original_name);
+ int snap_get_mirror_namespace(
+ uint64_t snap_id, snap_mirror_namespace_t *mirror_namespace,
+ size_t snap_mirror_namespace_size);
+
+ /* I/O */
+ ssize_t read(uint64_t ofs, size_t len, ceph::bufferlist& bl);
+ /* @param op_flags see librados.h constants beginning with LIBRADOS_OP_FLAG */
+ ssize_t read2(uint64_t ofs, size_t len, ceph::bufferlist& bl, int op_flags);
+ int64_t read_iterate(uint64_t ofs, size_t len,
+ int (*cb)(uint64_t, size_t, const char *, void *), void *arg);
+ int read_iterate2(uint64_t ofs, uint64_t len,
+ int (*cb)(uint64_t, size_t, const char *, void *), void *arg);
+ /**
+ * get difference between two versions of an image
+ *
+ * This will return the differences between two versions of an image
+ * via a callback, which gets the offset and length and a flag
+ * indicating whether the extent exists (1), or is known/defined to
+ * be zeros (a hole, 0). If the source snapshot name is NULL, we
+ * interpret that as the beginning of time and return all allocated
+ * regions of the image. The end version is whatever is currently
+ * selected for the image handle (either a snapshot or the writeable
+ * head).
+ *
+ * @param fromsnapname start snapshot name, or NULL
+ * @param ofs start offset
+ * @param len len in bytes of region to report on
+ * @param include_parent true if full history diff should include parent
+ * @param whole_object 1 if diff extents should cover whole object
+ * @param cb callback to call for each allocated region
+ * @param arg argument to pass to the callback
+ * @returns 0 on success, or negative error code on error
+ */
+ int diff_iterate(const char *fromsnapname,
+ uint64_t ofs, uint64_t len,
+ int (*cb)(uint64_t, size_t, int, void *), void *arg);
+ int diff_iterate2(const char *fromsnapname,
+ uint64_t ofs, uint64_t len,
+ bool include_parent, bool whole_object,
+ int (*cb)(uint64_t, size_t, int, void *), void *arg);
+
+ ssize_t write(uint64_t ofs, size_t len, ceph::bufferlist& bl);
+ /* @param op_flags see librados.h constants beginning with LIBRADOS_OP_FLAG */
+ ssize_t write2(uint64_t ofs, size_t len, ceph::bufferlist& bl, int op_flags);
+
+ int discard(uint64_t ofs, uint64_t len);
+ ssize_t writesame(uint64_t ofs, size_t len, ceph::bufferlist &bl, int op_flags);
+ ssize_t write_zeroes(uint64_t ofs, size_t len, int zero_flags, int op_flags);
+
+ /**
+ * compare and write from/to image
+ *
+ * Compare data in compare bufferlist to data at offset in image.
+ * len bytes of the compare bufferlist are compared, i.e. the compare
+ * bufferlist has to be at least len bytes long.
+ * If the compare is successful len bytes from the write bufferlist
+ * are written to the image, i.e. the write bufferlist also has to be
+ * at least len bytes long.
+ * If the compare is unsuccessful no data is written and the
+ * offset in the bufferlist where the compare first differed
+ * is returned through mismatch_off.
+ *
+ * @param off offset in image
+ * @param len length of compare, length of write
+ * @param cmp_bl bufferlist to compare from
+ * @param bl bufferlist to write to image if compare succeeds
+ * @param c aio completion to notify when compare and write is complete
+ * @param mismatch_off (out) offset in bufferlist where compare first differed
+ * @param op_flags see librados.h constants beginning with LIBRADOS_OP_FLAG
+ */
+ ssize_t compare_and_write(uint64_t ofs, size_t len, ceph::bufferlist &cmp_bl,
+ ceph::bufferlist& bl, uint64_t *mismatch_off, int op_flags);
+
+ int aio_write(uint64_t off, size_t len, ceph::bufferlist& bl, RBD::AioCompletion *c);
+ /* @param op_flags see librados.h constants beginning with LIBRADOS_OP_FLAG */
+ int aio_write2(uint64_t off, size_t len, ceph::bufferlist& bl,
+ RBD::AioCompletion *c, int op_flags);
+
+ int aio_discard(uint64_t off, uint64_t len, RBD::AioCompletion *c);
+ int aio_writesame(uint64_t off, size_t len, ceph::bufferlist& bl,
+ RBD::AioCompletion *c, int op_flags);
+ int aio_write_zeroes(uint64_t ofs, size_t len, RBD::AioCompletion *c,
+ int zero_flags, int op_flags);
+
+ int aio_compare_and_write(uint64_t off, size_t len, ceph::bufferlist& cmp_bl,
+ ceph::bufferlist& bl, RBD::AioCompletion *c,
+ uint64_t *mismatch_off, int op_flags);
+
+ /**
+ * read async from image
+ *
+ * The target bufferlist is populated with references to buffers
+ * that contain the data for the given extent of the image.
+ *
+ * NOTE: If caching is enabled, the bufferlist will directly
+ * reference buffers in the cache to avoid an unnecessary data copy.
+ * As a result, if the user intends to modify the buffer contents
+ * directly, they should make a copy first (unconditionally, or when
+ * the reference count on ther underlying buffer is more than 1).
+ *
+ * @param off offset in image
+ * @param len length of read
+ * @param bl bufferlist to read into
+ * @param c aio completion to notify when read is complete
+ */
+ int aio_read(uint64_t off, size_t len, ceph::bufferlist& bl, RBD::AioCompletion *c);
+ /* @param op_flags see librados.h constants beginning with LIBRADOS_OP_FLAG */
+ int aio_read2(uint64_t off, size_t len, ceph::bufferlist& bl,
+ RBD::AioCompletion *c, int op_flags);
+
+ int flush();
+ /**
+ * Start a flush if caching is enabled. Get a callback when
+ * the currently pending writes are on disk.
+ *
+ * @param image the image to flush writes to
+ * @param c what to call when flushing is complete
+ * @returns 0 on success, negative error code on failure
+ */
+ int aio_flush(RBD::AioCompletion *c);
+
+ /**
+ * Drop any cached data for this image
+ *
+ * @returns 0 on success, negative error code on failure
+ */
+ int invalidate_cache();
+
+ int poll_io_events(RBD::AioCompletion **comps, int numcomp);
+
+ int metadata_get(const std::string &key, std::string *value);
+ int metadata_set(const std::string &key, const std::string &value);
+ int metadata_remove(const std::string &key);
+ /**
+ * Returns a pair of key/value for this image
+ */
+ int metadata_list(const std::string &start, uint64_t max, std::map<std::string, ceph::bufferlist> *pairs);
+
+ // RBD image mirroring support functions
+ int mirror_image_enable() CEPH_RBD_DEPRECATED;
+ int mirror_image_enable2(mirror_image_mode_t mode);
+ int mirror_image_disable(bool force);
+ int mirror_image_promote(bool force);
+ int mirror_image_demote();
+ int mirror_image_resync();
+ int mirror_image_create_snapshot(uint64_t *snap_id);
+ int mirror_image_create_snapshot2(uint32_t flags, uint64_t *snap_id);
+ int mirror_image_get_info(mirror_image_info_t *mirror_image_info,
+ size_t info_size);
+ int mirror_image_get_mode(mirror_image_mode_t *mode);
+ int mirror_image_get_global_status(
+ mirror_image_global_status_t *mirror_image_global_status,
+ size_t status_size);
+ int mirror_image_get_status(
+ mirror_image_status_t *mirror_image_status, size_t status_size)
+ CEPH_RBD_DEPRECATED;
+ int mirror_image_get_instance_id(std::string *instance_id);
+ int aio_mirror_image_promote(bool force, RBD::AioCompletion *c);
+ int aio_mirror_image_demote(RBD::AioCompletion *c);
+ int aio_mirror_image_get_info(mirror_image_info_t *mirror_image_info,
+ size_t info_size, RBD::AioCompletion *c);
+ int aio_mirror_image_get_mode(mirror_image_mode_t *mode,
+ RBD::AioCompletion *c);
+ int aio_mirror_image_get_global_status(
+ mirror_image_global_status_t *mirror_image_global_status,
+ size_t status_size, RBD::AioCompletion *c);
+ int aio_mirror_image_get_status(
+ mirror_image_status_t *mirror_image_status, size_t status_size,
+ RBD::AioCompletion *c)
+ CEPH_RBD_DEPRECATED;
+ int aio_mirror_image_create_snapshot(uint32_t flags, uint64_t *snap_id,
+ RBD::AioCompletion *c);
+
+ int update_watch(UpdateWatchCtx *ctx, uint64_t *handle);
+ int update_unwatch(uint64_t handle);
+
+ int list_watchers(std::list<image_watcher_t> &watchers);
+
+ int config_list(std::vector<config_option_t> *options);
+
+ int quiesce_watch(QuiesceWatchCtx *ctx, uint64_t *handle);
+ int quiesce_unwatch(uint64_t handle);
+ void quiesce_complete(uint64_t handle, int r);
+
+private:
+ friend class RBD;
+
+ Image(const Image& rhs);
+ const Image& operator=(const Image& rhs);
+
+ image_ctx_t ctx;
+};
+
+} // namespace librbd
+
+#if __GNUC__ >= 4
+ #pragma GCC diagnostic pop
+#endif
+
+#endif // __LIBRBD_HPP
diff --git a/src/include/rbd/object_map_types.h b/src/include/rbd/object_map_types.h
new file mode 100644
index 000000000..54852caa8
--- /dev/null
+++ b/src/include/rbd/object_map_types.h
@@ -0,0 +1,13 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+#ifndef CEPH_RBD_OBJECT_MAP_TYPES_H
+#define CEPH_RBD_OBJECT_MAP_TYPES_H
+
+#include "include/int_types.h"
+
+static const uint8_t OBJECT_NONEXISTENT = 0;
+static const uint8_t OBJECT_EXISTS = 1;
+static const uint8_t OBJECT_PENDING = 2;
+static const uint8_t OBJECT_EXISTS_CLEAN = 3;
+
+#endif // CEPH_RBD_OBJECT_MAP_TYPES_H
diff --git a/src/include/rbd_types.h b/src/include/rbd_types.h
new file mode 100644
index 000000000..35a1a8bc3
--- /dev/null
+++ b/src/include/rbd_types.h
@@ -0,0 +1,159 @@
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2004-2010 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_RBD_TYPES_H
+#define CEPH_RBD_TYPES_H
+
+#include "include/types.h"
+#include "rbd/features.h"
+
+/* New-style rbd image 'foo' consists of objects
+ * rbd_id.foo - id of image
+ * rbd_header.<id> - image metadata
+ * rbd_object_map.<id> - optional image object map
+ * rbd_data.<id>.00000000
+ * rbd_data.<id>.00000001
+ * ... - data
+ */
+
+#define RBD_HEADER_PREFIX "rbd_header."
+#define RBD_OBJECT_MAP_PREFIX "rbd_object_map."
+#define RBD_DATA_PREFIX "rbd_data."
+#define RBD_ID_PREFIX "rbd_id."
+
+/*
+ * old-style rbd image 'foo' consists of objects
+ * foo.rbd - image metadata
+ * rb.<idhi>.<idlo>.00000000
+ * rb.<idhi>.<idlo>.00000001
+ * ... - data
+ */
+
+#define RBD_SUFFIX ".rbd"
+#define RBD_DIRECTORY "rbd_directory"
+#define RBD_INFO "rbd_info"
+#define RBD_NAMESPACE "rbd_namespace"
+#define RBD_TASK "rbd_task"
+
+/*
+ * rbd_children object in each pool contains omap entries
+ * that map parent (poolid, imageid, snapid) to a list of children
+ * (imageids; snapids aren't required because we get all the snapshot
+ * info from a read of the child's header object anyway).
+ *
+ * The clone operation writes a new item to this child list, and rm or
+ * flatten removes an item, and may remove the whole entry if no children
+ * exist after the rm/flatten.
+ *
+ * When attempting to remove a parent, all pools are searched for
+ * rbd_children objects with entries referring to that parent; if any
+ * exist (and those children exist), the parent removal is prevented.
+ */
+#define RBD_CHILDREN "rbd_children"
+#define RBD_LOCK_NAME "rbd_lock"
+
+/**
+ * rbd_mirroring object in each pool contains pool-specific settings
+ * for configuring mirroring.
+ */
+#define RBD_MIRRORING "rbd_mirroring"
+
+/**
+ * rbd_mirror_leader and rbd_mirror_instance.<instance id> objects are used
+ * for pool-level coordination between rbd-mirror daemons.
+ */
+#define RBD_MIRROR_LEADER "rbd_mirror_leader"
+#define RBD_MIRROR_INSTANCE_PREFIX "rbd_mirror_instance."
+
+#define RBD_MAX_OBJ_NAME_SIZE 96
+#define RBD_MAX_BLOCK_NAME_SIZE 24
+
+/**
+ * Maximum string length of the RBD v2 image id (not including
+ * null termination). This limit was derived from the existing
+ * RBD_MAX_BLOCK_NAME_SIZE limit which needs to hold the "rbd_data."
+ * prefix and null termination.
+ */
+#define RBD_MAX_IMAGE_ID_LENGTH 14
+
+/**
+ * Maximum string length of the RBD block object name prefix (not including
+ * null termination).
+ *
+ * v1 format: rb.<max 8-byte high id>.<max 8-byte low id>.<max 8-byte extra>
+ * v2 format: rbd_data.[<max 19-byte pool id>.]<max 14-byte image id>
+ *
+ * Note: new features might require increasing this maximum prefix length.
+ */
+#define RBD_MAX_BLOCK_NAME_PREFIX_LENGTH 43
+
+#define RBD_COMP_NONE 0
+#define RBD_CRYPT_NONE 0
+
+#define RBD_HEADER_TEXT "<<< Rados Block Device Image >>>\n"
+#define RBD_MIGRATE_HEADER_TEXT "<<< Migrating RBD Image >>>\n"
+#define RBD_HEADER_SIGNATURE "RBD"
+#define RBD_HEADER_VERSION "001.005"
+
+#define RBD_GROUP_INVALID_POOL (-1)
+
+#define RBD_GROUP_HEADER_PREFIX "rbd_group_header."
+
+#define RBD_GROUP_DIRECTORY "rbd_group_directory"
+
+#define RBD_TRASH "rbd_trash"
+
+/**
+ * MON config-key prefix for storing optional remote cluster connectivity
+ * parameters
+ */
+#define RBD_MIRROR_CONFIG_KEY_PREFIX "rbd/mirror/"
+#define RBD_MIRROR_SITE_NAME_CONFIG_KEY RBD_MIRROR_CONFIG_KEY_PREFIX "site_name"
+#define RBD_MIRROR_PEER_CLIENT_ID_CONFIG_KEY RBD_MIRROR_CONFIG_KEY_PREFIX "peer_client_id"
+#define RBD_MIRROR_PEER_CONFIG_KEY_PREFIX RBD_MIRROR_CONFIG_KEY_PREFIX "peer/"
+
+struct rbd_info {
+ ceph_le64 max_id;
+} __attribute__ ((packed));
+
+struct rbd_obj_snap_ondisk {
+ ceph_le64 id;
+ ceph_le64 image_size;
+} __attribute__((packed));
+
+struct rbd_obj_header_ondisk {
+ char text[40];
+ char block_name[RBD_MAX_BLOCK_NAME_SIZE];
+ char signature[4];
+ char version[8];
+ struct {
+ __u8 order;
+ __u8 crypt_type;
+ __u8 comp_type;
+ __u8 unused;
+ } __attribute__((packed)) options;
+ ceph_le64 image_size;
+ ceph_le64 snap_seq;
+ ceph_le32 snap_count;
+ ceph_le32 reserved;
+ ceph_le64 snap_names_len;
+ struct rbd_obj_snap_ondisk snaps[0];
+} __attribute__((packed));
+
+enum {
+ RBD_PROTECTION_STATUS_UNPROTECTED = 0,
+ RBD_PROTECTION_STATUS_UNPROTECTING = 1,
+ RBD_PROTECTION_STATUS_PROTECTED = 2,
+ RBD_PROTECTION_STATUS_LAST = 3
+};
+
+#endif
diff --git a/src/include/scope_guard.h b/src/include/scope_guard.h
new file mode 100644
index 000000000..eacc65e7b
--- /dev/null
+++ b/src/include/scope_guard.h
@@ -0,0 +1,49 @@
+// -*- 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) 2016 Red Hat
+ *
+ * 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 SCOPE_GUARD
+#define SCOPE_GUARD
+
+#include <utility>
+
+template <typename F>
+struct scope_guard {
+ F f;
+ scope_guard() = delete;
+ scope_guard(const scope_guard &) = delete;
+ scope_guard(scope_guard &&) = default;
+ scope_guard & operator=(const scope_guard &) = delete;
+ scope_guard & operator=(scope_guard &&) = default;
+ scope_guard(const F& f) : f(f) {}
+ scope_guard(F &&f) : f(std::move(f)) {}
+ template<typename... Args>
+ scope_guard(std::in_place_t, Args&& ...args) : f(std::forward<Args>(args)...) {}
+ ~scope_guard() {
+ std::move(f)(); // Support at-most-once functions
+ }
+};
+
+template <typename F>
+[[nodiscard("Unassigned scope guards will execute immediately")]]
+scope_guard<F> make_scope_guard(F &&f) {
+ return scope_guard<F>(std::forward<F>(f));
+}
+
+template<typename F, typename... Args>
+[[nodiscard("Unassigned scope guards will execute immediately")]]
+scope_guard<F> make_scope_guard(std::in_place_type_t<F>, Args&& ...args) {
+ return { std::in_place, std::forward<Args>(args)... };
+}
+
+#endif
diff --git a/src/include/sock_compat.h b/src/include/sock_compat.h
new file mode 100644
index 000000000..14b5efa1d
--- /dev/null
+++ b/src/include/sock_compat.h
@@ -0,0 +1,43 @@
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2018 Red Hat, Inc.
+ *
+ * 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_SOCK_COMPAT_H
+#define CEPH_SOCK_COMPAT_H
+
+#include "include/compat.h"
+#include <sys/socket.h>
+
+/*
+ * This optimization may not be available on all platforms (e.g. OSX).
+ * Apparently a similar approach based on TCP_CORK can be used.
+ */
+#ifndef MSG_MORE
+# define MSG_MORE 0
+#endif
+
+/*
+ * On BSD SO_NOSIGPIPE can be set via setsockopt to block SIGPIPE.
+ */
+#ifndef MSG_NOSIGNAL
+# define MSG_NOSIGNAL 0
+# ifdef SO_NOSIGPIPE
+# define CEPH_USE_SO_NOSIGPIPE
+# else
+# define CEPH_USE_SIGPIPE_BLOCKER
+# warning "Using SIGPIPE blocking instead of suppression; this is not well-tested upstream!"
+# endif
+#endif
+
+int socket_cloexec(int domain, int type, int protocol);
+int socketpair_cloexec(int domain, int type, int protocol, int sv[2]);
+int accept_cloexec(int sockfd, struct sockaddr* addr, socklen_t* addrlen);
+
+#endif
diff --git a/src/include/spinlock.h b/src/include/spinlock.h
new file mode 100644
index 000000000..3f12bdc00
--- /dev/null
+++ b/src/include/spinlock.h
@@ -0,0 +1,92 @@
+// -*- 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) 2017 SUSE LINUX GmbH
+ *
+ * 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.
+ *
+ * @author Jesse Williamson <jwilliamson@suse.de>
+ *
+*/
+
+#ifndef CEPH_SPINLOCK_HPP
+#define CEPH_SPINLOCK_HPP
+
+#include <atomic>
+
+namespace ceph {
+inline namespace version_1_0 {
+
+class spinlock;
+
+inline void spin_lock(std::atomic_flag& lock);
+inline void spin_unlock(std::atomic_flag& lock);
+inline void spin_lock(ceph::spinlock& lock);
+inline void spin_unlock(ceph::spinlock& lock);
+
+/* A pre-packaged spinlock type modelling BasicLockable: */
+class spinlock final
+{
+ std::atomic_flag af = ATOMIC_FLAG_INIT;
+
+ public:
+ void lock() {
+ ceph::spin_lock(af);
+ }
+
+ void unlock() noexcept {
+ ceph::spin_unlock(af);
+ }
+};
+
+// Free functions:
+inline void spin_lock(std::atomic_flag& lock)
+{
+ while(lock.test_and_set(std::memory_order_acquire))
+ ;
+}
+
+inline void spin_unlock(std::atomic_flag& lock)
+{
+ lock.clear(std::memory_order_release);
+}
+
+inline void spin_lock(std::atomic_flag *lock)
+{
+ spin_lock(*lock);
+}
+
+inline void spin_unlock(std::atomic_flag *lock)
+{
+ spin_unlock(*lock);
+}
+
+inline void spin_lock(ceph::spinlock& lock)
+{
+ lock.lock();
+}
+
+inline void spin_unlock(ceph::spinlock& lock)
+{
+ lock.unlock();
+}
+
+inline void spin_lock(ceph::spinlock *lock)
+{
+ spin_lock(*lock);
+}
+
+inline void spin_unlock(ceph::spinlock *lock)
+{
+ spin_unlock(*lock);
+}
+
+} // inline namespace (version)
+} // namespace ceph
+
+#endif
diff --git a/src/include/stat.h b/src/include/stat.h
new file mode 100644
index 000000000..19398758e
--- /dev/null
+++ b/src/include/stat.h
@@ -0,0 +1,145 @@
+#ifndef CEPH_STAT_H
+#define CEPH_STAT_H
+
+#include <acconfig.h>
+
+#include <sys/stat.h>
+
+/*
+ * Access time-related `struct stat` members.
+ *
+ * Note that for each of the stat member get/set functions below, setting a
+ * high-res value (stat_set_*_nsec) on a platform without high-res support is
+ * a no-op.
+ */
+
+#ifdef HAVE_STAT_ST_MTIM_TV_NSEC
+
+static inline uint32_t stat_get_mtime_nsec(struct stat *st)
+{
+ return st->st_mtim.tv_nsec;
+}
+
+static inline void stat_set_mtime_nsec(struct stat *st, uint32_t nsec)
+{
+ st->st_mtim.tv_nsec = nsec;
+}
+
+static inline uint32_t stat_get_atime_nsec(struct stat *st)
+{
+ return st->st_atim.tv_nsec;
+}
+
+static inline void stat_set_atime_nsec(struct stat *st, uint32_t nsec)
+{
+ st->st_atim.tv_nsec = nsec;
+}
+
+static inline uint32_t stat_get_ctime_nsec(struct stat *st)
+{
+ return st->st_ctim.tv_nsec;
+}
+
+static inline void stat_set_ctime_nsec(struct stat *st, uint32_t nsec)
+{
+ st->st_ctim.tv_nsec = nsec;
+}
+
+#elif defined(HAVE_STAT_ST_MTIMESPEC_TV_NSEC)
+
+static inline uint32_t stat_get_mtime_nsec(struct stat *st)
+{
+ return st->st_mtimespec.tv_nsec;
+}
+
+static inline void stat_set_mtime_nsec(struct stat *st, uint32_t nsec)
+{
+ st->st_mtimespec.tv_nsec = nsec;
+}
+
+static inline uint32_t stat_get_atime_nsec(struct stat *st)
+{
+ return st->st_atimespec.tv_nsec;
+}
+
+static inline void stat_set_atime_nsec(struct stat *st, uint32_t nsec)
+{
+ st->st_atimespec.tv_nsec = nsec;
+}
+
+static inline uint32_t stat_get_ctime_nsec(struct stat *st)
+{
+ return st->st_ctimespec.tv_nsec;
+}
+
+static inline void stat_set_ctime_nsec(struct stat *st, uint32_t nsec)
+{
+ st->st_ctimespec.tv_nsec = nsec;
+}
+
+#else
+
+static inline uint32_t stat_get_mtime_nsec(struct stat *st)
+{
+ return 0;
+}
+
+static inline void stat_set_mtime_nsec(struct stat *st, uint32_t nsec)
+{
+}
+
+static inline uint32_t stat_get_atime_nsec(struct stat *st)
+{
+ return 0;
+}
+
+static inline void stat_set_atime_nsec(struct stat *st, uint32_t nsec)
+{
+}
+
+static inline uint32_t stat_get_ctime_nsec(struct stat *st)
+{
+ return 0;
+}
+
+static inline void stat_set_ctime_nsec(struct stat *st, uint32_t nsec)
+{
+}
+
+#endif
+
+/*
+ * Access second-resolution `struct stat` members.
+ */
+
+static inline uint32_t stat_get_mtime_sec(struct stat *st)
+{
+ return st->st_mtime;
+}
+
+static inline void stat_set_mtime_sec(struct stat *st, uint32_t sec)
+{
+ st->st_mtime = sec;
+}
+
+static inline uint32_t stat_get_atime_sec(struct stat *st)
+{
+ return st->st_atime;
+}
+
+static inline void stat_set_atime_sec(struct stat *st, uint32_t sec)
+{
+ st->st_atime = sec;
+}
+
+static inline uint32_t stat_get_ctime_sec(struct stat *st)
+{
+ return st->st_ctime;
+}
+
+static inline void stat_set_ctime_sec(struct stat *st, uint32_t sec)
+{
+ st->st_ctime = sec;
+}
+
+#endif
diff --git a/src/include/statlite.h b/src/include/statlite.h
new file mode 100644
index 000000000..0ff4b04e7
--- /dev/null
+++ b/src/include/statlite.h
@@ -0,0 +1,74 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+#ifndef CEPH_STATLITE_H
+#define CEPH_STATLITE_H
+
+extern "C" {
+
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <dirent.h>
+
+#include "include/compat.h"
+
+struct statlite {
+ dev_t st_dev; /* device */
+ ino_t st_ino; /* inode */
+ mode_t st_mode; /* protection */
+ nlink_t st_nlink; /* number of hard links */
+ uid_t st_uid; /* user ID of owner */
+ gid_t st_gid; /* group ID of owner */
+ dev_t st_rdev; /* device type (if inode device)*/
+ unsigned long st_litemask; /* bit mask for optional fields */
+ /***************************************************************/
+ /**** Remaining fields are optional according to st_litemask ***/
+ off_t st_size; /* total size, in bytes */
+ blksize_t st_blksize; /* blocksize for filesystem I/O */
+ blkcnt_t st_blocks; /* number of blocks allocated */
+ struct timespec st_atim; /* Time of last access. */
+ struct timespec st_mtim; /* Time of last modification. */
+ struct timespec st_ctim; /* Time of last status change. */
+ //time_t st_atime; /* time of last access */
+ //time_t st_mtime; /* time of last modification */
+ //time_t st_ctime; /* time of last change */
+};
+
+#define S_STATLITE_SIZE 1
+#define S_STATLITE_BLKSIZE 2
+#define S_STATLITE_BLOCKS 4
+#define S_STATLITE_ATIME 8
+#define S_STATLITE_MTIME 16
+#define S_STATLITE_CTIME 32
+
+#define S_REQUIRESIZE(m) (m | S_STATLITE_SIZE)
+#define S_REQUIREBLKSIZE(m) (m | S_STATLITE_BLKSIZE)
+#define S_REQUIREBLOCKS(m) (m | S_STATLITE_BLOCKS)
+#define S_REQUIREATIME(m) (m | S_STATLITE_ATIME)
+#define S_REQUIREMTIME(m) (m | S_STATLITE_MTIME)
+#define S_REQUIRECTIME(m) (m | S_STATLITE_CTIME)
+
+#define S_ISVALIDSIZE(m) (m & S_STATLITE_SIZE)
+#define S_ISVALIDBLKSIZE(m) (m & S_STATLITE_BLKSIZE)
+#define S_ISVALIDBLOCKS(m) (m & S_STATLITE_BLOCKS)
+#define S_ISVALIDATIME(m) (m & S_STATLITE_ATIME)
+#define S_ISVALIDMTIME(m) (m & S_STATLITE_MTIME)
+#define S_ISVALIDCTIME(m) (m & S_STATLITE_CTIME)
+
+
+// readdirplus etc.
+
+struct dirent_plus {
+ struct dirent d_dirent; /* dirent struct for this entry */
+ struct stat d_stat; /* attributes for this entry */
+ int d_stat_err;/* errno for d_stat, or 0 */
+};
+struct dirent_lite {
+ struct dirent d_dirent; /* dirent struct for this entry */
+ struct statlite d_stat; /* attributes for this entry */
+ int d_stat_err;/* errno for d_stat, or 0 */
+};
+
+}
+#endif
diff --git a/src/include/str_list.h b/src/include/str_list.h
new file mode 100644
index 000000000..cad76c1d6
--- /dev/null
+++ b/src/include/str_list.h
@@ -0,0 +1,97 @@
+#ifndef CEPH_STRLIST_H
+#define CEPH_STRLIST_H
+
+#include <list>
+#include <set>
+#include <string>
+#include <string_view>
+#include <vector>
+
+namespace ceph {
+
+/// Split a string using the given delimiters, passing each piece as a
+/// (non-null-terminated) std::string_view to the callback.
+template <typename Func> // where Func(std::string_view) is a valid call
+void for_each_substr(std::string_view s, const char *delims, Func&& f)
+{
+ auto pos = s.find_first_not_of(delims);
+ while (pos != s.npos) {
+ s.remove_prefix(pos); // trim delims from the front
+ auto end = s.find_first_of(delims);
+ f(s.substr(0, end));
+ pos = s.find_first_not_of(delims, end);
+ }
+}
+
+} // namespace ceph
+
+/**
+ * Split **str** into a list of strings, using the ";,= \t" delimiters and output the result in **str_list**.
+ *
+ * @param [in] str String to split and save as list
+ * @param [out] str_list List modified containing str after it has been split
+**/
+extern void get_str_list(const std::string& str,
+ std::list<std::string>& str_list);
+
+/**
+ * Split **str** into a list of strings, using the **delims** delimiters and output the result in **str_list**.
+ *
+ * @param [in] str String to split and save as list
+ * @param [in] delims characters used to split **str**
+ * @param [out] str_list List modified containing str after it has been split
+**/
+extern void get_str_list(const std::string& str,
+ const char *delims,
+ std::list<std::string>& str_list);
+
+std::list<std::string> get_str_list(const std::string& str,
+ const char *delims = ";,= \t");
+
+/**
+ * Split **str** into a vector of strings, using the ";,= \t" delimiters and output the result in **str_vec**.
+ *
+ * @param [in] str String to split and save as Vector
+ * @param [out] str_vec Vector modified containing str after it has been split
+**/
+void get_str_vec(std::string_view str, std::vector<std::string>& str_vec);
+
+/**
+ * Split **str** into a vector of strings, using the **delims** delimiters and output the result in **str_vec**.
+ *
+ * @param [in] str String to split and save as Vector
+ * @param [in] delims characters used to split **str**
+ * @param [out] str_vec Vector modified containing str after it has been split
+**/
+void get_str_vec(std::string_view str,
+ const char *delims,
+ std::vector<std::string>& str_vec);
+
+std::vector<std::string> get_str_vec(std::string_view str,
+ const char *delims = ";,= \t");
+
+/**
+ * Return a String containing the vector **v** joined with **sep**
+ *
+ * If **v** is empty, the function returns an empty string
+ * For each element in **v**,
+ * it will concatenate this element and **sep** with result
+ *
+ * @param [in] v Vector to join as a String
+ * @param [in] sep String used to join each element from **v**
+ * @return empty string if **v** is empty or concatenated string
+**/
+inline std::string str_join(const std::vector<std::string>& v, const std::string& sep)
+{
+ if (v.empty())
+ return std::string();
+ auto i = v.cbegin();
+ std::string r = *i;
+ for (++i; i != v.cend(); ++i) {
+ r += sep;
+ r += *i;
+ }
+ return r;
+}
+
+#endif
diff --git a/src/include/str_map.h b/src/include/str_map.h
new file mode 100644
index 000000000..7f354fd46
--- /dev/null
+++ b/src/include/str_map.h
@@ -0,0 +1,180 @@
+// -*- 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) 2013 Cloudwatt <libre.licensing@cloudwatt.com>
+ *
+ * Author: Loic Dachary <loic@dachary.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ */
+
+#ifndef CEPH_STRMAP_H
+#define CEPH_STRMAP_H
+
+#define CONST_DELIMS ",;\t\n "
+
+#include <map>
+#include <string>
+#include <sstream>
+
+template <typename Func>
+void for_each_pair(std::string_view s, const char* delims, Func&& f)
+{
+ auto pos = s.find_first_not_of(delims);
+ while (pos != s.npos) {
+ s.remove_prefix(pos); // trim delims from the front
+ auto end = s.find_first_of(delims);
+ auto kv = s.substr(0, end);
+ if (auto equal = kv.find('='); equal != kv.npos) {
+ f(kv.substr(0, equal), kv.substr(equal + 1));
+ } else {
+ f(kv.substr(0, equal), std::string_view());
+ }
+ pos = s.find_first_not_of(delims, end);
+ }
+}
+
+using str_map_t = std::map<std::string,std::string>;
+
+/**
+ * Parse **str** and set **str_map** with the key/value pairs read
+ * from it. The format of **str** is either a well formed JSON object
+ * or a custom key[=value] plain text format.
+ *
+ * JSON is tried first. If successfully parsed into a JSON object, it
+ * is copied into **str_map** verbatim. If it is not a JSON object ( a
+ * string, integer etc. ), -EINVAL is returned and **ss** is set to
+ * a human readable error message.
+ *
+ * If **str** is no valid JSON and if **fallback_to_plain** is set to true
+ * (default: true) it is assumed to be a string containing white space
+ * separated key=value pairs. A white space is either space, tab or newline.
+ * Function **get_str_map** will be leveraged to parse the plain-text
+ * key/value pairs.
+ *
+ * @param [in] str JSON or plain text key/value pairs
+ * @param [out] ss human readable message on error
+ * @param [out] str_map key/value pairs read from str
+ * @param [in] fallback_to_plain attempt parsing as plain-text if json fails
+ * @return **0** on success or a -EINVAL on error.
+ */
+int get_json_str_map(
+ const std::string &str,
+ std::ostream &ss,
+ str_map_t* str_map,
+ bool fallback_to_plain = true);
+
+/**
+ * Parse **str** and set **str_map** with the key/value pairs read from
+ * it. The format of **str** is a number of custom key[=value] pairs in
+ * plain text format.
+ *
+ * The string will be parsed taking **delims** as field delimiters for
+ * key/values. The value is optional resulting in an empty string when
+ * not provided. For example, using white space as delimiters:
+ *
+ * insert your own=political/ideological statement=here
+ *
+ * will be parsed into:
+ *
+ * { "insert": "",
+ * "your": "",
+ * "own": "political/ideological",
+ * "statement": "here" }
+ *
+ * Alternative delimiters may be provided. For instance, specifying
+ * "white space and slash", for the above statement, would be parsed
+ * into:
+ *
+ * { "insert": "",
+ * "your": "",
+ * "own": "political",
+ * "ideological": "",
+ * "statement": "here" }
+ *
+ * See how adding '/' to the delimiters field will spawn a new key without
+ * a set value.
+ *
+ * Always returns 0, as there is no condition for failure.
+ *
+ * @param [in] str plain text key/value pairs
+ * @param [in] delims field delimiters to be used for parsing str
+ * @param [out] str_map key/value pairs parsed from str
+ * @return **0**
+ */
+int get_str_map(
+ const std::string &str,
+ str_map_t* str_map,
+ const char *delims = CONST_DELIMS);
+
+// an alternate form (as we never fail):
+str_map_t get_str_map(
+ const std::string& str,
+ const char* delim = CONST_DELIMS);
+
+/**
+ * Returns the value of **key** in **str_map** if available.
+ *
+ * If **key** is not available in **str_map**, and if **def_val** is
+ * not-NULL then returns **def_val**. Otherwise checks if the value of
+ * **key** is an empty string and if so will return **key**.
+ * If the map contains **key**, the function returns the value of **key**.
+ *
+ * @param[in] str_map Map to obtain **key** from
+ * @param[in] key The key to search for in the map
+ * @param[in] def_val The value to return in case **key** is not present
+ */
+std::string get_str_map_value(
+ const str_map_t& str_map,
+ const std::string &key,
+ const std::string *def_val = nullptr);
+
+/**
+ * Returns the value of **key** in **str_map** if available.
+ *
+ * If **key** is available in **str_map** returns the value of **key**.
+ *
+ * If **key** is not available in **str_map**, and if **def_key**
+ * is not-NULL and available in **str_map**, then returns the value
+ * of **def_key**.
+ *
+ * Otherwise returns an empty string.
+ *
+ * @param[in] str_map Map to obtain **key** or **def_key** from
+ * @param[in] key Key to obtain the value of from **str_map**
+ * @param[in] def_key Key to fallback to if **key** is not present
+ * in **str_map**
+ */
+std::string get_str_map_key(
+ const str_map_t& str_map,
+ const std::string &key,
+ const std::string *fallback_key = nullptr);
+
+// This function's only purpose is to check whether a given map has only
+// ONE key with an empty value (which would mean that 'get_str_map()' read
+// a map in the form of 'VALUE', without any KEY/VALUE pairs) and, in such
+// event, to assign said 'VALUE' to a given 'def_key', such that we end up
+// with a map of the form "m = { 'def_key' : 'VALUE' }" instead of the
+// original "m = { 'VALUE' : '' }".
+int get_conf_str_map_helper(
+ const std::string &str,
+ std::ostringstream &oss,
+ str_map_t* str_map,
+ const std::string &default_key);
+
+std::string get_value_via_strmap(
+ const std::string& conf_string,
+ std::string_view default_key);
+
+std::string get_value_via_strmap(
+ const std::string& conf_string,
+ const std::string& key,
+ std::string_view default_key);
+
+#endif
diff --git a/src/include/stringify.h b/src/include/stringify.h
new file mode 100644
index 000000000..1b2a130c9
--- /dev/null
+++ b/src/include/stringify.h
@@ -0,0 +1,33 @@
+#ifndef __CEPH_STRINGIFY_H
+#define __CEPH_STRINGIFY_H
+
+#include <string>
+#include <sstream>
+
+#include "include/types.h"
+
+template<typename T>
+inline std::string stringify(const T& a) {
+#if defined(__GNUC__) && !(defined(__clang__) || defined(__INTEL_COMPILER))
+ static __thread std::ostringstream ss;
+ ss.str("");
+#else
+ std::ostringstream ss;
+#endif
+ ss << a;
+ return ss.str();
+}
+
+template <class T, class A>
+T joinify(const A &begin, const A &end, const T &t)
+{
+ T result;
+ for (A it = begin; it != end; it++) {
+ if (!result.empty())
+ result.append(t);
+ result.append(*it);
+ }
+ return result;
+}
+
+#endif
diff --git a/src/include/timegm.h b/src/include/timegm.h
new file mode 100644
index 000000000..fb970432d
--- /dev/null
+++ b/src/include/timegm.h
@@ -0,0 +1,79 @@
+// (C) Copyright Howard Hinnant
+// (C) Copyright 2010-2011 Vicente J. Botet Escriba
+// Use, modification and distribution are subject to the Boost Software License,
+// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt).
+
+//===-------------------------- locale ------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// This code was adapted by Vicente from Howard Hinnant's experimental work
+// on chrono i/o to Boost and some functions from libc++/locale to emulate the missing time_get::get()
+
+#ifndef BOOST_CHRONO_IO_TIME_POINT_IO_H
+#define BOOST_CHRONO_IO_TIME_POINT_IO_H
+
+#include <time.h>
+
+static int32_t is_leap(int32_t year) {
+ if(year % 400 == 0)
+ return 1;
+ if(year % 100 == 0)
+ return 0;
+ if(year % 4 == 0)
+ return 1;
+ return 0;
+}
+
+static int32_t days_from_0(int32_t year) {
+ year--;
+ return 365 * year + (year / 400) - (year/100) + (year / 4);
+}
+
+int32_t static days_from_1970(int32_t year) {
+ static const int days_from_0_to_1970 = days_from_0(1970);
+ return days_from_0(year) - days_from_0_to_1970;
+}
+
+static int32_t days_from_1jan(int32_t year,int32_t month,int32_t day) {
+ static const int32_t days[2][12] =
+ {
+ { 0,31,59,90,120,151,181,212,243,273,304,334},
+ { 0,31,60,91,121,152,182,213,244,274,305,335}
+ };
+
+ return days[is_leap(year)][month-1] + day - 1;
+}
+
+static time_t internal_timegm(tm const *t) {
+ int year = t->tm_year + 1900;
+ int month = t->tm_mon;
+ if(month > 11)
+ {
+ year += month/12;
+ month %= 12;
+ }
+ else if(month < 0)
+ {
+ int years_diff = (-month + 11)/12;
+ year -= years_diff;
+ month+=12 * years_diff;
+ }
+ month++;
+ int day = t->tm_mday;
+ int day_of_year = days_from_1jan(year,month,day);
+ int days_since_epoch = days_from_1970(year) + day_of_year ;
+
+ time_t seconds_in_day = 3600 * 24;
+ time_t result = seconds_in_day * days_since_epoch + 3600 * t->tm_hour + 60 * t->tm_min + t->tm_sec;
+
+ return result;
+}
+
+#endif
diff --git a/src/include/types.h b/src/include/types.h
new file mode 100644
index 000000000..a76360db4
--- /dev/null
+++ b/src/include/types.h
@@ -0,0 +1,629 @@
+// -*- 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-2006 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_TYPES_H
+#define CEPH_TYPES_H
+
+// this is needed for ceph_fs to compile in userland
+#include "int_types.h"
+#include "byteorder.h"
+
+#include "uuid.h"
+
+#include <netinet/in.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include "ceph_fs.h"
+#include "ceph_frag.h"
+#include "rbd_types.h"
+
+#ifdef __cplusplus
+#ifndef _BACKWARD_BACKWARD_WARNING_H
+#define _BACKWARD_BACKWARD_WARNING_H // make gcc 4.3 shut up about hash_*
+#endif
+#endif
+
+extern "C" {
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "statlite.h"
+}
+
+#include <string>
+#include <list>
+#include <set>
+#include <boost/container/flat_set.hpp>
+#include <boost/container/flat_map.hpp>
+#include <map>
+#include <vector>
+#include <optional>
+#include <ostream>
+#include <iomanip>
+
+
+#include "include/unordered_map.h"
+
+#include "object.h"
+#include "intarith.h"
+
+#include "acconfig.h"
+
+#include "assert.h"
+
+// DARWIN compatibility
+#ifdef __APPLE__
+typedef long long loff_t;
+typedef long long off64_t;
+#define O_DIRECT 00040000
+#endif
+
+// FreeBSD compatibility
+#ifdef __FreeBSD__
+typedef off_t loff_t;
+typedef off_t off64_t;
+#endif
+
+#if defined(__sun) || defined(_AIX)
+typedef off_t loff_t;
+#endif
+
+
+// -- io helpers --
+
+// Forward declare all the I/O helpers so strict ADL can find them in
+// the case of containers of containers. I'm tempted to abstract this
+// stuff using template templates like I did for denc.
+
+namespace std {
+template<class A, class B>
+inline std::ostream& operator<<(std::ostream&out, const std::pair<A,B>& v);
+template<class A, class Alloc>
+inline std::ostream& operator<<(std::ostream& out, const std::vector<A,Alloc>& v);
+template<class A, std::size_t N, class Alloc>
+inline std::ostream& operator<<(std::ostream& out, const boost::container::small_vector<A,N,Alloc>& v);
+template<class A, class Comp, class Alloc>
+inline std::ostream& operator<<(std::ostream& out, const std::deque<A,Alloc>& v);
+template<typename... Ts>
+inline std::ostream& operator<<(std::ostream& out, const std::tuple<Ts...> &t);
+template<typename T>
+inline std::ostream& operator<<(std::ostream& out, const std::optional<T> &t);
+template<class A, class Alloc>
+inline std::ostream& operator<<(std::ostream& out, const std::list<A,Alloc>& ilist);
+template<class A, class Comp, class Alloc>
+inline std::ostream& operator<<(std::ostream& out, const std::set<A, Comp, Alloc>& iset);
+template<class A, class Comp, class Alloc>
+inline std::ostream& operator<<(std::ostream& out, const std::multiset<A,Comp,Alloc>& iset);
+template<class A, class B, class Comp, class Alloc>
+inline std::ostream& operator<<(std::ostream& out, const std::map<A,B,Comp,Alloc>& m);
+template<class A, class B, class Comp, class Alloc>
+inline std::ostream& operator<<(std::ostream& out, const std::multimap<A,B,Comp,Alloc>& m);
+}
+
+namespace boost {
+template<typename... Ts>
+inline std::ostream& operator<<(std::ostream& out, const boost::tuple<Ts...> &t);
+
+namespace container {
+template<class A, class Comp, class Alloc>
+inline std::ostream& operator<<(std::ostream& out, const boost::container::flat_set<A, Comp, Alloc>& iset);
+template<class A, class B, class Comp, class Alloc>
+inline std::ostream& operator<<(std::ostream& out, const boost::container::flat_map<A, B, Comp, Alloc>& iset);
+}
+}
+
+namespace std {
+template<class A, class B>
+inline std::ostream& operator<<(std::ostream& out, const std::pair<A,B>& v) {
+ return out << v.first << "," << v.second;
+}
+
+template<class A, class Alloc>
+inline std::ostream& operator<<(std::ostream& out, const std::vector<A,Alloc>& v) {
+ bool first = true;
+ out << "[";
+ for (const auto& p : v) {
+ if (!first) out << ",";
+ out << p;
+ first = false;
+ }
+ out << "]";
+ return out;
+}
+
+template<class A, std::size_t N, class Alloc>
+inline std::ostream& operator<<(std::ostream& out, const boost::container::small_vector<A,N,Alloc>& v) {
+ bool first = true;
+ out << "[";
+ for (const auto& p : v) {
+ if (!first) out << ",";
+ out << p;
+ first = false;
+ }
+ out << "]";
+ return out;
+}
+
+template<class A, class Alloc>
+inline std::ostream& operator<<(std::ostream& out, const std::deque<A,Alloc>& v) {
+ out << "<";
+ for (auto p = v.begin(); p != v.end(); ++p) {
+ if (p != v.begin()) out << ",";
+ out << *p;
+ }
+ out << ">";
+ return out;
+}
+
+template<typename... Ts>
+inline std::ostream& operator<<(std::ostream& out, const std::tuple<Ts...> &t) {
+ auto f = [n = sizeof...(Ts), i = 0U, &out](const auto& e) mutable {
+ out << e;
+ if (++i != n)
+ out << ",";
+ };
+ ceph::for_each(t, f);
+ return out;
+}
+
+// Mimics boost::optional
+template<typename T>
+inline std::ostream& operator<<(std::ostream& out, const std::optional<T> &t) {
+ if (!t)
+ out << "--" ;
+ else
+ out << ' ' << *t ;
+ return out;
+}
+
+template<class A, class Alloc>
+inline std::ostream& operator<<(std::ostream& out, const std::list<A,Alloc>& ilist) {
+ for (auto it = ilist.begin();
+ it != ilist.end();
+ ++it) {
+ if (it != ilist.begin()) out << ",";
+ out << *it;
+ }
+ return out;
+}
+
+template<class A, class Comp, class Alloc>
+inline std::ostream& operator<<(std::ostream& out, const std::set<A, Comp, Alloc>& iset) {
+ for (auto it = iset.begin();
+ it != iset.end();
+ ++it) {
+ if (it != iset.begin()) out << ",";
+ out << *it;
+ }
+ return out;
+}
+
+template<class A, class Comp, class Alloc>
+inline std::ostream& operator<<(std::ostream& out, const std::multiset<A,Comp,Alloc>& iset) {
+ for (auto it = iset.begin();
+ it != iset.end();
+ ++it) {
+ if (it != iset.begin()) out << ",";
+ out << *it;
+ }
+ return out;
+}
+
+template<class A, class B, class Comp, class Alloc>
+inline std::ostream& operator<<(std::ostream& out, const std::map<A,B,Comp,Alloc>& m)
+{
+ out << "{";
+ for (auto it = m.begin();
+ it != m.end();
+ ++it) {
+ if (it != m.begin()) out << ",";
+ out << it->first << "=" << it->second;
+ }
+ out << "}";
+ return out;
+}
+
+template<class A, class B, class Comp, class Alloc>
+inline std::ostream& operator<<(std::ostream& out, const std::multimap<A,B,Comp,Alloc>& m)
+{
+ out << "{{";
+ for (auto it = m.begin();
+ it != m.end();
+ ++it) {
+ if (it != m.begin()) out << ",";
+ out << it->first << "=" << it->second;
+ }
+ out << "}}";
+ return out;
+}
+
+} // namespace std
+
+namespace boost {
+namespace tuples {
+template<typename A, typename B, typename C>
+inline std::ostream& operator<<(std::ostream& out, const boost::tuples::tuple<A, B, C> &t) {
+ return out << boost::get<0>(t) << ","
+ << boost::get<1>(t) << ","
+ << boost::get<2>(t);
+}
+}
+namespace container {
+template<class A, class Comp, class Alloc>
+inline std::ostream& operator<<(std::ostream& out, const boost::container::flat_set<A, Comp, Alloc>& iset) {
+ for (auto it = iset.begin();
+ it != iset.end();
+ ++it) {
+ if (it != iset.begin()) out << ",";
+ out << *it;
+ }
+ return out;
+}
+
+template<class A, class B, class Comp, class Alloc>
+inline std::ostream& operator<<(std::ostream& out, const boost::container::flat_map<A, B, Comp, Alloc>& m) {
+ for (auto it = m.begin();
+ it != m.end();
+ ++it) {
+ if (it != m.begin()) out << ",";
+ out << it->first << "=" << it->second;
+ }
+ return out;
+}
+}
+} // namespace boost
+
+
+
+/*
+ * comparators for stl containers
+ */
+// for ceph::unordered_map:
+// ceph::unordered_map<const char*, long, hash<const char*>, eqstr> vals;
+struct eqstr
+{
+ bool operator()(const char* s1, const char* s2) const
+ {
+ return strcmp(s1, s2) == 0;
+ }
+};
+
+// for set, map
+struct ltstr
+{
+ bool operator()(const char* s1, const char* s2) const
+ {
+ return strcmp(s1, s2) < 0;
+ }
+};
+
+
+namespace ceph {
+ class Formatter;
+}
+
+#include "encoding.h"
+
+WRITE_RAW_ENCODER(ceph_fsid)
+WRITE_RAW_ENCODER(ceph_file_layout)
+WRITE_RAW_ENCODER(ceph_dir_layout)
+WRITE_RAW_ENCODER(ceph_mds_session_head)
+WRITE_RAW_ENCODER(ceph_mds_request_head_legacy)
+WRITE_RAW_ENCODER(ceph_mds_request_release)
+WRITE_RAW_ENCODER(ceph_filelock)
+WRITE_RAW_ENCODER(ceph_mds_caps_head)
+WRITE_RAW_ENCODER(ceph_mds_caps_export_body)
+WRITE_RAW_ENCODER(ceph_mds_caps_non_export_body)
+WRITE_RAW_ENCODER(ceph_mds_cap_peer)
+WRITE_RAW_ENCODER(ceph_mds_cap_release)
+WRITE_RAW_ENCODER(ceph_mds_cap_item)
+WRITE_RAW_ENCODER(ceph_mds_lease)
+WRITE_RAW_ENCODER(ceph_mds_snap_head)
+WRITE_RAW_ENCODER(ceph_mds_snap_realm)
+WRITE_RAW_ENCODER(ceph_mds_reply_head)
+WRITE_RAW_ENCODER(ceph_mds_reply_cap)
+WRITE_RAW_ENCODER(ceph_mds_cap_reconnect)
+WRITE_RAW_ENCODER(ceph_mds_snaprealm_reconnect)
+WRITE_RAW_ENCODER(ceph_frag_tree_split)
+WRITE_RAW_ENCODER(ceph_osd_reply_head)
+WRITE_RAW_ENCODER(ceph_osd_op)
+WRITE_RAW_ENCODER(ceph_msg_header)
+WRITE_RAW_ENCODER(ceph_msg_footer)
+WRITE_RAW_ENCODER(ceph_msg_footer_old)
+WRITE_RAW_ENCODER(ceph_mon_subscribe_item)
+
+WRITE_RAW_ENCODER(ceph_mon_statfs)
+WRITE_RAW_ENCODER(ceph_mon_statfs_reply)
+
+// ----------------------
+// some basic types
+
+// NOTE: these must match ceph_fs.h typedefs
+typedef uint64_t ceph_tid_t; // transaction id
+typedef uint64_t version_t;
+typedef __u32 epoch_t; // map epoch (32bits -> 13 epochs/second for 10 years)
+
+// --------------------------------------
+// identify individual mount clients by 64bit value
+
+struct client_t {
+ int64_t v;
+
+ // cppcheck-suppress noExplicitConstructor
+ client_t(int64_t _v = -2) : v(_v) {}
+
+ void encode(ceph::buffer::list& bl) const {
+ using ceph::encode;
+ encode(v, bl);
+ }
+ void decode(ceph::buffer::list::const_iterator& bl) {
+ using ceph::decode;
+ decode(v, bl);
+ }
+};
+WRITE_CLASS_ENCODER(client_t)
+
+static inline bool operator==(const client_t& l, const client_t& r) { return l.v == r.v; }
+static inline bool operator!=(const client_t& l, const client_t& r) { return l.v != r.v; }
+static inline bool operator<(const client_t& l, const client_t& r) { return l.v < r.v; }
+static inline bool operator<=(const client_t& l, const client_t& r) { return l.v <= r.v; }
+static inline bool operator>(const client_t& l, const client_t& r) { return l.v > r.v; }
+static inline bool operator>=(const client_t& l, const client_t& r) { return l.v >= r.v; }
+
+static inline bool operator>=(const client_t& l, int64_t o) { return l.v >= o; }
+static inline bool operator<(const client_t& l, int64_t o) { return l.v < o; }
+
+inline std::ostream& operator<<(std::ostream& out, const client_t& c) {
+ return out << c.v;
+}
+
+
+
+// --
+
+namespace {
+inline std::ostream& format_u(std::ostream& out, const uint64_t v, const uint64_t n,
+ const int index, const uint64_t mult, const char* u)
+ {
+ char buffer[32];
+
+ if (index == 0) {
+ (void) snprintf(buffer, sizeof(buffer), "%" PRId64 "%s", n, u);
+ } else if ((v % mult) == 0) {
+ // If this is an even multiple of the base, always display
+ // without any decimal fraction.
+ (void) snprintf(buffer, sizeof(buffer), "%" PRId64 "%s", n, u);
+ } else {
+ // We want to choose a precision that reflects the best choice
+ // for fitting in 5 characters. This can get rather tricky when
+ // we have numbers that are very close to an order of magnitude.
+ // For example, when displaying 10239 (which is really 9.999K),
+ // we want only a single place of precision for 10.0K. We could
+ // develop some complex heuristics for this, but it's much
+ // easier just to try each combination in turn.
+ int i;
+ for (i = 2; i >= 0; i--) {
+ if (snprintf(buffer, sizeof(buffer), "%.*f%s", i,
+ static_cast<double>(v) / mult, u) <= 7)
+ break;
+ }
+ }
+
+ return out << buffer;
+ }
+}
+
+/*
+ * Use this struct to pretty print values that should be formatted with a
+ * decimal unit prefix (the classic SI units). No actual unit will be added.
+ */
+struct si_u_t {
+ uint64_t v;
+ explicit si_u_t(uint64_t _v) : v(_v) {};
+};
+
+inline std::ostream& operator<<(std::ostream& out, const si_u_t& b)
+{
+ uint64_t n = b.v;
+ int index = 0;
+ uint64_t mult = 1;
+ const char* u[] = {"", "k", "M", "G", "T", "P", "E"};
+
+ while (n >= 1000 && index < 7) {
+ n /= 1000;
+ index++;
+ mult *= 1000;
+ }
+
+ return format_u(out, b.v, n, index, mult, u[index]);
+}
+
+/*
+ * Use this struct to pretty print values that should be formatted with a
+ * binary unit prefix (IEC units). Since binary unit prefixes are to be used for
+ * "multiples of units in data processing, data transmission, and digital
+ * information" (so bits and bytes) and so far bits are not printed, the unit
+ * "B" for "byte" is added besides the multiplier.
+ */
+struct byte_u_t {
+ uint64_t v;
+ explicit byte_u_t(uint64_t _v) : v(_v) {};
+};
+
+inline std::ostream& operator<<(std::ostream& out, const byte_u_t& b)
+{
+ uint64_t n = b.v;
+ int index = 0;
+ const char* u[] = {" B", " KiB", " MiB", " GiB", " TiB", " PiB", " EiB"};
+
+ while (n >= 1024 && index < 7) {
+ n /= 1024;
+ index++;
+ }
+
+ return format_u(out, b.v, n, index, 1ULL << (10 * index), u[index]);
+}
+
+inline std::ostream& operator<<(std::ostream& out, const ceph_mon_subscribe_item& i)
+{
+ return out << (long)i.start
+ << ((i.flags & CEPH_SUBSCRIBE_ONETIME) ? "" : "+");
+}
+
+struct weightf_t {
+ float v;
+ // cppcheck-suppress noExplicitConstructor
+ weightf_t(float _v) : v(_v) {}
+};
+
+inline std::ostream& operator<<(std::ostream& out, const weightf_t& w)
+{
+ if (w.v < -0.01F) {
+ return out << "-";
+ } else if (w.v < 0.000001F) {
+ return out << "0";
+ } else {
+ std::streamsize p = out.precision();
+ return out << std::fixed << std::setprecision(5) << w.v << std::setprecision(p);
+ }
+}
+
+struct shard_id_t {
+ int8_t id;
+
+ shard_id_t() : id(0) {}
+ explicit shard_id_t(int8_t _id) : id(_id) {}
+
+ operator int8_t() const { return id; }
+
+ const static shard_id_t NO_SHARD;
+
+ void encode(ceph::buffer::list &bl) const {
+ using ceph::encode;
+ encode(id, bl);
+ }
+ void decode(ceph::buffer::list::const_iterator &bl) {
+ using ceph::decode;
+ decode(id, bl);
+ }
+
+ bool operator==(const shard_id_t&) const = default;
+ auto operator<=>(const shard_id_t&) const = default;
+};
+WRITE_CLASS_ENCODER(shard_id_t)
+std::ostream &operator<<(std::ostream &lhs, const shard_id_t &rhs);
+
+#if defined(__sun) || defined(_AIX) || defined(__APPLE__) || \
+ defined(__FreeBSD__) || defined(_WIN32)
+extern "C" {
+__s32 ceph_to_hostos_errno(__s32 e);
+__s32 hostos_to_ceph_errno(__s32 e);
+}
+#else
+#define ceph_to_hostos_errno(e) (e)
+#define hostos_to_ceph_errno(e) (e)
+#endif
+
+struct errorcode32_t {
+ int32_t code;
+
+ errorcode32_t() : code(0) {}
+ // cppcheck-suppress noExplicitConstructor
+ explicit errorcode32_t(int32_t i) : code(i) {}
+
+ operator int() const { return code; }
+ int* operator&() { return &code; }
+ errorcode32_t& operator=(int32_t i) {
+ code = i;
+ return *this;
+ }
+ bool operator==(const errorcode32_t&) const = default;
+ auto operator<=>(const errorcode32_t&) const = default;
+
+ void encode(ceph::buffer::list &bl) const {
+ using ceph::encode;
+ __s32 newcode = hostos_to_ceph_errno(code);
+ encode(newcode, bl);
+ }
+ void decode(ceph::buffer::list::const_iterator &bl) {
+ using ceph::decode;
+ decode(code, bl);
+ code = ceph_to_hostos_errno(code);
+ }
+};
+WRITE_CLASS_ENCODER(errorcode32_t)
+
+template <uint8_t S>
+struct sha_digest_t {
+ constexpr static uint32_t SIZE = S;
+ // TODO: we might consider std::array in the future. Avoiding it for now
+ // as sha_digest_t is a part of our public API.
+ unsigned char v[S] = {0};
+
+ std::string to_str() const {
+ char str[S * 2 + 1] = {0};
+ str[0] = '\0';
+ for (size_t i = 0; i < S; i++) {
+ ::sprintf(&str[i * 2], "%02x", static_cast<int>(v[i]));
+ }
+ return std::string(str);
+ }
+ sha_digest_t(const unsigned char *_v) { memcpy(v, _v, SIZE); };
+ sha_digest_t() {}
+
+ bool operator==(const sha_digest_t& r) const {
+ return ::memcmp(v, r.v, SIZE) == 0;
+ }
+ bool operator!=(const sha_digest_t& r) const {
+ return ::memcmp(v, r.v, SIZE) != 0;
+ }
+
+ void encode(ceph::buffer::list &bl) const {
+ // copy to avoid reinterpret_cast, is_pod and other nasty things
+ using ceph::encode;
+ std::array<unsigned char, SIZE> tmparr;
+ memcpy(tmparr.data(), v, SIZE);
+ encode(tmparr, bl);
+ }
+ void decode(ceph::buffer::list::const_iterator &bl) {
+ using ceph::decode;
+ std::array<unsigned char, SIZE> tmparr;
+ decode(tmparr, bl);
+ memcpy(v, tmparr.data(), SIZE);
+ }
+};
+
+template<uint8_t S>
+inline std::ostream &operator<<(std::ostream &out, const sha_digest_t<S> &b) {
+ std::string str = b.to_str();
+ return out << str;
+}
+
+#if FMT_VERSION >= 90000
+template <uint8_t S> struct fmt::formatter<sha_digest_t<S>> : fmt::ostream_formatter {};
+#endif
+
+using sha1_digest_t = sha_digest_t<20>;
+WRITE_CLASS_ENCODER(sha1_digest_t)
+
+using sha256_digest_t = sha_digest_t<32>;
+WRITE_CLASS_ENCODER(sha256_digest_t)
+
+using sha512_digest_t = sha_digest_t<64>;
+
+using md5_digest_t = sha_digest_t<16>;
+WRITE_CLASS_ENCODER(md5_digest_t)
+
+
+#endif
diff --git a/src/include/unordered_map.h b/src/include/unordered_map.h
new file mode 100644
index 000000000..aee5f5a76
--- /dev/null
+++ b/src/include/unordered_map.h
@@ -0,0 +1,11 @@
+#ifndef CEPH_UNORDERED_MAP_H
+#define CEPH_UNORDERED_MAP_H
+
+#include <unordered_map>
+
+namespace ceph {
+ using std::unordered_map;
+ using std::unordered_multimap;
+}
+
+#endif
diff --git a/src/include/unordered_set.h b/src/include/unordered_set.h
new file mode 100644
index 000000000..e30e1799e
--- /dev/null
+++ b/src/include/unordered_set.h
@@ -0,0 +1,10 @@
+#ifndef CEPH_UNORDERED_SET_H
+#define CEPH_UNORDERED_SET_H
+
+#include <unordered_set>
+
+namespace ceph {
+ using std::unordered_set;
+}
+
+#endif
diff --git a/src/include/uses_allocator.h b/src/include/uses_allocator.h
new file mode 100644
index 000000000..35cdbd709
--- /dev/null
+++ b/src/include/uses_allocator.h
@@ -0,0 +1,266 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+// Derived from:
+/* uses_allocator.h -*-C++-*-
+ *
+ * Copyright (C) 2016 Pablo Halpern <phalpern@halpernwightsoftware.com>
+ * Distributed under the Boost Software License - Version 1.0
+ */
+// Downloaded from https://github.com/phalpern/uses-allocator.git
+
+#pragma once
+
+#include <memory>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+namespace ceph {
+
+namespace internal {
+template <class T, class Tuple, std::size_t... Indexes>
+T make_from_tuple_imp(Tuple&& t, std::index_sequence<Indexes...>)
+{
+ return T(std::get<Indexes>(std::forward<Tuple>(t))...);
+}
+} // namespace internal
+
+template<class T, class Tuple>
+T make_from_tuple(Tuple&& args_tuple)
+{
+ using namespace internal;
+ using Indices = std::make_index_sequence<std::tuple_size_v<
+ std::decay_t<Tuple>>>;
+ return make_from_tuple_imp<T>(std::forward<Tuple>(args_tuple), Indices{});
+}
+
+////////////////////////////////////////////////////////////////////////
+
+// Forward declaration
+template <class T, class Alloc, class... Args>
+auto uses_allocator_construction_args(const Alloc& a, Args&&... args);
+
+namespace internal {
+
+template <class T, class A>
+struct has_allocator : std::uses_allocator<T, A> { };
+
+// Specialization of `has_allocator` for `std::pair`
+template <class T1, class T2, class A>
+struct has_allocator<std::pair<T1, T2>, A>
+ : std::integral_constant<bool, has_allocator<T1, A>::value ||
+ has_allocator<T2, A>::value>
+{
+};
+
+template <bool V> using boolean_constant = std::integral_constant<bool, V>;
+
+template <class T> struct is_pair : std::false_type { };
+
+template <class T1, class T2>
+struct is_pair<std::pair<T1, T2>> : std::true_type { };
+
+// Return a tuple of arguments appropriate for uses-allocator construction
+// with allocator `Alloc` and ctor arguments `Args`.
+// This overload is handles types for which `has_allocator<T, Alloc>` is false.
+template <class T, class Unused1, class Unused2, class Alloc, class... Args>
+auto uses_allocator_args_imp(Unused1 /* is_pair */,
+ std::false_type /* has_allocator */,
+ Unused2 /* uses prefix allocator arg */,
+ const Alloc& /* ignored */,
+ Args&&... args)
+{
+ // Allocator is ignored
+ return std::forward_as_tuple(std::forward<Args>(args)...);
+}
+
+// Return a tuple of arguments appropriate for uses-allocator construction
+// with allocator `Alloc` and ctor arguments `Args`.
+// This overload handles non-pair `T` for which `has_allocator<T, Alloc>` is
+// true and constructor `T(allocator_arg_t, a, args...)` is valid.
+template <class T, class Alloc, class... Args>
+auto uses_allocator_args_imp(std::false_type /* is_pair */,
+ std::true_type /* has_allocator */,
+ std::true_type /* uses prefix allocator arg */,
+ const Alloc& a,
+ Args&&... args)
+{
+ // Allocator added to front of argument list, after `allocator_arg`.
+ return std::tuple<std::allocator_arg_t, const Alloc&,
+ Args&&...>(std::allocator_arg, a, std::forward<Args>(args)...);
+}
+
+// Return a tuple of arguments appropriate for uses-allocator construction
+// with allocator `Alloc` and ctor arguments `Args`.
+// This overload handles non-pair `T` for which `has_allocator<T, Alloc>` is
+// true and constructor `T(allocator_arg_t, a, args...)` NOT valid.
+// This function will produce invalid results unless `T(args..., a)` is valid.
+template <class T1, class Alloc, class... Args>
+auto uses_allocator_args_imp(std::false_type /* is_pair */,
+ std::true_type /* has_allocator */,
+ std::false_type /* prefix allocator arg */,
+ const Alloc& a,
+ Args&&... args)
+{
+ // Allocator added to end of argument list
+ return std::forward_as_tuple(std::forward<Args>(args)..., a);
+}
+
+// Return a tuple of arguments appropriate for uses-allocator construction
+// with allocator `Alloc` and ctor arguments `Args`.
+// This overload handles specializations of `T` = `std::pair` for which
+// `has_allocator<T, Alloc>` is true for either or both of the elements and
+// piecewise_construct arguments are passed in.
+template <class T, class Alloc, class Tuple1, class Tuple2>
+auto uses_allocator_args_imp(std::true_type /* is_pair */,
+ std::true_type /* has_allocator */,
+ std::false_type /* prefix allocator arg */,
+ const Alloc& a,
+ std::piecewise_construct_t,
+ Tuple1&& x, Tuple2&& y)
+{
+ using T1 = typename T::first_type;
+ using T2 = typename T::second_type;
+
+ return std::make_tuple(
+ std::piecewise_construct,
+ std::apply([&a](auto&&... args1) -> auto {
+ return uses_allocator_construction_args<T1>(
+ a, std::forward<decltype(args1)>(args1)...);
+ }, std::forward<Tuple1>(x)),
+ std::apply([&a](auto&&... args2) -> auto {
+ return uses_allocator_construction_args<T2>(
+ a, std::forward<decltype(args2)>(args2)...);
+ }, std::forward<Tuple2>(y))
+ );
+}
+
+// Return a tuple of arguments appropriate for uses-allocator construction
+// with allocator `Alloc` and ctor arguments `Args`.
+// This overload handles specializations of `T` = `std::pair` for which
+// `has_allocator<T, Alloc>` is true for either or both of the elements and
+// no other constructor arguments are passed in.
+template <class T, class Alloc>
+auto uses_allocator_args_imp(std::true_type /* is_pair */,
+ std::true_type /* has_allocator */,
+ std::false_type /* prefix allocator arg */,
+ const Alloc& a)
+{
+ // using T1 = typename T::first_type;
+ // using T2 = typename T::second_type;
+
+ // return std::make_tuple(
+ // piecewise_construct,
+ // uses_allocator_construction_args<T1>(a),
+ // uses_allocator_construction_args<T2>(a));
+ return uses_allocator_construction_args<T>(a, std::piecewise_construct,
+ std::tuple<>{}, std::tuple<>{});
+}
+
+// Return a tuple of arguments appropriate for uses-allocator construction
+// with allocator `Alloc` and ctor arguments `Args`.
+// This overload handles specializations of `T` = `std::pair` for which
+// `has_allocator<T, Alloc>` is true for either or both of the elements and
+// a single argument of type const-lvalue-of-pair is passed in.
+template <class T, class Alloc, class U1, class U2>
+auto uses_allocator_args_imp(std::true_type /* is_pair */,
+ std::true_type /* has_allocator */,
+ std::false_type /* prefix allocator arg */,
+ const Alloc& a,
+ const std::pair<U1, U2>& arg)
+{
+ // using T1 = typename T::first_type;
+ // using T2 = typename T::second_type;
+
+ // return std::make_tuple(
+ // piecewise_construct,
+ // uses_allocator_construction_args<T1>(a, arg.first),
+ // uses_allocator_construction_args<T2>(a, arg.second));
+ return uses_allocator_construction_args<T>(a, std::piecewise_construct,
+ std::forward_as_tuple(arg.first),
+ std::forward_as_tuple(arg.second));
+}
+
+// Return a tuple of arguments appropriate for uses-allocator construction
+// with allocator `Alloc` and ctor arguments `Args`.
+// This overload handles specializations of `T` = `std::pair` for which
+// `has_allocator<T, Alloc>` is true for either or both of the elements and
+// a single argument of type rvalue-of-pair is passed in.
+template <class T, class Alloc, class U1, class U2>
+auto uses_allocator_args_imp(std::true_type /* is_pair */,
+ std::true_type /* has_allocator */,
+ std::false_type /* prefix allocator arg */,
+ const Alloc& a,
+ std::pair<U1, U2>&& arg)
+{
+ // using T1 = typename T::first_type;
+ // using T2 = typename T::second_type;
+
+ // return std::make_tuple(
+ // piecewise_construct,
+ // uses_allocator_construction_args<T1>(a, forward<U1>(arg.first)),
+ // uses_allocator_construction_args<T2>(a, forward<U2>(arg.second)));
+ return uses_allocator_construction_args<T>(a, std::piecewise_construct,
+ std::forward_as_tuple(std::forward<U1>(arg.first)),
+ std::forward_as_tuple(std::forward<U2>(arg.second)));
+}
+
+// Return a tuple of arguments appropriate for uses-allocator construction
+// with allocator `Alloc` and ctor arguments `Args`.
+// This overload handles specializations of `T` = `std::pair` for which
+// `has_allocator<T, Alloc>` is true for either or both of the elements and
+// two additional constructor arguments are passed in.
+template <class T, class Alloc, class U1, class U2>
+auto uses_allocator_args_imp(std::true_type /* is_pair */,
+ std::true_type /* has_allocator */,
+ std::false_type /* prefix allocator arg */,
+ const Alloc& a,
+ U1&& arg1, U2&& arg2)
+{
+ // using T1 = typename T::first_type;
+ // using T2 = typename T::second_type;
+
+ // return std::make_tuple(
+ // piecewise_construct,
+ // uses_allocator_construction_args<T1>(a, forward<U1>(arg1)),
+ // uses_allocator_construction_args<T2>(a, forward<U2>(arg2)));
+ return uses_allocator_construction_args<T>(
+ a, std::piecewise_construct,
+ std::forward_as_tuple(std::forward<U1>(arg1)),
+ std::forward_as_tuple(std::forward<U2>(arg2)));
+}
+
+} // close namespace internal
+
+template <class T, class Alloc, class... Args>
+auto uses_allocator_construction_args(const Alloc& a, Args&&... args)
+{
+ using namespace internal;
+ return uses_allocator_args_imp<T>(is_pair<T>(),
+ has_allocator<T, Alloc>(),
+ std::is_constructible<T, std::allocator_arg_t,
+ Alloc, Args...>(),
+ a, std::forward<Args>(args)...);
+}
+
+template <class T, class Alloc, class... Args>
+T make_obj_using_allocator(const Alloc& a, Args&&... args)
+{
+ return make_from_tuple<T>(
+ uses_allocator_construction_args<T>(a, std::forward<Args>(args)...));
+}
+
+template <class T, class Alloc, class... Args>
+T* uninitialized_construct_using_allocator(T* p,
+ const Alloc& a,
+ Args&&... args)
+{
+ return std::apply([p](auto&&... args2){
+ return ::new(static_cast<void*>(p))
+ T(std::forward<decltype(args2)>(args2)...);
+ }, uses_allocator_construction_args<T>(
+ a, std::forward<Args>(args)...));
+}
+
+} // namespace ceph
diff --git a/src/include/util.h b/src/include/util.h
new file mode 100644
index 000000000..acad4a52c
--- /dev/null
+++ b/src/include/util.h
@@ -0,0 +1,114 @@
+// -*- 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) 2012 Inktank Storage, Inc.
+ * Copyright (C) 2014 Red Hat <contact@redhat.com>
+ *
+ * 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_UTIL_H
+#define CEPH_UTIL_H
+
+#include "common/Formatter.h"
+#include "include/types.h"
+
+std::string bytes2str(uint64_t count);
+
+struct ceph_data_stats
+{
+ uint64_t byte_total;
+ uint64_t byte_used;
+ uint64_t byte_avail;
+ int avail_percent;
+
+ ceph_data_stats() :
+ byte_total(0),
+ byte_used(0),
+ byte_avail(0),
+ avail_percent(0)
+ { }
+
+ void dump(ceph::Formatter *f) const {
+ ceph_assert(f != NULL);
+ f->dump_int("total", byte_total);
+ f->dump_int("used", byte_used);
+ f->dump_int("avail", byte_avail);
+ f->dump_int("avail_percent", avail_percent);
+ }
+
+ void encode(ceph::buffer::list &bl) const {
+ ENCODE_START(1, 1, bl);
+ encode(byte_total, bl);
+ encode(byte_used, bl);
+ encode(byte_avail, bl);
+ encode(avail_percent, bl);
+ ENCODE_FINISH(bl);
+ }
+
+ void decode(ceph::buffer::list::const_iterator &p) {
+ DECODE_START(1, p);
+ decode(byte_total, p);
+ decode(byte_used, p);
+ decode(byte_avail, p);
+ decode(avail_percent, p);
+ DECODE_FINISH(p);
+ }
+
+ static void generate_test_instances(std::list<ceph_data_stats*>& ls) {
+ ls.push_back(new ceph_data_stats);
+ ls.push_back(new ceph_data_stats);
+ ls.back()->byte_total = 1024*1024;
+ ls.back()->byte_used = 512*1024;
+ ls.back()->byte_avail = 512*1024;
+ ls.back()->avail_percent = 50;
+ }
+};
+typedef struct ceph_data_stats ceph_data_stats_t;
+WRITE_CLASS_ENCODER(ceph_data_stats)
+
+int get_fs_stats(ceph_data_stats_t &stats, const char *path);
+
+/// get memory limit for the current cgroup
+int get_cgroup_memory_limit(uint64_t *limit);
+
+/// collect info from @p uname(2), @p /proc/meminfo and @p /proc/cpuinfo
+void collect_sys_info(std::map<std::string, std::string> *m, CephContext *cct);
+
+#ifdef _WIN32
+/// Retrieve the actual Windows version, regardless of the app manifest.
+int get_windows_version(POSVERSIONINFOEXW ver);
+#endif
+
+/// dump service ids grouped by their host to the specified formatter
+/// @param f formatter for the output
+/// @param services a map from hostname to a list of service id hosted by this host
+/// @param type the service type of given @p services, for example @p osd or @p mon.
+void dump_services(ceph::Formatter* f,
+ const std::map<std::string, std::list<int> >& services,
+ const char* type);
+/// dump service names grouped by their host to the specified formatter
+/// @param f formatter for the output
+/// @param services a map from hostname to a list of service name hosted by this host
+/// @param type the service type of given @p services, for example @p osd or @p mon.
+void dump_services(ceph::Formatter* f, const std::map<std::string,
+ std::list<std::string> >& services, const char* type);
+
+std::string cleanbin(ceph::buffer::list &bl, bool &b64, bool show = false);
+std::string cleanbin(std::string &str);
+
+namespace ceph::util {
+
+// Returns true if s matches any parameters:
+template <typename ...XS>
+bool match_str(const std::string& s, const XS& ...xs)
+{
+ return ((s == xs) || ...);
+}
+
+} // namespace ceph::util
+#endif /* CEPH_UTIL_H */
diff --git a/src/include/utime.cc b/src/include/utime.cc
new file mode 100644
index 000000000..2252a1ca4
--- /dev/null
+++ b/src/include/utime.cc
@@ -0,0 +1,31 @@
+// -*- 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) 2019 Red Hat
+ *
+ * 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.
+ *
+ */
+
+#include "utime.h"
+#include "common/Formatter.h"
+
+void utime_t::dump(ceph::Formatter *f) const
+{
+ f->dump_int("seconds", tv.tv_sec);
+ f->dump_int("nanoseconds", tv.tv_nsec);
+}
+
+void utime_t::generate_test_instances(std::list<utime_t*>& o)
+{
+ o.push_back(new utime_t());
+ o.push_back(new utime_t());
+ o.back()->tv.tv_sec = static_cast<__u32>((1L << 32) - 1);
+ o.push_back(new utime_t());
+ o.back()->tv.tv_nsec = static_cast<__u32>((1L << 32) - 1);
+}
diff --git a/src/include/utime.h b/src/include/utime.h
new file mode 100644
index 000000000..fad66af79
--- /dev/null
+++ b/src/include/utime.h
@@ -0,0 +1,602 @@
+// -*- 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-2006 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_UTIME_H
+#define CEPH_UTIME_H
+
+#include <math.h>
+#include <sys/time.h>
+#include <time.h>
+#include <errno.h>
+
+#if defined(WITH_SEASTAR)
+#include <seastar/core/lowres_clock.hh>
+#endif
+
+#include "include/compat.h"
+#include "include/types.h"
+#include "include/timegm.h"
+#include "common/strtol.h"
+#include "common/ceph_time.h"
+#include "common/safe_io.h"
+#include "common/SubProcess.h"
+#include "include/denc.h"
+
+
+// --------
+// utime_t
+
+inline __u32 cap_to_u32_max(__u64 t) {
+ return std::min(t, (__u64)std::numeric_limits<uint32_t>::max());
+}
+/* WARNING: If add member in utime_t, please make sure the encode/decode function
+ * work well. For little-endian machine, we should make sure there is no padding
+ * in 32-bit machine and 64-bit machine.
+ * You should also modify the padding_check function.
+ */
+class utime_t {
+public:
+ struct {
+ __u32 tv_sec, tv_nsec;
+ } tv;
+
+ public:
+ bool is_zero() const {
+ return (tv.tv_sec == 0) && (tv.tv_nsec == 0);
+ }
+
+ void normalize() {
+ if (tv.tv_nsec > 1000000000ul) {
+ tv.tv_sec = cap_to_u32_max(tv.tv_sec + tv.tv_nsec / (1000000000ul));
+ tv.tv_nsec %= 1000000000ul;
+ }
+ }
+
+ // cons
+ utime_t() { tv.tv_sec = 0; tv.tv_nsec = 0; }
+ utime_t(time_t s, int n) { tv.tv_sec = s; tv.tv_nsec = n; normalize(); }
+ utime_t(const struct ceph_timespec &v) {
+ decode_timeval(&v);
+ }
+ utime_t(const struct timespec v)
+ {
+ // NOTE: this is used by ceph_clock_now() so should be kept
+ // as thin as possible.
+ tv.tv_sec = v.tv_sec;
+ tv.tv_nsec = v.tv_nsec;
+ }
+ // conversion from ceph::real_time/coarse_real_time
+ template <typename Clock, typename std::enable_if_t<
+ ceph::converts_to_timespec_v<Clock>>* = nullptr>
+ explicit utime_t(const std::chrono::time_point<Clock>& t)
+ : utime_t(Clock::to_timespec(t)) {} // forward to timespec ctor
+
+ template<class Rep, class Period>
+ explicit utime_t(const std::chrono::duration<Rep, Period>& dur) {
+ using common_t = std::common_type_t<Rep, int>;
+ tv.tv_sec = std::max<common_t>(std::chrono::duration_cast<std::chrono::seconds>(dur).count(), 0);
+ tv.tv_nsec = std::max<common_t>((std::chrono::duration_cast<std::chrono::nanoseconds>(dur) %
+ std::chrono::seconds(1)).count(), 0);
+ }
+#if defined(WITH_SEASTAR)
+ explicit utime_t(const seastar::lowres_system_clock::time_point& t) {
+ tv.tv_sec = std::chrono::duration_cast<std::chrono::seconds>(
+ t.time_since_epoch()).count();
+ tv.tv_nsec = std::chrono::duration_cast<std::chrono::nanoseconds>(
+ t.time_since_epoch() % std::chrono::seconds(1)).count();
+ }
+ explicit operator seastar::lowres_system_clock::time_point() const noexcept {
+ using clock_t = seastar::lowres_system_clock;
+ return clock_t::time_point{std::chrono::duration_cast<clock_t::duration>(
+ std::chrono::seconds{tv.tv_sec} + std::chrono::nanoseconds{tv.tv_nsec})};
+ }
+#endif
+
+ utime_t(const struct timeval &v) {
+ set_from_timeval(&v);
+ }
+ utime_t(const struct timeval *v) {
+ set_from_timeval(v);
+ }
+ void to_timespec(struct timespec *ts) const {
+ ts->tv_sec = tv.tv_sec;
+ ts->tv_nsec = tv.tv_nsec;
+ }
+ void set_from_double(double d) {
+ tv.tv_sec = (__u32)trunc(d);
+ tv.tv_nsec = (__u32)((d - (double)tv.tv_sec) * 1000000000.0);
+ }
+
+ ceph::real_time to_real_time() const {
+ ceph_timespec ts;
+ encode_timeval(&ts);
+ return ceph::real_clock::from_ceph_timespec(ts);
+ }
+
+ // accessors
+ time_t sec() const { return tv.tv_sec; }
+ long usec() const { return tv.tv_nsec/1000; }
+ int nsec() const { return tv.tv_nsec; }
+
+ // ref accessors/modifiers
+ __u32& sec_ref() { return tv.tv_sec; }
+ __u32& nsec_ref() { return tv.tv_nsec; }
+
+ uint64_t to_nsec() const {
+ return (uint64_t)tv.tv_nsec + (uint64_t)tv.tv_sec * 1000000000ull;
+ }
+ uint64_t to_msec() const {
+ return (uint64_t)tv.tv_nsec / 1000000ull + (uint64_t)tv.tv_sec * 1000ull;
+ }
+
+ void copy_to_timeval(struct timeval *v) const {
+ v->tv_sec = tv.tv_sec;
+ v->tv_usec = tv.tv_nsec/1000;
+ }
+ void set_from_timeval(const struct timeval *v) {
+ tv.tv_sec = v->tv_sec;
+ tv.tv_nsec = v->tv_usec*1000;
+ }
+ void padding_check() {
+ static_assert(
+ sizeof(utime_t) ==
+ sizeof(tv.tv_sec) +
+ sizeof(tv.tv_nsec)
+ ,
+ "utime_t have padding");
+ }
+ void encode(ceph::buffer::list &bl) const {
+#if defined(CEPH_LITTLE_ENDIAN)
+ bl.append((char *)(this), sizeof(__u32) + sizeof(__u32));
+#else
+ using ceph::encode;
+ encode(tv.tv_sec, bl);
+ encode(tv.tv_nsec, bl);
+#endif
+ }
+ void decode(ceph::buffer::list::const_iterator &p) {
+#if defined(CEPH_LITTLE_ENDIAN)
+ p.copy(sizeof(__u32) + sizeof(__u32), (char *)(this));
+#else
+ using ceph::decode;
+ decode(tv.tv_sec, p);
+ decode(tv.tv_nsec, p);
+#endif
+ }
+
+ DENC(utime_t, v, p) {
+ denc(v.tv.tv_sec, p);
+ denc(v.tv.tv_nsec, p);
+ }
+
+ void dump(ceph::Formatter *f) const;
+ static void generate_test_instances(std::list<utime_t*>& o);
+
+ void encode_timeval(struct ceph_timespec *t) const {
+ t->tv_sec = tv.tv_sec;
+ t->tv_nsec = tv.tv_nsec;
+ }
+ void decode_timeval(const struct ceph_timespec *t) {
+ tv.tv_sec = t->tv_sec;
+ tv.tv_nsec = t->tv_nsec;
+ }
+
+ utime_t round_to_minute() {
+ struct tm bdt;
+ time_t tt = sec();
+ localtime_r(&tt, &bdt);
+ bdt.tm_sec = 0;
+ tt = mktime(&bdt);
+ return utime_t(tt, 0);
+ }
+
+ utime_t round_to_hour() {
+ struct tm bdt;
+ time_t tt = sec();
+ localtime_r(&tt, &bdt);
+ bdt.tm_sec = 0;
+ bdt.tm_min = 0;
+ tt = mktime(&bdt);
+ return utime_t(tt, 0);
+ }
+
+ utime_t round_to_day() {
+ struct tm bdt;
+ time_t tt = sec();
+ localtime_r(&tt, &bdt);
+ bdt.tm_sec = 0;
+ bdt.tm_min = 0;
+ bdt.tm_hour = 0;
+ tt = mktime(&bdt);
+ return utime_t(tt, 0);
+ }
+
+ // cast to double
+ operator double() const {
+ return (double)sec() + ((double)nsec() / 1000000000.0f);
+ }
+ operator ceph_timespec() const {
+ ceph_timespec ts;
+ ts.tv_sec = sec();
+ ts.tv_nsec = nsec();
+ return ts;
+ }
+
+ void sleep() const {
+ struct timespec ts;
+ to_timespec(&ts);
+ nanosleep(&ts, NULL);
+ }
+
+ // output
+ std::ostream& gmtime(std::ostream& out, bool legacy_form=false) const {
+ out.setf(std::ios::right);
+ char oldfill = out.fill();
+ out.fill('0');
+ if (sec() < ((time_t)(60*60*24*365*10))) {
+ // raw seconds. this looks like a relative time.
+ out << (long)sec() << "." << std::setw(6) << usec();
+ } else {
+ // this looks like an absolute time.
+ // conform to http://en.wikipedia.org/wiki/ISO_8601
+ struct tm bdt;
+ time_t tt = sec();
+ gmtime_r(&tt, &bdt);
+ out << std::setw(4) << (bdt.tm_year+1900) // 2007 -> '07'
+ << '-' << std::setw(2) << (bdt.tm_mon+1)
+ << '-' << std::setw(2) << bdt.tm_mday;
+ if (legacy_form) {
+ out << ' ';
+ } else {
+ out << 'T';
+ }
+ out << std::setw(2) << bdt.tm_hour
+ << ':' << std::setw(2) << bdt.tm_min
+ << ':' << std::setw(2) << bdt.tm_sec;
+ out << "." << std::setw(6) << usec();
+ out << "Z";
+ }
+ out.fill(oldfill);
+ out.unsetf(std::ios::right);
+ return out;
+ }
+
+ // output
+ std::ostream& gmtime_nsec(std::ostream& out) const {
+ out.setf(std::ios::right);
+ char oldfill = out.fill();
+ out.fill('0');
+ if (sec() < ((time_t)(60*60*24*365*10))) {
+ // raw seconds. this looks like a relative time.
+ out << (long)sec() << "." << std::setw(6) << usec();
+ } else {
+ // this looks like an absolute time.
+ // conform to http://en.wikipedia.org/wiki/ISO_8601
+ struct tm bdt;
+ time_t tt = sec();
+ gmtime_r(&tt, &bdt);
+ out << std::setw(4) << (bdt.tm_year+1900) // 2007 -> '07'
+ << '-' << std::setw(2) << (bdt.tm_mon+1)
+ << '-' << std::setw(2) << bdt.tm_mday
+ << 'T'
+ << std::setw(2) << bdt.tm_hour
+ << ':' << std::setw(2) << bdt.tm_min
+ << ':' << std::setw(2) << bdt.tm_sec;
+ out << "." << std::setw(9) << nsec();
+ out << "Z";
+ }
+ out.fill(oldfill);
+ out.unsetf(std::ios::right);
+ return out;
+ }
+
+ // output
+ std::ostream& asctime(std::ostream& out) const {
+ out.setf(std::ios::right);
+ char oldfill = out.fill();
+ out.fill('0');
+ if (sec() < ((time_t)(60*60*24*365*10))) {
+ // raw seconds. this looks like a relative time.
+ out << (long)sec() << "." << std::setw(6) << usec();
+ } else {
+ // this looks like an absolute time.
+ struct tm bdt;
+ time_t tt = sec();
+ gmtime_r(&tt, &bdt);
+
+ char buf[128];
+ asctime_r(&bdt, buf);
+ int len = strlen(buf);
+ if (buf[len - 1] == '\n')
+ buf[len - 1] = '\0';
+ out << buf;
+ }
+ out.fill(oldfill);
+ out.unsetf(std::ios::right);
+ return out;
+ }
+
+ std::ostream& localtime(std::ostream& out, bool legacy_form=false) const {
+ out.setf(std::ios::right);
+ char oldfill = out.fill();
+ out.fill('0');
+ if (sec() < ((time_t)(60*60*24*365*10))) {
+ // raw seconds. this looks like a relative time.
+ out << (long)sec() << "." << std::setw(6) << usec();
+ } else {
+ // this looks like an absolute time.
+ // conform to http://en.wikipedia.org/wiki/ISO_8601
+ struct tm bdt;
+ time_t tt = sec();
+ localtime_r(&tt, &bdt);
+ out << std::setw(4) << (bdt.tm_year+1900) // 2007 -> '07'
+ << '-' << std::setw(2) << (bdt.tm_mon+1)
+ << '-' << std::setw(2) << bdt.tm_mday;
+ if (legacy_form) {
+ out << ' ';
+ } else {
+ out << 'T';
+ }
+ out << std::setw(2) << bdt.tm_hour
+ << ':' << std::setw(2) << bdt.tm_min
+ << ':' << std::setw(2) << bdt.tm_sec;
+ out << "." << std::setw(6) << usec();
+ if (!legacy_form) {
+ char buf[32] = { 0 };
+ strftime(buf, sizeof(buf), "%z", &bdt);
+ out << buf;
+ }
+ }
+ out.fill(oldfill);
+ out.unsetf(std::ios::right);
+ return out;
+ }
+
+ static int invoke_date(const std::string& date_str, utime_t *result) {
+ char buf[256];
+
+ SubProcess bin_date("/bin/date", SubProcess::CLOSE, SubProcess::PIPE,
+ SubProcess::KEEP);
+ bin_date.add_cmd_args("-d", date_str.c_str(), "+%s %N", NULL);
+
+ int r = bin_date.spawn();
+ if (r < 0) return r;
+
+ ssize_t n = safe_read(bin_date.get_stdout(), buf, sizeof(buf));
+
+ r = bin_date.join();
+ if (r || n <= 0) return -EINVAL;
+
+ uint64_t epoch, nsec;
+ std::istringstream iss(buf);
+
+ iss >> epoch;
+ iss >> nsec;
+
+ *result = utime_t(epoch, nsec);
+
+ return 0;
+ }
+
+
+ static int parse_date(const std::string& date, uint64_t *epoch, uint64_t *nsec,
+ std::string *out_date=nullptr,
+ std::string *out_time=nullptr) {
+ struct tm tm;
+ memset(&tm, 0, sizeof(tm));
+
+ if (nsec)
+ *nsec = 0;
+
+ const char *p = strptime(date.c_str(), "%Y-%m-%d", &tm);
+ if (p) {
+ if (*p == ' ' || *p == 'T') {
+ p++;
+ // strptime doesn't understand fractional/decimal seconds, and
+ // it also only takes format chars or literals, so we have to
+ // get creative.
+ char fmt[32] = {0};
+ strncpy(fmt, p, sizeof(fmt) - 1);
+ fmt[0] = '%';
+ fmt[1] = 'H';
+ fmt[2] = ':';
+ fmt[3] = '%';
+ fmt[4] = 'M';
+ fmt[6] = '%';
+ fmt[7] = 'S';
+ const char *subsec = 0;
+ char *q = fmt + 8;
+ if (*q == '.') {
+ ++q;
+ subsec = p + 9;
+ q = fmt + 9;
+ while (*q && isdigit(*q)) {
+ ++q;
+ }
+ }
+ // look for tz...
+ if (*q == '-' || *q == '+') {
+ *q = '%';
+ *(q+1) = 'z';
+ *(q+2) = 0;
+ }
+ p = strptime(p, fmt, &tm);
+ if (!p) {
+ return -EINVAL;
+ }
+ if (nsec && subsec) {
+ unsigned i;
+ char buf[10]; /* 9 digit + null termination */
+ for (i = 0; (i < sizeof(buf) - 1) && isdigit(*subsec); ++i, ++subsec) {
+ buf[i] = *subsec;
+ }
+ for (; i < sizeof(buf) - 1; ++i) {
+ buf[i] = '0';
+ }
+ buf[i] = '\0';
+ std::string err;
+ *nsec = (uint64_t)strict_strtol(buf, 10, &err);
+ if (!err.empty()) {
+ return -EINVAL;
+ }
+ }
+ }
+ } else {
+ int sec, usec;
+ int r = sscanf(date.c_str(), "%d.%d", &sec, &usec);
+ if (r != 2) {
+ return -EINVAL;
+ }
+
+ time_t tt = sec;
+ gmtime_r(&tt, &tm);
+
+ if (nsec) {
+ *nsec = (uint64_t)usec * 1000;
+ }
+ }
+
+ #ifndef _WIN32
+ // apply the tm_gmtoff manually below, since none of mktime,
+ // gmtime, and localtime seem to do it. zero it out here just in
+ // case some other libc *does* apply it. :(
+ auto gmtoff = tm.tm_gmtoff;
+ tm.tm_gmtoff = 0;
+ #else
+ auto gmtoff = _timezone;
+ #endif /* _WIN32 */
+
+ time_t t = internal_timegm(&tm);
+ if (epoch)
+ *epoch = (uint64_t)t;
+
+ *epoch -= gmtoff;
+
+ if (out_date) {
+ char buf[32];
+ strftime(buf, sizeof(buf), "%Y-%m-%d", &tm);
+ *out_date = buf;
+ }
+ if (out_time) {
+ char buf[32];
+ strftime(buf, sizeof(buf), "%H:%M:%S", &tm);
+ *out_time = buf;
+ }
+
+ return 0;
+ }
+
+ bool parse(const std::string& s) {
+ uint64_t epoch, nsec;
+ int r = parse_date(s, &epoch, &nsec);
+ if (r < 0) {
+ return false;
+ }
+ *this = utime_t(epoch, nsec);
+ return true;
+ }
+};
+WRITE_CLASS_ENCODER(utime_t)
+WRITE_CLASS_DENC(utime_t)
+
+// arithmetic operators
+inline utime_t operator+(const utime_t& l, const utime_t& r) {
+ __u64 sec = (__u64)l.sec() + r.sec();
+ return utime_t(cap_to_u32_max(sec), l.nsec() + r.nsec());
+}
+inline utime_t& operator+=(utime_t& l, const utime_t& r) {
+ l.sec_ref() = cap_to_u32_max((__u64)l.sec() + r.sec());
+ l.nsec_ref() += r.nsec();
+ l.normalize();
+ return l;
+}
+inline utime_t& operator+=(utime_t& l, double f) {
+ double fs = trunc(f);
+ double ns = (f - fs) * 1000000000.0;
+ l.sec_ref() = cap_to_u32_max(l.sec() + (__u64)fs);
+ l.nsec_ref() += (long)ns;
+ l.normalize();
+ return l;
+}
+
+inline utime_t operator-(const utime_t& l, const utime_t& r) {
+ return utime_t( l.sec() - r.sec() - (l.nsec()<r.nsec() ? 1:0),
+ l.nsec() - r.nsec() + (l.nsec()<r.nsec() ? 1000000000:0) );
+}
+inline utime_t& operator-=(utime_t& l, const utime_t& r) {
+ l.sec_ref() -= r.sec();
+ if (l.nsec() >= r.nsec())
+ l.nsec_ref() -= r.nsec();
+ else {
+ l.nsec_ref() += 1000000000L - r.nsec();
+ l.sec_ref()--;
+ }
+ return l;
+}
+inline utime_t& operator-=(utime_t& l, double f) {
+ double fs = trunc(f);
+ double ns = (f - fs) * 1000000000.0;
+ l.sec_ref() -= (long)fs;
+ long nsl = (long)ns;
+ if (nsl) {
+ l.sec_ref()--;
+ l.nsec_ref() = 1000000000L + l.nsec_ref() - nsl;
+ }
+ l.normalize();
+ return l;
+}
+
+
+// comparators
+inline bool operator>(const utime_t& a, const utime_t& b)
+{
+ return (a.sec() > b.sec()) || (a.sec() == b.sec() && a.nsec() > b.nsec());
+}
+inline bool operator<=(const utime_t& a, const utime_t& b)
+{
+ return !(operator>(a, b));
+}
+inline bool operator<(const utime_t& a, const utime_t& b)
+{
+ return (a.sec() < b.sec()) || (a.sec() == b.sec() && a.nsec() < b.nsec());
+}
+inline bool operator>=(const utime_t& a, const utime_t& b)
+{
+ return !(operator<(a, b));
+}
+
+inline bool operator==(const utime_t& a, const utime_t& b)
+{
+ return a.sec() == b.sec() && a.nsec() == b.nsec();
+}
+inline bool operator!=(const utime_t& a, const utime_t& b)
+{
+ return a.sec() != b.sec() || a.nsec() != b.nsec();
+}
+
+
+// output
+
+// ostream
+inline std::ostream& operator<<(std::ostream& out, const utime_t& t)
+{
+ return t.localtime(out);
+}
+
+inline std::string utimespan_str(const utime_t& age) {
+ auto age_ts = ceph::timespan(age.nsec()) + std::chrono::seconds(age.sec());
+ return ceph::timespan_str(age_ts);
+}
+
+#endif
diff --git a/src/include/utime_fmt.h b/src/include/utime_fmt.h
new file mode 100644
index 000000000..e7a98d209
--- /dev/null
+++ b/src/include/utime_fmt.h
@@ -0,0 +1,47 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+#pragma once
+/**
+ * \file fmtlib formatter for utime_t
+ */
+#include <fmt/chrono.h>
+#include <fmt/format.h>
+
+#include "include/utime.h"
+
+template <>
+struct fmt::formatter<utime_t> {
+ template <typename ParseContext>
+ constexpr auto parse(ParseContext& ctx)
+ {
+ auto it = ctx.begin();
+ if (it != ctx.end() && *it == 's') {
+ short_format = true;
+ ++it;
+ }
+ return it;
+ }
+
+ template <typename FormatContext>
+ auto format(const utime_t& utime, FormatContext& ctx)
+ {
+ if (utime.sec() < ((time_t)(60 * 60 * 24 * 365 * 10))) {
+ // raw seconds. this looks like a relative time.
+ return fmt::format_to(ctx.out(), "{}.{:06}", (long)utime.sec(),
+ utime.usec());
+ }
+
+ // this looks like an absolute time.
+ // conform to http://en.wikipedia.org/wiki/ISO_8601
+ // (unless short_format is set)
+ auto aslocal = fmt::localtime(utime.sec());
+ if (short_format) {
+ return fmt::format_to(ctx.out(), "{:%FT%T}.{:03}", aslocal,
+ utime.usec() / 1000);
+ }
+ return fmt::format_to(ctx.out(), "{:%FT%T}.{:06}{:%z}", aslocal,
+ utime.usec(), aslocal);
+ }
+
+ bool short_format{false};
+};
diff --git a/src/include/uuid.cc b/src/include/uuid.cc
new file mode 100644
index 000000000..106fc1db5
--- /dev/null
+++ b/src/include/uuid.cc
@@ -0,0 +1,36 @@
+// -*- 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) 2019 Red Hat
+ *
+ * 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.
+ *
+ */
+
+#include "uuid.h"
+#include "common/Formatter.h"
+
+void uuid_d::dump(ceph::Formatter *f) const
+{
+ f->dump_stream("uuid") << to_string();
+}
+
+void uuid_d::generate_test_instances(std::list<uuid_d*>& o)
+{
+ // these are sourced from examples at
+ // https://www.boost.org/doc/libs/1_62_0/libs/uuid/uuid.html#Synopsis_generators
+ boost::uuids::string_generator gen;
+ o.push_back(new uuid_d());
+ o.back()->uuid = gen("{01234567-89ab-cdef-0123-456789abcdef}");
+ o.push_back(new uuid_d());
+ o.back()->uuid = gen(L"01234567-89ab-cdef-0123-456789abcdef");
+ o.push_back(new uuid_d());
+ o.back()->uuid = gen(std::string("0123456789abcdef0123456789abcdef"));
+ o.push_back(new uuid_d());
+ o.back()->uuid = gen(std::wstring(L"01234567-89ab-cdef-0123-456789abcdef"));
+}
diff --git a/src/include/uuid.h b/src/include/uuid.h
new file mode 100644
index 000000000..f6ef9878d
--- /dev/null
+++ b/src/include/uuid.h
@@ -0,0 +1,107 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+#ifndef _CEPH_UUID_H
+#define _CEPH_UUID_H
+
+/*
+ * Thin C++ wrapper around libuuid.
+ */
+
+#include "encoding.h"
+#include "random.h"
+
+#include <ostream>
+#include <random>
+
+#include <boost/uuid/uuid.hpp>
+#include <boost/uuid/uuid_generators.hpp>
+#include <boost/uuid/uuid_io.hpp>
+
+#if FMT_VERSION >= 90000
+#include <fmt/ostream.h>
+#endif
+
+namespace ceph {
+ class Formatter;
+}
+
+struct uuid_d {
+ boost::uuids::uuid uuid;
+
+ uuid_d() {
+ boost::uuids::nil_generator gen;
+ uuid = gen();
+ }
+
+ bool is_zero() const {
+ return uuid.is_nil();
+ }
+
+ void generate_random() {
+ random_device_t rng;
+ boost::uuids::basic_random_generator gen(rng);
+ uuid = gen();
+ }
+
+ bool parse(const char *s) {
+ try {
+ boost::uuids::string_generator gen;
+ uuid = gen(s);
+ return true;
+ } catch (std::runtime_error& e) {
+ return false;
+ }
+ }
+ void print(char *s) const {
+ memcpy(s, boost::uuids::to_string(uuid).c_str(), 37);
+ }
+
+ std::string to_string() const {
+ return boost::uuids::to_string(uuid);
+ }
+
+ const char *bytes() const {
+ return (const char*)uuid.data;
+ }
+
+ void encode(::ceph::buffer::list::contiguous_appender& p) const {
+ p.append(reinterpret_cast<const char *>(&uuid), sizeof(uuid));
+ }
+
+ void bound_encode(size_t& p) const {
+ p += sizeof(uuid);
+ }
+
+ void decode(::ceph::buffer::ptr::const_iterator& p) {
+ assert((p.get_end() - p.get_pos()) >= (int)sizeof(*this));
+ memcpy((char *)this, p.get_pos_add(sizeof(*this)), sizeof(*this));
+ }
+
+ void dump(ceph::Formatter *f) const;
+ static void generate_test_instances(std::list<uuid_d*>& o);
+};
+WRITE_CLASS_DENC_BOUNDED(uuid_d)
+
+inline std::ostream& operator<<(std::ostream& out, const uuid_d& u) {
+ char b[37];
+ u.print(b);
+ return out << b;
+}
+
+inline bool operator==(const uuid_d& l, const uuid_d& r) {
+ return l.uuid == r.uuid;
+}
+inline bool operator!=(const uuid_d& l, const uuid_d& r) {
+ return l.uuid != r.uuid;
+}
+inline bool operator<(const uuid_d& l, const uuid_d& r) {
+ return l.to_string() < r.to_string();
+}
+inline bool operator>(const uuid_d& l, const uuid_d& r) {
+ return l.to_string() > r.to_string();
+}
+
+#if FMT_VERSION >= 90000
+template <> struct fmt::formatter<uuid_d> : fmt::ostream_formatter {};
+#endif
+
+#endif
diff --git a/src/include/win32/arpa/inet.h b/src/include/win32/arpa/inet.h
new file mode 100644
index 000000000..44983f03f
--- /dev/null
+++ b/src/include/win32/arpa/inet.h
@@ -0,0 +1 @@
+#include "winsock_compat.h"
diff --git a/src/include/win32/dlfcn.h b/src/include/win32/dlfcn.h
new file mode 100644
index 000000000..32e51f16f
--- /dev/null
+++ b/src/include/win32/dlfcn.h
@@ -0,0 +1 @@
+#include "../dlfcn_compat.h"
diff --git a/src/include/win32/fs_compat.h b/src/include/win32/fs_compat.h
new file mode 100644
index 000000000..deeedf071
--- /dev/null
+++ b/src/include/win32/fs_compat.h
@@ -0,0 +1,47 @@
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2021 SUSE LINUX GmbH
+ *
+ * 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.
+ *
+ */
+
+// Those definitions allow handling information coming from Ceph and should
+// not be passed to Windows functions.
+
+#pragma once
+
+#define S_IFLNK 0120000
+
+#define S_ISTYPE(m, TYPE) ((m & S_IFMT) == TYPE)
+#define S_ISLNK(m) S_ISTYPE(m, S_IFLNK)
+#define S_ISUID 04000
+#define S_ISGID 02000
+#define S_ISVTX 01000
+
+#define LOCK_SH 1
+#define LOCK_EX 2
+#define LOCK_NB 4
+#define LOCK_UN 8
+#define LOCK_MAND 32
+#define LOCK_READ 64
+#define LOCK_WRITE 128
+#define LOCK_RW 192
+
+#define AT_SYMLINK_NOFOLLOW 0x100
+#define AT_REMOVEDIR 0x200
+
+#define MAXSYMLINKS 65000
+
+#define O_DIRECTORY 0200000
+#define O_NOFOLLOW 0400000
+
+#define XATTR_CREATE 1
+#define XATTR_REPLACE 2
+
+typedef unsigned int uid_t;
+typedef unsigned int gid_t;
diff --git a/src/include/win32/ifaddrs.h b/src/include/win32/ifaddrs.h
new file mode 100644
index 000000000..45e1a362c
--- /dev/null
+++ b/src/include/win32/ifaddrs.h
@@ -0,0 +1,39 @@
+// -*- 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) 2002-2016 Free Software Foundation, Inc.
+ * Copyright (C) 2019 SUSE LINUX GmbH
+ *
+ * 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 IFADDRS_H
+#define IFADDRS_H
+
+#include "winsock_compat.h"
+#include <ifdef.h>
+
+struct ifaddrs {
+ struct ifaddrs *ifa_next; /* Next item in list */
+ char *ifa_name; /* Name of interface */
+ unsigned int ifa_flags; /* Flags from SIOCGIFFLAGS */
+ struct sockaddr *ifa_addr; /* Address of interface */
+ struct sockaddr *ifa_netmask; /* Netmask of interface */
+
+ struct sockaddr_storage in_addrs;
+ struct sockaddr_storage in_netmasks;
+
+ char ad_name[IF_MAX_STRING_SIZE];
+ size_t speed;
+};
+
+int getifaddrs(struct ifaddrs **ifap);
+void freeifaddrs(struct ifaddrs *ifa);
+
+#endif
diff --git a/src/include/win32/netdb.h b/src/include/win32/netdb.h
new file mode 100644
index 000000000..44983f03f
--- /dev/null
+++ b/src/include/win32/netdb.h
@@ -0,0 +1 @@
+#include "winsock_compat.h"
diff --git a/src/include/win32/netinet/in.h b/src/include/win32/netinet/in.h
new file mode 100644
index 000000000..44983f03f
--- /dev/null
+++ b/src/include/win32/netinet/in.h
@@ -0,0 +1 @@
+#include "winsock_compat.h"
diff --git a/src/include/win32/netinet/ip.h b/src/include/win32/netinet/ip.h
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/include/win32/netinet/ip.h
diff --git a/src/include/win32/netinet/tcp.h b/src/include/win32/netinet/tcp.h
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/include/win32/netinet/tcp.h
diff --git a/src/include/win32/poll.h b/src/include/win32/poll.h
new file mode 100644
index 000000000..44983f03f
--- /dev/null
+++ b/src/include/win32/poll.h
@@ -0,0 +1 @@
+#include "winsock_compat.h"
diff --git a/src/include/win32/sys/errno.h b/src/include/win32/sys/errno.h
new file mode 100644
index 000000000..339f4fc10
--- /dev/null
+++ b/src/include/win32/sys/errno.h
@@ -0,0 +1 @@
+#include <errno.h>
diff --git a/src/include/win32/sys/select.h b/src/include/win32/sys/select.h
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/include/win32/sys/select.h
diff --git a/src/include/win32/sys/socket.h b/src/include/win32/sys/socket.h
new file mode 100644
index 000000000..44983f03f
--- /dev/null
+++ b/src/include/win32/sys/socket.h
@@ -0,0 +1 @@
+#include "winsock_compat.h"
diff --git a/src/include/win32/sys/statvfs.h b/src/include/win32/sys/statvfs.h
new file mode 100644
index 000000000..73a892b88
--- /dev/null
+++ b/src/include/win32/sys/statvfs.h
@@ -0,0 +1,36 @@
+#ifndef _SYS_STATVFS_H
+#define _SYS_STATVFS_H 1
+
+typedef unsigned __int64 fsfilcnt64_t;
+typedef unsigned __int64 fsblkcnt64_t;
+typedef unsigned __int64 fsblkcnt_t;
+
+struct statvfs
+{
+ unsigned long int f_bsize;
+ unsigned long int f_frsize;
+ fsblkcnt64_t f_blocks;
+ fsblkcnt64_t f_bfree;
+ fsblkcnt64_t f_bavail;
+ fsfilcnt64_t f_files;
+ fsfilcnt64_t f_ffree;
+ fsfilcnt64_t f_favail;
+ unsigned long int f_fsid;
+ unsigned long int f_flag;
+ unsigned long int f_namemax;
+ int __f_spare[6];
+};
+struct flock {
+ short l_type;
+ short l_whence;
+ off_t l_start;
+ off_t l_len;
+ pid_t l_pid;
+};
+
+#define F_RDLCK 0
+#define F_WRLCK 1
+#define F_UNLCK 2
+#define F_SETLK 6
+
+#endif /* _SYS_STATVFS_H */
diff --git a/src/include/win32/sys/uio.h b/src/include/win32/sys/uio.h
new file mode 100644
index 000000000..15e95be7f
--- /dev/null
+++ b/src/include/win32/sys/uio.h
@@ -0,0 +1 @@
+#include "include/compat.h"
diff --git a/src/include/win32/sys/un.h b/src/include/win32/sys/un.h
new file mode 100644
index 000000000..d08940b2c
--- /dev/null
+++ b/src/include/win32/sys/un.h
@@ -0,0 +1 @@
+#include "include/win32/winsock_compat.h"
diff --git a/src/include/win32/syslog.h b/src/include/win32/syslog.h
new file mode 100644
index 000000000..28389e0b9
--- /dev/null
+++ b/src/include/win32/syslog.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2013, 2015 Cloudbase Solutions Srl
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.You may obtain
+ * a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef SYSLOG_H
+#define SYSLOG_H 1
+
+#define LOG_EMERG 0 /* system is unusable */
+#define LOG_ALERT 1 /* action must be taken immediately */
+#define LOG_CRIT 2 /* critical conditions */
+#define LOG_ERR 3 /* error conditions */
+#define LOG_WARNING 4 /* warning conditions */
+#define LOG_NOTICE 5 /* normal but significant condition */
+#define LOG_INFO 6 /* informational */
+#define LOG_DEBUG 7 /* debug-level messages */
+
+#define LOG_KERN (0<<3) /* kernel messages */
+#define LOG_USER (1<<3) /* user-level messages */
+#define LOG_MAIL (2<<3) /* mail system */
+#define LOG_DAEMON (3<<3) /* system daemons */
+#define LOG_AUTH (4<<3) /* security/authorization messages */
+#define LOG_SYSLOG (5<<3) /* messages generated internally by syslogd */
+#define LOG_LPR (6<<3) /* line printer subsystem */
+#define LOG_NEWS (7<<3) /* network news subsystem */
+#define LOG_UUCP (8<<3) /* UUCP subsystem */
+#define LOG_CRON (9<<3) /* clock daemon */
+#define LOG_AUTHPRIV (10<<3) /* security/authorization messages */
+#define LOG_FTP (11<<3) /* FTP daemon */
+
+#define LOG_LOCAL0 (16<<3) /* reserved for local use */
+#define LOG_LOCAL1 (17<<3) /* reserved for local use */
+#define LOG_LOCAL2 (18<<3) /* reserved for local use */
+#define LOG_LOCAL3 (19<<3) /* reserved for local use */
+#define LOG_LOCAL4 (20<<3) /* reserved for local use */
+#define LOG_LOCAL5 (21<<3) /* reserved for local use */
+#define LOG_LOCAL6 (22<<3) /* reserved for local use */
+#define LOG_LOCAL7 (23<<3) /* reserved for local use */
+
+#define LOG_PRIMASK 0x07 /* mask to extract priority part (internal) */
+ /* extract priority */
+#define LOG_PRI(p) ((p) & LOG_PRIMASK)
+
+
+static inline void
+openlog(const char *ident, int option, int facility)
+{
+}
+
+void
+syslog(int priority, const char *format, ...);
+
+#endif /* syslog.h */
diff --git a/src/include/win32/win32_errno.h b/src/include/win32/win32_errno.h
new file mode 100644
index 000000000..dd8ff8474
--- /dev/null
+++ b/src/include/win32/win32_errno.h
@@ -0,0 +1,146 @@
+// -*- 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) 2020 SUSE LINUX GmbH
+ *
+ * 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.
+ *
+ */
+
+// We're going to preserve the error numbers defined by the Windows SDK but not
+// by Mingw headers. For others, we're going to use numbers greater than 256 to
+// avoid unintended overlaps.
+
+#ifndef WIN32_ERRNO_H
+#define WIN32_ERRNO_H 1
+
+#include <errno.h>
+
+#include "include/int_types.h"
+
+#ifndef EBADMSG
+#define EBADMSG 104
+#endif
+
+#ifndef ENODATA
+#define ENODATA 120
+#endif
+
+#ifndef ENOLINK
+#define ENOLINK 121
+#endif
+
+#ifndef ENOMSG
+#define ENOMSG 122
+#endif
+
+#ifndef ENOTRECOVERABLE
+#define ENOTRECOVERABLE 127
+#endif
+
+#ifndef ETIME
+#define ETIME 137
+#endif
+
+#ifndef ETXTBSY
+#define ETXTBSY 139
+#endif
+
+#ifndef ENODATA
+#define ENODATA 120
+#endif
+
+#define ESTALE 256
+#define EREMOTEIO 257
+
+#ifndef EBADE
+#define EBADE 258
+#endif
+
+#define EUCLEAN 259
+#define EREMCHG 260
+#define EKEYREJECTED 261
+#define EREMOTE 262
+
+// Not used at moment. Full coverage ensures that remote errors will be
+// converted and handled properly.
+#define EADV 263
+#define EBADFD 264
+#define EBADR 265
+#define EBADRQC 266
+#define EBADSLT 267
+#define EBFONT 268
+#define ECHRNG 269
+#define ECOMM 270
+#define EDOTDOT 271
+#define EHOSTDOWN 272
+#define EHWPOISON 273
+// Defined by Boost.
+#ifndef EIDRM
+#define EIDRM 274
+#endif
+#define EISNAM 275
+#define EKEYEXPIRED 276
+#define EKEYREVOKED 277
+#define EL2HLT 278
+#define EL2NSYNC 279
+#define EL3HLT 280
+#define EL3RST 281
+#define ELIBACC 282
+#define ELIBBAD 283
+#define ELIBEXEC 284
+#define ELIBMAX 285
+#define ELIBSCN 286
+#define ELNRNG 287
+#define EMEDIUMTYPE 288
+#define EMULTIHOP 289
+#define ENAVAIL 290
+#define ENOANO 291
+#define ENOCSI 292
+#define ENOKEY 293
+#define ENOMEDIUM 294
+#define ENONET 295
+#define ENOPKG 296
+#ifndef ENOSR
+#define ENOSR 297
+#endif
+#ifndef ENOSTR
+#define ENOSTR 298
+#endif
+#define ENOTNAM 299
+#define ENOTUNIQ 300
+#define EPFNOSUPPORT 301
+#define ERFKILL 302
+#define ESOCKTNOSUPPORT 303
+#define ESRMNT 304
+#define ESTRPIPE 305
+#define ETOOMANYREFS 306
+#define EUNATCH 307
+#define EUSERS 308
+#define EXFULL 309
+#define ENOTBLK 310
+
+#ifndef EDQUOT
+#define EDQUOT 311
+#endif
+
+#define ESHUTDOWN 312
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+__s32 wsae_to_errno(__s32 r);
+__u32 errno_to_ntstatus(__s32 r);
+__u32 cephfs_errno_to_ntstatus_map(int cephfs_errno);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // WIN32_ERRNO_H
diff --git a/src/include/win32/winsock_compat.h b/src/include/win32/winsock_compat.h
new file mode 100644
index 000000000..990cc4823
--- /dev/null
+++ b/src/include/win32/winsock_compat.h
@@ -0,0 +1,39 @@
+// -*- 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) 2019 SUSE LLC
+ *
+ * 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 WINSOCK_COMPAT_H
+#define WINSOCK_COMPAT_H 1
+
+#include "winsock_wrapper.h"
+
+#ifndef poll
+#define poll WSAPoll
+#endif
+
+// afunix.h is available starting with Windows SDK 17063. Still, it wasn't
+// picked up by mingw yet, for which reason we're going to define sockaddr_un
+// here.
+#ifndef _AFUNIX_
+#define UNIX_PATH_MAX 108
+
+typedef struct sockaddr_un
+{
+ ADDRESS_FAMILY sun_family; /* AF_UNIX */
+ char sun_path[UNIX_PATH_MAX]; /* pathname */
+} SOCKADDR_UN, *PSOCKADDR_UN;
+
+#define SIO_AF_UNIX_GETPEERPID _WSAIOR(IOC_VENDOR, 256)
+#endif /* _AFUNIX */
+
+#endif /* WINSOCK_COMPAT_H */
diff --git a/src/include/win32/winsock_wrapper.h b/src/include/win32/winsock_wrapper.h
new file mode 100644
index 000000000..1bb951a9d
--- /dev/null
+++ b/src/include/win32/winsock_wrapper.h
@@ -0,0 +1,27 @@
+// -*- 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) 2020 SUSE LLC
+ *
+ * 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 WINSOCK_WRAPPER_H
+#define WINSOCK_WRAPPER_H 1
+
+#ifdef __cplusplus
+// Boost complains if winsock2.h (or windows.h) is included before asio.hpp.
+#include <boost/asio.hpp>
+#endif
+
+#include <winsock2.h>
+#include <ws2ipdef.h>
+#include <ws2tcpip.h>
+
+#endif /* WINSOCK_WRAPPER_H */
diff --git a/src/include/xlist.h b/src/include/xlist.h
new file mode 100644
index 000000000..76d0ddccd
--- /dev/null
+++ b/src/include/xlist.h
@@ -0,0 +1,237 @@
+// -*- 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-2006 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_XLIST_H
+#define CEPH_XLIST_H
+
+#include <iterator>
+#include <cstdlib>
+#include <ostream>
+
+#include "include/ceph_assert.h"
+
+template<typename T>
+class xlist {
+public:
+ class item {
+ public:
+ item(T i) : _item(i) {}
+ ~item() {
+ ceph_assert(!is_on_list());
+ }
+
+ item(const item& other) = delete;
+ item(item&& other) = delete;
+ const item& operator= (const item& right) = delete;
+ item& operator= (item&& right) = delete;
+
+ xlist* get_list() { return _list; }
+ bool is_on_list() const { return _list ? true:false; }
+ bool remove_myself() {
+ if (_list) {
+ _list->remove(this);
+ ceph_assert(_list == 0);
+ return true;
+ } else
+ return false;
+ }
+ void move_to_front() {
+ ceph_assert(_list);
+ _list->push_front(this);
+ }
+ void move_to_back() {
+ ceph_assert(_list);
+ _list->push_back(this);
+ }
+
+ private:
+ friend xlist;
+ T _item;
+ item *_prev = nullptr, *_next = nullptr;
+ xlist *_list = nullptr;
+ };
+
+ typedef item* value_type;
+ typedef item* const_reference;
+
+private:
+ item *_front, *_back;
+ size_t _size;
+
+public:
+ xlist(const xlist& other) {
+ _front = other._front;
+ _back = other._back;
+ _size = other._size;
+ }
+
+ xlist() : _front(0), _back(0), _size(0) {}
+ ~xlist() {
+ ceph_assert(_size == 0);
+ ceph_assert(_front == 0);
+ ceph_assert(_back == 0);
+ }
+
+ size_t size() const {
+ ceph_assert((bool)_front == (bool)_size);
+ return _size;
+ }
+ bool empty() const {
+ ceph_assert((bool)_front == (bool)_size);
+ return _front == 0;
+ }
+
+ void clear() {
+ while (_front)
+ remove(_front);
+ ceph_assert((bool)_front == (bool)_size);
+ }
+
+ void push_front(item *i) {
+ if (i->_list)
+ i->_list->remove(i);
+
+ i->_list = this;
+ i->_next = _front;
+ i->_prev = 0;
+ if (_front)
+ _front->_prev = i;
+ else
+ _back = i;
+ _front = i;
+ _size++;
+ }
+ void push_back(item *i) {
+ if (i->_list)
+ i->_list->remove(i);
+
+ i->_list = this;
+ i->_next = 0;
+ i->_prev = _back;
+ if (_back)
+ _back->_next = i;
+ else
+ _front = i;
+ _back = i;
+ _size++;
+ }
+ void remove(item *i) {
+ ceph_assert(i->_list == this);
+
+ if (i->_prev)
+ i->_prev->_next = i->_next;
+ else
+ _front = i->_next;
+ if (i->_next)
+ i->_next->_prev = i->_prev;
+ else
+ _back = i->_prev;
+ _size--;
+
+ i->_list = 0;
+ i->_next = i->_prev = 0;
+ ceph_assert((bool)_front == (bool)_size);
+ }
+
+ T front() { return static_cast<T>(_front->_item); }
+ const T front() const { return static_cast<const T>(_front->_item); }
+
+ T back() { return static_cast<T>(_back->_item); }
+ const T back() const { return static_cast<const T>(_back->_item); }
+
+ void pop_front() {
+ ceph_assert(!empty());
+ remove(_front);
+ }
+ void pop_back() {
+ ceph_assert(!empty());
+ remove(_back);
+ }
+
+ class iterator {
+ private:
+ item *cur;
+ public:
+ using iterator_category = std::forward_iterator_tag;
+ using value_type = T;
+ using difference_type = std::ptrdiff_t;
+ using pointer = T*;
+ using reference = T&;
+ iterator(item *i = 0) : cur(i) {}
+ T operator*() { return static_cast<T>(cur->_item); }
+ iterator& operator++() {
+ ceph_assert(cur);
+ ceph_assert(cur->_list);
+ cur = cur->_next;
+ return *this;
+ }
+ bool end() const { return cur == 0; }
+ friend bool operator==(const iterator& lhs, const iterator& rhs) {
+ return lhs.cur == rhs.cur;
+ }
+ friend bool operator!=(const iterator& lhs, const iterator& rhs) {
+ return lhs.cur != rhs.cur;
+ }
+ };
+
+ iterator begin() { return iterator(_front); }
+ iterator end() { return iterator(NULL); }
+
+ class const_iterator {
+ private:
+ item *cur;
+ public:
+ using iterator_category = std::forward_iterator_tag;
+ using value_type = T;
+ using difference_type = std::ptrdiff_t;
+ using pointer = const T*;
+ using reference = const T&;
+
+ const_iterator(item *i = 0) : cur(i) {}
+ const T operator*() { return static_cast<const T>(cur->_item); }
+ const_iterator& operator++() {
+ ceph_assert(cur);
+ ceph_assert(cur->_list);
+ cur = cur->_next;
+ return *this;
+ }
+ bool end() const { return cur == 0; }
+ friend bool operator==(const const_iterator& lhs,
+ const const_iterator& rhs) {
+ return lhs.cur == rhs.cur;
+ }
+ friend bool operator!=(const const_iterator& lhs,
+ const const_iterator& rhs) {
+ return lhs.cur != rhs.cur;
+ }
+ };
+
+ const_iterator begin() const { return const_iterator(_front); }
+ const_iterator end() const { return const_iterator(NULL); }
+
+ friend std::ostream &operator<<(std::ostream &oss, const xlist<T> &list) {
+ bool first = true;
+ for (const auto &item : list) {
+ if (!first) {
+ oss << ", ";
+ }
+ oss << *item; /* item should be a pointer */
+ first = false;
+ }
+ return oss;
+ }
+};
+
+
+#endif