// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- // vim: ts=8 sw=2 smarttab /** \file * * This is an OSD class that implements methods for management * and use of fifo * */ #include #include #include #include #include "include/buffer.h" #include "include/types.h" #include "objclass/objclass.h" #include "cls/fifo/cls_fifo_ops.h" #include "cls/fifo/cls_fifo_types.h" CLS_VER(1,0) CLS_NAME(fifo) namespace rados::cls::fifo { static constexpr auto CLS_FIFO_MAX_PART_HEADER_SIZE = 512; static std::uint32_t part_entry_overhead; struct entry_header_pre { ceph_le64 magic; ceph_le64 pre_size; ceph_le64 header_size; ceph_le64 data_size; ceph_le64 index; ceph_le32 reserved; } __attribute__ ((packed)); struct entry_header { ceph::real_time mtime; void encode(ceph::buffer::list& bl) const { ENCODE_START(1, 1, bl); encode(mtime, bl); ENCODE_FINISH(bl); } void decode(ceph::buffer::list::const_iterator& bl) { DECODE_START(1, bl); decode(mtime, bl); DECODE_FINISH(bl); } }; WRITE_CLASS_ENCODER(entry_header) namespace { std::string new_oid_prefix(std::string id, std::optional& val) { static constexpr auto PREFIX_RND_SIZE = 12; if (val) { return *val; } char buf[PREFIX_RND_SIZE + 1]; buf[PREFIX_RND_SIZE] = 0; cls_gen_rand_base64(buf, sizeof(buf) - 1); return fmt::format("{}.{}", id, buf); } int write_header(cls_method_context_t hctx, info& header) { static constexpr auto HEADER_INSTANCE_SIZE = 16; if (header.version.instance.empty()) { char buf[HEADER_INSTANCE_SIZE + 1]; buf[HEADER_INSTANCE_SIZE] = 0; cls_gen_rand_base64(buf, sizeof(buf) - 1); header.version.instance = buf; } ceph::buffer::list bl; encode(header, bl); return cls_cxx_write_full(hctx, &bl); } int read_part_header(cls_method_context_t hctx, part_header* part_header) { ceph::buffer::list bl; int r = cls_cxx_read2(hctx, 0, CLS_FIFO_MAX_PART_HEADER_SIZE, &bl, CEPH_OSD_OP_FLAG_FADVISE_WILLNEED); if (r < 0) { CLS_ERR("ERROR: %s: cls_cxx_read2() on obj returned %d", __PRETTY_FUNCTION__, r); return r; } auto iter = bl.cbegin(); try { decode(*part_header, iter); } catch (const ceph::buffer::error& err) { CLS_ERR("ERROR: %s: failed decoding part header", __PRETTY_FUNCTION__); return -EIO; } using ceph::operator <<; std::ostringstream ss; ss << part_header->max_time; CLS_LOG(5, "%s:%d read part_header:\n" "\tmagic=0x%" PRIx64 "\n" "\tmin_ofs=%" PRId64 "\n" "\tlast_ofs=%" PRId64 "\n" "\tnext_ofs=%" PRId64 "\n" "\tmin_index=%" PRId64 "\n" "\tmax_index=%" PRId64 "\n" "\tmax_time=%s\n", __PRETTY_FUNCTION__, __LINE__, part_header->magic, part_header->min_ofs, part_header->last_ofs, part_header->next_ofs, part_header->min_index, part_header->max_index, ss.str().c_str()); return 0; } int write_part_header(cls_method_context_t hctx, part_header& part_header) { ceph::buffer::list bl; encode(part_header, bl); if (bl.length() > CLS_FIFO_MAX_PART_HEADER_SIZE) { CLS_ERR("%s: cannot write part header, buffer exceeds max size", __PRETTY_FUNCTION__); return -EIO; } int r = cls_cxx_write2(hctx, 0, bl.length(), &bl, CEPH_OSD_OP_FLAG_FADVISE_WILLNEED); if (r < 0) { CLS_ERR("%s: failed to write part header: r=%d", __PRETTY_FUNCTION__, r); return r; } return 0; } int read_header(cls_method_context_t hctx, std::optional objv, info* info, bool get_info = false) { std::uint64_t size; int r = cls_cxx_stat2(hctx, &size, nullptr); if (r < 0) { CLS_ERR("ERROR: %s: cls_cxx_stat2() on obj returned %d", __PRETTY_FUNCTION__, r); return r; } ceph::buffer::list bl; r = cls_cxx_read2(hctx, 0, size, &bl, CEPH_OSD_OP_FLAG_FADVISE_WILLNEED); if (r < 0) { CLS_ERR("ERROR: %s: cls_cxx_read2() on obj returned %d", __PRETTY_FUNCTION__, r); return r; } if (r == 0) { if (get_info) { CLS_LOG(5, "%s: Zero length object, likely probe, returning ENODATA", __PRETTY_FUNCTION__); } else { CLS_ERR("ERROR: %s: Zero length object, returning ENODATA", __PRETTY_FUNCTION__); } return -ENODATA; } try { auto iter = bl.cbegin(); decode(*info, iter); } catch (const ceph::buffer::error& err) { CLS_ERR("ERROR: %s: failed decoding header", __PRETTY_FUNCTION__); return -EIO; } if (objv && !(info->version== *objv)) { auto s1 = info->version.to_str(); auto s2 = objv->to_str(); CLS_ERR("%s: version mismatch (header=%s, req=%s), canceled operation", __PRETTY_FUNCTION__, s1.c_str(), s2.c_str()); return -ECANCELED; } return 0; } int create_meta(cls_method_context_t hctx, ceph::buffer::list* in, ceph::buffer::list* out) { CLS_LOG(5, "%s", __PRETTY_FUNCTION__); op::create_meta op; try { auto iter = in->cbegin(); decode(op, iter); } catch (const ceph::buffer::error& err) { CLS_ERR("ERROR: %s: failed to decode request: %s", __PRETTY_FUNCTION__, err.what()); return -EINVAL; } if (op.id.empty()) { CLS_ERR("%s: ID cannot be empty", __PRETTY_FUNCTION__); return -EINVAL; } if (op.max_part_size == 0 || op.max_entry_size == 0 || op.max_entry_size > op.max_part_size) { CLS_ERR("ERROR: %s: invalid dimensions.", __PRETTY_FUNCTION__); return -EINVAL; } std::uint64_t size; int r = cls_cxx_stat2(hctx, &size, nullptr); if (r < 0 && r != -ENOENT) { CLS_ERR("ERROR: %s: cls_cxx_stat2() on obj returned %d", __PRETTY_FUNCTION__, r); return r; } if (op.exclusive && r == 0) { CLS_ERR("%s: exclusive create but queue already exists", __PRETTY_FUNCTION__); return -EEXIST; } if (r == 0) { CLS_LOG(5, "%s: FIFO already exists, reading from disk and comparing.", __PRETTY_FUNCTION__); ceph::buffer::list bl; r = cls_cxx_read2(hctx, 0, size, &bl, CEPH_OSD_OP_FLAG_FADVISE_WILLNEED); if (r < 0) { CLS_ERR("ERROR: %s: cls_cxx_read2() on obj returned %d", __PRETTY_FUNCTION__, r); return r; } info header; try { auto iter = bl.cbegin(); decode(header, iter); } catch (const ceph::buffer::error& err) { CLS_ERR("ERROR: %s: failed decoding header: %s", __PRETTY_FUNCTION__, err.what()); return -EIO; } if (!(header.id == op.id && (!op.oid_prefix || header.oid_prefix == *op.oid_prefix) && (!op.version || header.version == *op.version))) { CLS_ERR("%s: failed to re-create existing queue " "with different params", __PRETTY_FUNCTION__); return -EEXIST; } return 0; /* already exists */ } info header; header.id = op.id; if (op.version) { header.version = *op.version; } else { static constexpr auto DEFAULT_INSTANCE_SIZE = 16; char buf[DEFAULT_INSTANCE_SIZE + 1]; cls_gen_rand_base64(buf, sizeof(buf)); buf[DEFAULT_INSTANCE_SIZE] = '\0'; header.version.instance = buf; header.version.ver = 1; } header.oid_prefix = new_oid_prefix(op.id, op.oid_prefix); header.params.max_part_size = op.max_part_size; header.params.max_entry_size = op.max_entry_size; header.params.full_size_threshold = op.max_part_size - op.max_entry_size - part_entry_overhead; r = write_header(hctx, header); if (r < 0) { CLS_ERR("%s: failed to write header: r=%d", __PRETTY_FUNCTION__, r); return r; } return 0; } int update_meta(cls_method_context_t hctx, ceph::buffer::list* in, ceph::buffer::list* out) { CLS_LOG(5, "%s", __PRETTY_FUNCTION__); op::update_meta op; try { auto iter = in->cbegin(); decode(op, iter); } catch (const ceph::buffer::error& err) { CLS_ERR("ERROR: %s: failed to decode request", __PRETTY_FUNCTION__); return -EINVAL; } if (op.version.empty()) { CLS_ERR("%s: no version supplied", __PRETTY_FUNCTION__); return -EINVAL; } info header; int r = read_header(hctx, op.version, &header); if (r < 0) { return r; } auto u = fifo::update().tail_part_num(op.tail_part_num) .head_part_num(op.head_part_num) .min_push_part_num(op.min_push_part_num) .max_push_part_num(op.max_push_part_num) .journal_entries_add( std::move(op.journal_entries_add)) .journal_entries_rm( std::move(op.journal_entries_rm)); auto changed = header.apply_update(u); if (changed) { r = write_header(hctx, header); if (r < 0) { CLS_ERR("%s: failed to write header: r=%d", __PRETTY_FUNCTION__, r); return r; } } else { CLS_LOG(10, "%s: No change, nothing to write.", __PRETTY_FUNCTION__); } return 0; } int get_meta(cls_method_context_t hctx, ceph::buffer::list* in, ceph::buffer::list* out) { CLS_LOG(5, "%s", __PRETTY_FUNCTION__); op::get_meta op; try { auto iter = in->cbegin(); decode(op, iter); } catch (const ceph::buffer::error &err) { CLS_ERR("ERROR: %s: failed to decode request", __PRETTY_FUNCTION__); return -EINVAL; } op::get_meta_reply reply; int r = read_header(hctx, op.version, &reply.info, true); if (r < 0) { return r; } reply.part_header_size = CLS_FIFO_MAX_PART_HEADER_SIZE; reply.part_entry_overhead = part_entry_overhead; encode(reply, *out); return 0; } int init_part(cls_method_context_t hctx, ceph::buffer::list* in, ceph::buffer::list *out) { CLS_LOG(5, "%s", __PRETTY_FUNCTION__); op::init_part op; try { auto iter = in->cbegin(); decode(op, iter); } catch (const ceph::buffer::error &err) { CLS_ERR("ERROR: %s: failed to decode request", __PRETTY_FUNCTION__); return -EINVAL; } std::uint64_t size; int r = cls_cxx_stat2(hctx, &size, nullptr); if (r < 0 && r != -ENOENT) { CLS_ERR("ERROR: %s: cls_cxx_stat2() on obj returned %d", __PRETTY_FUNCTION__, r); return r; } if (r == 0 && size > 0) { part_header part_header; r = read_part_header(hctx, &part_header); if (r < 0) { CLS_ERR("%s: failed to read part header", __PRETTY_FUNCTION__); return r; } if (!(part_header.params == op.params)) { CLS_ERR("%s: failed to re-create existing part with different " "params", __PRETTY_FUNCTION__); return -EEXIST; } return 0; /* already exists */ } part_header part_header; part_header.params = op.params; part_header.min_ofs = CLS_FIFO_MAX_PART_HEADER_SIZE; part_header.last_ofs = 0; part_header.next_ofs = part_header.min_ofs; part_header.max_time = ceph::real_clock::now(); cls_gen_random_bytes(reinterpret_cast(&part_header.magic), sizeof(part_header.magic)); r = write_part_header(hctx, part_header); if (r < 0) { CLS_ERR("%s: failed to write header: r=%d", __PRETTY_FUNCTION__, r); return r; } return 0; } bool full_part(const part_header& part_header) { return (part_header.next_ofs > part_header.params.full_size_threshold); } int push_part(cls_method_context_t hctx, ceph::buffer::list* in, ceph::buffer::list* out) { CLS_LOG(5, "%s", __PRETTY_FUNCTION__); op::push_part op; try { auto iter = in->cbegin(); decode(op, iter); } catch (const ceph::buffer::error& err) { CLS_ERR("ERROR: %s: failed to decode request", __PRETTY_FUNCTION__); return -EINVAL; } part_header part_header; int r = read_part_header(hctx, &part_header); if (r < 0) { CLS_ERR("%s: failed to read part header", __PRETTY_FUNCTION__); return r; } std::uint64_t effective_len = op.total_len + op.data_bufs.size() * part_entry_overhead; if (effective_len > part_header.params.max_part_size) { return -EINVAL; } if (full_part(part_header)) { return -ERANGE; } auto now = ceph::real_clock::now(); struct entry_header entry_header = { now }; ceph::buffer::list entry_header_bl; encode(entry_header, entry_header_bl); auto max_index = part_header.max_index; const auto write_ofs = part_header.next_ofs; auto ofs = part_header.next_ofs; entry_header_pre pre_header; pre_header.magic = part_header.magic; pre_header.pre_size = sizeof(pre_header); pre_header.reserved = 0; std::uint64_t total_data = 0; for (auto& data : op.data_bufs) { total_data += data.length(); } if (total_data != op.total_len) { CLS_ERR("%s: length mismatch: op.total_len=%" PRId64 " total data received=%" PRId64, __PRETTY_FUNCTION__, op.total_len, total_data); return -EINVAL; } int entries_pushed = 0; ceph::buffer::list all_data; for (auto& data : op.data_bufs) { if (full_part(part_header)) break; pre_header.header_size = entry_header_bl.length(); pre_header.data_size = data.length(); pre_header.index = max_index; bufferptr pre(reinterpret_cast(&pre_header), sizeof(pre_header)); auto entry_write_len = pre.length() + entry_header_bl.length() + data.length(); all_data.append(pre); all_data.append(entry_header_bl); all_data.claim_append(data); part_header.last_ofs = ofs; ofs += entry_write_len; ++max_index; ++entries_pushed; part_header.max_index = max_index; part_header.next_ofs = ofs; } part_header.max_time = now; auto write_len = all_data.length(); r = cls_cxx_write2(hctx, write_ofs, write_len, &all_data, CEPH_OSD_OP_FLAG_FADVISE_WILLNEED); if (r < 0) { CLS_ERR("%s: failed to write entries (ofs=%" PRIu64 " len=%u): r=%d", __PRETTY_FUNCTION__, write_ofs, write_len, r); return r; } r = write_part_header(hctx, part_header); if (r < 0) { CLS_ERR("%s: failed to write header: r=%d", __PRETTY_FUNCTION__, r); return r; } if (entries_pushed == 0) { CLS_ERR("%s: pushed no entries? Can't happen!", __PRETTY_FUNCTION__); return -EFAULT; } return entries_pushed; } class EntryReader { static constexpr std::uint64_t prefetch_len = (128 * 1024); cls_method_context_t hctx; const fifo::part_header& part_header; std::uint64_t ofs; ceph::buffer::list data; int fetch(std::uint64_t num_bytes); int read(std::uint64_t num_bytes, ceph::buffer::list* pbl); int peek(std::uint64_t num_bytes, char *dest); int seek(std::uint64_t num_bytes); public: EntryReader(cls_method_context_t hctx, const fifo::part_header& part_header, uint64_t ofs) : hctx(hctx), part_header(part_header), ofs(ofs < part_header.min_ofs ? part_header.min_ofs : ofs) {} std::uint64_t get_ofs() const { return ofs; } bool end() const { return (ofs >= part_header.next_ofs); } int peek_pre_header(entry_header_pre* pre_header); int get_next_entry(ceph::buffer::list* pbl, std::uint64_t* pofs, ceph::real_time* pmtime); }; int EntryReader::fetch(std::uint64_t num_bytes) { CLS_LOG(5, "%s: fetch %d bytes, ofs=%d data.length()=%d", __PRETTY_FUNCTION__, (int)num_bytes, (int)ofs, (int)data.length()); if (data.length() < num_bytes) { ceph::buffer::list bl; CLS_LOG(5, "%s: reading % " PRId64 " bytes at ofs=%" PRId64, __PRETTY_FUNCTION__, prefetch_len, ofs + data.length()); int r = cls_cxx_read2(hctx, ofs + data.length(), prefetch_len, &bl, CEPH_OSD_OP_FLAG_FADVISE_WILLNEED); if (r < 0) { CLS_ERR("ERROR: %s: cls_cxx_read2() on obj returned %d", __PRETTY_FUNCTION__, r); return r; } data.claim_append(bl); } if (static_cast(num_bytes) > data.length()) { CLS_ERR("%s: requested %" PRId64 " bytes, but only " "%u were available", __PRETTY_FUNCTION__, num_bytes, data.length()); return -ERANGE; } return 0; } int EntryReader::read(std::uint64_t num_bytes, ceph::buffer::list* pbl) { int r = fetch(num_bytes); if (r < 0) { return r; } data.splice(0, num_bytes, pbl); ofs += num_bytes; return 0; } int EntryReader::peek(std::uint64_t num_bytes, char* dest) { int r = fetch(num_bytes); if (r < 0) { return r; } data.begin().copy(num_bytes, dest); return 0; } int EntryReader::seek(std::uint64_t num_bytes) { ceph::buffer::list bl; CLS_LOG(5, "%s:%d: num_bytes=%" PRIu64, __PRETTY_FUNCTION__, __LINE__, num_bytes); return read(num_bytes, &bl); } int EntryReader::peek_pre_header(entry_header_pre* pre_header) { if (end()) { return -ENOENT; } int r = peek(sizeof(*pre_header), reinterpret_cast(pre_header)); if (r < 0) { CLS_ERR("ERROR: %s: peek() size=%zu failed: r=%d", __PRETTY_FUNCTION__, sizeof(pre_header), r); return r; } if (pre_header->magic != part_header.magic) { CLS_ERR("ERROR: %s: unexpected pre_header magic", __PRETTY_FUNCTION__); return -ERANGE; } return 0; } int EntryReader::get_next_entry(ceph::buffer::list* pbl, std::uint64_t* pofs, ceph::real_time* pmtime) { entry_header_pre pre_header; int r = peek_pre_header(&pre_header); if (r < 0) { CLS_ERR("ERROR: %s: peek_pre_header() failed: r=%d", __PRETTY_FUNCTION__, r); return r; } if (pofs) { *pofs = ofs; } CLS_LOG(5, "%s:%d: pre_header.pre_size=%" PRIu64, __PRETTY_FUNCTION__, __LINE__, uint64_t(pre_header.pre_size)); r = seek(pre_header.pre_size); if (r < 0) { CLS_ERR("ERROR: %s: failed to seek: r=%d", __PRETTY_FUNCTION__, r); return r; } ceph::buffer::list header; CLS_LOG(5, "%s:%d: pre_header.header_size=%d", __PRETTY_FUNCTION__, __LINE__, (int)pre_header.header_size); r = read(pre_header.header_size, &header); if (r < 0) { CLS_ERR("ERROR: %s: failed to read entry header: r=%d", __PRETTY_FUNCTION__, r); return r; } entry_header entry_header; auto iter = header.cbegin(); try { decode(entry_header, iter); } catch (ceph::buffer::error& err) { CLS_ERR("%s: failed decoding entry header", __PRETTY_FUNCTION__); return -EIO; } if (pmtime) { *pmtime = entry_header.mtime; } if (pbl) { r = read(pre_header.data_size, pbl); if (r < 0) { CLS_ERR("%s: failed reading data: r=%d", __PRETTY_FUNCTION__, r); return r; } } else { r = seek(pre_header.data_size); if (r < 0) { CLS_ERR("ERROR: %s: failed to seek: r=%d", __PRETTY_FUNCTION__, r); return r; } } return 0; } int trim_part(cls_method_context_t hctx, ceph::buffer::list *in, ceph::buffer::list *out) { CLS_LOG(5, "%s", __PRETTY_FUNCTION__); op::trim_part op; try { auto iter = in->cbegin(); decode(op, iter); } catch (const ceph::buffer::error &err) { CLS_ERR("ERROR: %s: failed to decode request", __PRETTY_FUNCTION__); return -EINVAL; } part_header part_header; int r = read_part_header(hctx, &part_header); if (r < 0) { CLS_ERR("%s: failed to read part header", __PRETTY_FUNCTION__); return r; } if (op.ofs < part_header.min_ofs) { return 0; } if (op.exclusive && op.ofs == part_header.min_ofs) { return 0; } if (op.ofs >= part_header.next_ofs) { if (full_part(part_header)) { /* * trim full part completely: remove object */ r = cls_cxx_remove(hctx); if (r < 0) { CLS_ERR("%s: ERROR: cls_cxx_remove() returned r=%d", __PRETTY_FUNCTION__, r); return r; } return 0; } part_header.min_ofs = part_header.next_ofs; part_header.min_index = part_header.max_index; } else { EntryReader reader(hctx, part_header, op.ofs); entry_header_pre pre_header; int r = reader.peek_pre_header(&pre_header); if (r < 0) { return r; } if (op.exclusive) { part_header.min_index = pre_header.index; } else { r = reader.get_next_entry(nullptr, nullptr, nullptr); if (r < 0) { CLS_ERR("ERROR: %s: unexpected failure at get_next_entry: r=%d", __PRETTY_FUNCTION__, r); return r; } part_header.min_index = pre_header.index + 1; } part_header.min_ofs = reader.get_ofs(); } r = write_part_header(hctx, part_header); if (r < 0) { CLS_ERR("%s: failed to write header: r=%d", __PRETTY_FUNCTION__, r); return r; } return 0; } int list_part(cls_method_context_t hctx, ceph::buffer::list* in, ceph::buffer::list* out) { CLS_LOG(5, "%s", __PRETTY_FUNCTION__); op::list_part op; try { auto iter = in->cbegin(); decode(op, iter); } catch (const buffer::error &err) { CLS_ERR("ERROR: %s: failed to decode request", __PRETTY_FUNCTION__); return -EINVAL; } part_header part_header; int r = read_part_header(hctx, &part_header); if (r < 0) { CLS_ERR("%s: failed to read part header", __PRETTY_FUNCTION__); return r; } EntryReader reader(hctx, part_header, op.ofs); if (op.ofs >= part_header.min_ofs && !reader.end()) { r = reader.get_next_entry(nullptr, nullptr, nullptr); if (r < 0) { CLS_ERR("ERROR: %s: unexpected failure at get_next_entry: r=%d", __PRETTY_FUNCTION__, r); return r; } } op::list_part_reply reply; auto max_entries = std::min(op.max_entries, op::MAX_LIST_ENTRIES); for (int i = 0; i < max_entries && !reader.end(); ++i) { ceph::buffer::list data; ceph::real_time mtime; std::uint64_t ofs; r = reader.get_next_entry(&data, &ofs, &mtime); if (r < 0) { CLS_ERR("ERROR: %s: unexpected failure at get_next_entry: r=%d", __PRETTY_FUNCTION__, r); return r; } reply.entries.emplace_back(std::move(data), ofs, mtime); } reply.more = !reader.end(); reply.full_part = full_part(part_header); encode(reply, *out); return 0; } int get_part_info(cls_method_context_t hctx, ceph::buffer::list *in, ceph::buffer::list *out) { CLS_LOG(5, "%s", __PRETTY_FUNCTION__); op::get_part_info op; try { auto iter = in->cbegin(); decode(op, iter); } catch (const ceph::buffer::error &err) { CLS_ERR("ERROR: %s: failed to decode request", __PRETTY_FUNCTION__); return -EINVAL; } op::get_part_info_reply reply; int r = read_part_header(hctx, &reply.header); if (r < 0) { CLS_ERR("%s: failed to read part header", __PRETTY_FUNCTION__); return r; } encode(reply, *out); return 0; } } } // namespace rados::cls::fifo CLS_INIT(fifo) { using namespace rados::cls::fifo; CLS_LOG(10, "Loaded fifo class!"); cls_handle_t h_class; cls_method_handle_t h_create_meta; cls_method_handle_t h_get_meta; cls_method_handle_t h_update_meta; cls_method_handle_t h_init_part; cls_method_handle_t h_push_part; cls_method_handle_t h_trim_part; cls_method_handle_t h_list_part; cls_method_handle_t h_get_part_info; cls_register(op::CLASS, &h_class); cls_register_cxx_method(h_class, op::CREATE_META, CLS_METHOD_RD | CLS_METHOD_WR, create_meta, &h_create_meta); cls_register_cxx_method(h_class, op::GET_META, CLS_METHOD_RD, get_meta, &h_get_meta); cls_register_cxx_method(h_class, op::UPDATE_META, CLS_METHOD_RD | CLS_METHOD_WR, update_meta, &h_update_meta); cls_register_cxx_method(h_class, op::INIT_PART, CLS_METHOD_RD | CLS_METHOD_WR, init_part, &h_init_part); cls_register_cxx_method(h_class, op::PUSH_PART, CLS_METHOD_RD | CLS_METHOD_WR, push_part, &h_push_part); cls_register_cxx_method(h_class, op::TRIM_PART, CLS_METHOD_RD | CLS_METHOD_WR, trim_part, &h_trim_part); cls_register_cxx_method(h_class, op::LIST_PART, CLS_METHOD_RD, list_part, &h_list_part); cls_register_cxx_method(h_class, op::GET_PART_INFO, CLS_METHOD_RD, get_part_info, &h_get_part_info); /* calculate entry overhead */ struct entry_header entry_header; ceph::buffer::list entry_header_bl; encode(entry_header, entry_header_bl); part_entry_overhead = sizeof(entry_header_pre) + entry_header_bl.length(); return; }