// -*- 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 * * 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_MCLIENTREPLY_H #define CEPH_MCLIENTREPLY_H #include "include/types.h" #include "include/fs_types.h" #include "include/mempool.h" #include "MClientRequest.h" #include "msg/Message.h" #include "include/ceph_features.h" #include "common/errno.h" #include "common/strescape.h" /*** * * MClientReply - container message for MDS reply to a client's MClientRequest * * key fields: * long tid - transaction id, so the client can match up with pending request * int result - error code, or fh if it was open * * for most requests: * trace is a vector of InodeStat's tracing from root to the file/dir/whatever * the operation referred to, so that the client can update it's info about what * metadata lives on what MDS. * * for readdir replies: * dir_contents is a vector of InodeStat*'s. * * that's mostly it, i think! * */ struct LeaseStat { // this matches ceph_mds_reply_lease __u16 mask = 0; __u32 duration_ms = 0; __u32 seq = 0; std::string alternate_name; LeaseStat() = default; LeaseStat(__u16 msk, __u32 dur, __u32 sq) : mask{msk}, duration_ms{dur}, seq{sq} {} void decode(ceph::buffer::list::const_iterator &bl, const uint64_t features) { using ceph::decode; if (features == (uint64_t)-1) { DECODE_START(2, bl); decode(mask, bl); decode(duration_ms, bl); decode(seq, bl); if (struct_v >= 2) decode(alternate_name, bl); DECODE_FINISH(bl); } else { decode(mask, bl); decode(duration_ms, bl); decode(seq, bl); } } }; inline std::ostream& operator<<(std::ostream& out, const LeaseStat& l) { out << "lease(mask " << l.mask << " dur " << l.duration_ms; if (l.alternate_name.size()) { out << " altn " << binstrprint(l.alternate_name, 128) << ")"; } return out << ")"; } struct DirStat { // mds distribution hints frag_t frag; __s32 auth; std::set<__s32> dist; DirStat() : auth(CDIR_AUTH_PARENT) {} DirStat(ceph::buffer::list::const_iterator& p, const uint64_t features) { decode(p, features); } void decode(ceph::buffer::list::const_iterator& p, const uint64_t features) { using ceph::decode; if (features == (uint64_t)-1) { DECODE_START(1, p); decode(frag, p); decode(auth, p); decode(dist, p); DECODE_FINISH(p); } else { decode(frag, p); decode(auth, p); decode(dist, p); } } // see CDir::encode_dirstat for encoder. }; struct InodeStat { vinodeno_t vino; uint32_t rdev = 0; version_t version = 0; version_t xattr_version = 0; ceph_mds_reply_cap cap; file_layout_t layout; utime_t ctime, btime, mtime, atime, snap_btime; uint32_t time_warp_seq = 0; uint64_t size = 0, max_size = 0; uint64_t change_attr = 0; uint64_t truncate_size = 0; uint32_t truncate_seq = 0; uint32_t mode = 0, uid = 0, gid = 0, nlink = 0; frag_info_t dirstat; nest_info_t rstat; fragtree_t dirfragtree; std::string symlink; // symlink content (if symlink) ceph_dir_layout dir_layout; ceph::buffer::list xattrbl; ceph::buffer::list inline_data; version_t inline_version; quota_info_t quota; mds_rank_t dir_pin; std::map snap_metadata; std::vector fscrypt_auth; std::vector fscrypt_file; public: InodeStat() {} InodeStat(ceph::buffer::list::const_iterator& p, const uint64_t features) { decode(p, features); } void decode(ceph::buffer::list::const_iterator &p, const uint64_t features) { using ceph::decode; if (features == (uint64_t)-1) { DECODE_START(7, p); decode(vino.ino, p); decode(vino.snapid, p); decode(rdev, p); decode(version, p); decode(xattr_version, p); decode(cap, p); { ceph_file_layout legacy_layout; decode(legacy_layout, p); layout.from_legacy(legacy_layout); } decode(ctime, p); decode(mtime, p); decode(atime, p); decode(time_warp_seq, p); decode(size, p); decode(max_size, p); decode(truncate_size, p); decode(truncate_seq, p); decode(mode, p); decode(uid, p); decode(gid, p); decode(nlink, p); decode(dirstat.nfiles, p); decode(dirstat.nsubdirs, p); decode(rstat.rbytes, p); decode(rstat.rfiles, p); decode(rstat.rsubdirs, p); decode(rstat.rctime, p); decode(dirfragtree, p); decode(symlink, p); decode(dir_layout, p); decode(xattrbl, p); decode(inline_version, p); decode(inline_data, p); decode(quota, p); decode(layout.pool_ns, p); decode(btime, p); decode(change_attr, p); if (struct_v > 1) { decode(dir_pin, p); } else { dir_pin = -ENODATA; } if (struct_v >= 3) { decode(snap_btime, p); } // else remains zero if (struct_v >= 4) { decode(rstat.rsnaps, p); } // else remains zero if (struct_v >= 5) { decode(snap_metadata, p); } if (struct_v >= 6) { bool fscrypt_flag; decode(fscrypt_flag, p); // ignore this } if (struct_v >= 7) { decode(fscrypt_auth, p); decode(fscrypt_file, p); } DECODE_FINISH(p); } else { decode(vino.ino, p); decode(vino.snapid, p); decode(rdev, p); decode(version, p); decode(xattr_version, p); decode(cap, p); { ceph_file_layout legacy_layout; decode(legacy_layout, p); layout.from_legacy(legacy_layout); } decode(ctime, p); decode(mtime, p); decode(atime, p); decode(time_warp_seq, p); decode(size, p); decode(max_size, p); decode(truncate_size, p); decode(truncate_seq, p); decode(mode, p); decode(uid, p); decode(gid, p); decode(nlink, p); decode(dirstat.nfiles, p); decode(dirstat.nsubdirs, p); decode(rstat.rbytes, p); decode(rstat.rfiles, p); decode(rstat.rsubdirs, p); decode(rstat.rctime, p); decode(dirfragtree, p); decode(symlink, p); if (features & CEPH_FEATURE_DIRLAYOUTHASH) decode(dir_layout, p); else memset(&dir_layout, 0, sizeof(dir_layout)); decode(xattrbl, p); if (features & CEPH_FEATURE_MDS_INLINE_DATA) { decode(inline_version, p); decode(inline_data, p); } else { inline_version = CEPH_INLINE_NONE; } if (features & CEPH_FEATURE_MDS_QUOTA) decode(quota, p); else quota = quota_info_t{}; if ((features & CEPH_FEATURE_FS_FILE_LAYOUT_V2)) decode(layout.pool_ns, p); if ((features & CEPH_FEATURE_FS_BTIME)) { decode(btime, p); decode(change_attr, p); } else { btime = utime_t(); change_attr = 0; } } } // see CInode::encode_inodestat for encoder. }; struct openc_response_t { _inodeno_t created_ino; interval_set delegated_inos; public: void encode(ceph::buffer::list& bl) const { using ceph::encode; ENCODE_START(1, 1, bl); encode(created_ino, bl); encode(delegated_inos, bl); ENCODE_FINISH(bl); } void decode(ceph::buffer::list::const_iterator &p) { using ceph::decode; DECODE_START(1, p); decode(created_ino, p); decode(delegated_inos, p); DECODE_FINISH(p); } } __attribute__ ((__may_alias__)); WRITE_CLASS_ENCODER(openc_response_t) class MClientReply final : public MMDSOp { public: // reply data struct ceph_mds_reply_head head {}; ceph::buffer::list trace_bl; ceph::buffer::list extra_bl; ceph::buffer::list snapbl; int get_op() const { return head.op; } void set_mdsmap_epoch(epoch_t e) { head.mdsmap_epoch = e; } epoch_t get_mdsmap_epoch() const { return head.mdsmap_epoch; } int get_result() const { #ifdef _WIN32 // libclient and libcephfs return CEPHFS_E* errors, which are basically // Linux errno codes. If we convert mds errors to host errno values, we // end up mixing error codes. // // For Windows, we'll preserve the original error value, which is expected // to be a linux (CEPHFS_E*) error. It may be worth doing the same for // other platforms. return head.result; #else return ceph_to_hostos_errno((__s32)(__u32)head.result); #endif } void set_result(int r) { head.result = r; } void set_unsafe() { head.safe = 0; } bool is_safe() const { return head.safe; } protected: MClientReply() : MMDSOp{CEPH_MSG_CLIENT_REPLY} {} MClientReply(const MClientRequest &req, int result = 0) : MMDSOp{CEPH_MSG_CLIENT_REPLY} { memset(&head, 0, sizeof(head)); header.tid = req.get_tid(); head.op = req.get_op(); head.result = result; head.safe = 1; } ~MClientReply() final {} public: std::string_view get_type_name() const override { return "creply"; } void print(std::ostream& o) const override { o << "client_reply(???:" << get_tid(); o << " = " << get_result(); if (get_result() <= 0) { o << " " << cpp_strerror(get_result()); } if (head.op & CEPH_MDS_OP_WRITE) { if (head.safe) o << " safe"; else o << " unsafe"; } o << ")"; } // serialization void decode_payload() override { using ceph::decode; auto p = payload.cbegin(); decode(head, p); decode(trace_bl, p); decode(extra_bl, p); decode(snapbl, p); ceph_assert(p.end()); } void encode_payload(uint64_t features) override { using ceph::encode; encode(head, payload); encode(trace_bl, payload); encode(extra_bl, payload); encode(snapbl, payload); } // dir contents void set_extra_bl(ceph::buffer::list& bl) { extra_bl = std::move(bl); } ceph::buffer::list& get_extra_bl() { return extra_bl; } const ceph::buffer::list& get_extra_bl() const { return extra_bl; } // trace void set_trace(ceph::buffer::list& bl) { trace_bl = std::move(bl); } ceph::buffer::list& get_trace_bl() { return trace_bl; } const ceph::buffer::list& get_trace_bl() const { return trace_bl; } private: template friend boost::intrusive_ptr ceph::make_message(Args&&... args); template friend MURef crimson::make_message(Args&&... args); }; #endif