]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
tool: add cephfs-meta-injection to show and amend info of inode
authorsimon gao <simon29rock@gmail.com>
Tue, 6 Aug 2019 09:58:58 +0000 (05:58 -0400)
committerSimon Gao <simon29rock@gmail.com>
Tue, 24 Mar 2020 08:39:01 +0000 (16:39 +0800)
Signed-off-by: simon gao <simon29rock@gmail.com>
14 files changed:
src/CMakeLists.txt
src/common/ceph_json.cc
src/common/ceph_json.h
src/common/fs_types.cc
src/include/fs_types.h
src/mds/CInode.cc
src/mds/CInode.h
src/mds/mdstypes.cc
src/mds/mdstypes.h
src/tools/cephfs/CMakeLists.txt
src/tools/cephfs/MetaTool.cc [new file with mode: 0644]
src/tools/cephfs/MetaTool.h [new file with mode: 0644]
src/tools/cephfs/cephfs-meta-injection.cc [new file with mode: 0644]
src/tools/cephfs/type_helper.hpp [new file with mode: 0644]

index 7ae4f162af8ac2768b4be790dcd49bfab79575d4..460dde54a5069a014d6f6c9175fd338baf60a13e 100644 (file)
@@ -829,6 +829,7 @@ add_custom_target(cephfs_testing DEPENDS
     ceph-fuse
     ceph-dencoder
     cephfs-journal-tool
+    cephfs-meta-injection
     cephfs-data-scan
     cephfs-table-tool)
 
index 50906a42e6d9d05978c4722f27bf02b7e34d1adc..7250b147c4dc436fdc220508e732558fd09f955d 100644 (file)
@@ -478,6 +478,19 @@ void decode_json_obj(utime_t& val, JSONObj *obj)
   }
 }
 
+void decode_json_obj(ceph_dir_layout& i, JSONObj *obj){
+
+    unsigned tmp;
+    JSONDecoder::decode_json("dir_hash", tmp, obj, true);
+    i.dl_dir_hash = tmp;
+    JSONDecoder::decode_json("unused1", tmp, obj, true);
+    i.dl_unused1 = tmp;
+    JSONDecoder::decode_json("unused2", tmp, obj, true);
+    i.dl_unused2 = tmp;
+    JSONDecoder::decode_json("unused3", tmp, obj, true);
+    i.dl_unused3 = tmp;
+}
+
 void encode_json(const char *name, const string& val, Formatter *f)
 {
   f->dump_string(name, val);
index 938b6f87fca0b3942069bcd908cf11c615595016..b22882b173cf3b784c6df52c3ec42ec639d7a6c5 100644 (file)
@@ -5,12 +5,12 @@
 #include <typeindex>
 #include <include/types.h>
 #include <boost/container/flat_map.hpp>
+#include <include/ceph_fs.h>
 
 #include "json_spirit/json_spirit.h"
 
 #include "Formatter.h"
 
-using namespace json_spirit;
 
 
 class JSONObj;
@@ -168,6 +168,7 @@ void decode_json_obj(bool& val, JSONObj *obj);
 void decode_json_obj(ceph::buffer::list& val, JSONObj *obj);
 class utime_t;
 void decode_json_obj(utime_t& val, JSONObj *obj);
+void decode_json_obj(ceph_dir_layout& i, JSONObj *obj);
 
 template<class T>
 void decode_json_obj(std::list<T>& l, JSONObj *obj)
index 0dd5528eaa2e68c9e4fea3e3ba2b1737c9faeda6..47021e360f8ccb91e74f018f24e57298cb3c54fd 100644 (file)
@@ -4,6 +4,7 @@
 #include "include/fs_types.h"
 #include "common/Formatter.h"
 #include "include/ceph_features.h"
+#include "common/ceph_json.h"
 
 void dump(const ceph_file_layout& l, ceph::Formatter *f)
 {
@@ -120,6 +121,15 @@ void file_layout_t::dump(ceph::Formatter *f) const
   f->dump_string("pool_ns", pool_ns);
 }
 
+void file_layout_t::decode_json(JSONObj *obj){
+
+    JSONDecoder::decode_json("stripe_unit", stripe_unit, obj, true);
+    JSONDecoder::decode_json("stripe_count", stripe_count, obj, true);
+    JSONDecoder::decode_json("object_size", object_size, obj, true);
+    JSONDecoder::decode_json("pool_id", pool_id, obj, true);
+    JSONDecoder::decode_json("pool_ns", pool_ns, obj, true);
+}
+
 void file_layout_t::generate_test_instances(std::list<file_layout_t*>& o)
 {
   o.push_back(new file_layout_t);
index 091f39f4b66521a42cb159ba00f5cdc46eda6644..757eb194b216d52b4ea6c9886bc72e832e02818f 100644 (file)
@@ -4,6 +4,7 @@
 #define CEPH_INCLUDE_FS_TYPES_H
 
 #include "types.h"
+class JSONObj;
 
 // --------------------------------------
 // ino
@@ -114,6 +115,7 @@ struct file_layout_t {
   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)
index 57908d996518dcee3f4075ab82c85565f44ed06a..0de21439be1886599007bcc9974459a768e71f98 100644 (file)
@@ -4350,6 +4350,43 @@ void InodeStoreBase::dump(Formatter *f) const
   f->dump_unsigned("damage_flags", damage_flags);
 }
 
+template <>
+void decode_json_obj(mempool::mds_co::string& t, JSONObj *obj){
+
+  t = mempool::mds_co::string(std::string_view(obj->get_data()));
+}
+
+void InodeStoreBase::decode_json(JSONObj *obj){
+
+  inode.decode_json(obj);
+  JSONDecoder::decode_json("symlink", symlink, obj, true);
+  // JSONDecoder::decode_json("dirfragtree", dirfragtree, obj, true); // cann't decode it now
+  JSONDecoder::decode_json("xattrs", InodeStoreBase::xattrs, xattrs_cb, obj, true);
+  // JSONDecoder::decode_json("old_inodes", old_inodes, InodeStoreBase::old_indoes_cb, obj, true); // cann't decode old_inodes now
+  JSONDecoder::decode_json("oldest_snap", oldest_snap.val, obj, true);
+  JSONDecoder::decode_json("damage_flags", damage_flags, obj, true);
+  //sr_t srnode;
+  //JSONDecoder::decode_json("snap_blob", srnode, obj, true);   // cann't decode it now
+  //snap_blob = srnode;
+}
+
+void InodeStoreBase::xattrs_cb(InodeStoreBase::mempool_xattr_map& c, JSONObj *obj){
+
+  string k;
+  JSONDecoder::decode_json("key", k, obj, true);
+  string v;
+  JSONDecoder::decode_json("val", v, obj, true);
+  c[k.c_str()] = buffer::copy(v.c_str(), v.size());
+}
+
+void InodeStoreBase::old_indoes_cb(InodeStoreBase::mempool_old_inode_map& c, JSONObj *obj){
+
+  snapid_t s;
+  JSONDecoder::decode_json("last", s.val, obj, true);
+  InodeStoreBase::mempool_old_inode i;
+  // i.decode_json(obj); // cann't decode now, simon
+  c[s] = i;
+}
 
 void InodeStore::generate_test_instances(std::list<InodeStore*> &ls)
 {
index 64295f651fb500f2fb8339aa0930114dc6d7a462..e76fd2b3f30720cca7003401225aa9fe6ff19c8b 100644 (file)
@@ -91,6 +91,10 @@ public:
   /* For test/debug output */
   void dump(ceph::Formatter *f) const;
 
+  void decode_json(JSONObj *obj);
+  static void xattrs_cb(InodeStoreBase::mempool_xattr_map& c, JSONObj *obj);
+  static void old_indoes_cb(InodeStoreBase::mempool_old_inode_map& c, JSONObj *obj);
+  
   /* For use by offline tools */
   __u32 hash_dentry_name(std::string_view dn);
   frag_t pick_dirfrag(std::string_view dn);
index abe459dfb14e6f20612c550e6020a9512135e5f9..360a8fc6db5c63b9702e1ef7c8f1f49e5aed0384 100644 (file)
@@ -55,6 +55,15 @@ void frag_info_t::dump(Formatter *f) const
   f->dump_unsigned("change_attr", change_attr);
 }
 
+void frag_info_t::decode_json(JSONObj *obj){
+
+  JSONDecoder::decode_json("version", version, obj, true);
+  //JSONDecoder::decode_json("mtime", mtime, obj, true);   // remove now
+  JSONDecoder::decode_json("num_files", nfiles, obj, true);
+  JSONDecoder::decode_json("num_subdirs", nsubdirs, obj, true);
+  JSONDecoder::decode_json("change_attr", change_attr, obj, true);
+}
+
 void frag_info_t::generate_test_instances(std::list<frag_info_t*>& ls)
 {
   ls.push_back(new frag_info_t);
@@ -126,6 +135,16 @@ void nest_info_t::dump(Formatter *f) const
   f->dump_stream("rctime") << rctime;
 }
 
+void nest_info_t::decode_json(JSONObj *obj){
+
+  JSONDecoder::decode_json("version", version, obj, true);
+  JSONDecoder::decode_json("rbytes", rbytes, obj, true);
+  JSONDecoder::decode_json("rfiles", rfiles, obj, true);
+  JSONDecoder::decode_json("rsubdirs", rsubdirs, obj, true);
+  JSONDecoder::decode_json("rsnaps", rsnaps, obj, true);
+  //JSONDecoder::decode_json("rctime", rctime, obj, true);   // remove now
+}
+
 void nest_info_t::generate_test_instances(std::list<nest_info_t*>& ls)
 {
   ls.push_back(new nest_info_t);
@@ -164,6 +183,12 @@ void quota_info_t::dump(Formatter *f) const
   f->dump_int("max_files", max_files);
 }
 
+void  quota_info_t::decode_json(JSONObj *obj){
+
+  JSONDecoder::decode_json("max_bytes", max_bytes, obj, true);
+  JSONDecoder::decode_json("max_files", max_files, obj, true);
+}
+
 void quota_info_t::generate_test_instances(std::list<quota_info_t *>& ls)
 {
   ls.push_back(new quota_info_t);
@@ -212,6 +237,12 @@ void client_writeable_range_t::dump(Formatter *f) const
   f->dump_unsigned("follows", follows);
 }
 
+void client_writeable_range_t::byte_range_t::decode_json(JSONObj *obj){
+
+  JSONDecoder::decode_json("first", first, obj, true);
+  JSONDecoder::decode_json("last", last, obj, true);
+}
+
 void client_writeable_range_t::generate_test_instances(std::list<client_writeable_range_t*>& ls)
 {
   ls.push_back(new client_writeable_range_t);
index e2c61bfb23306db172c21afcb159fb1a12ce7541..3f46860e78b562ad00dc19f8d957d51c5ef5a782 100644 (file)
@@ -30,6 +30,7 @@
 #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"
 
@@ -189,6 +190,7 @@ struct frag_info_t : public scatter_info_t {
   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
@@ -249,6 +251,7 @@ struct nest_info_t : public scatter_info_t {
   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
@@ -325,6 +328,7 @@ struct quota_info_t
   bool is_enable() const {
     return max_bytes || max_files;
   }
+  void decode_json(JSONObj *obj);
 
   int64_t max_bytes = 0;
   int64_t max_files = 0;
@@ -359,6 +363,8 @@ inline std::ostream& operator<<(std::ostream &out, const vinodeno_t &vino) {
 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;
@@ -528,6 +534,9 @@ struct inode_t {
   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*
@@ -832,6 +841,65 @@ void inode_t<Allocator>::dump(ceph::Formatter *f) const
   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)
 {
index f980f35f5ff1de9ac0800b8b045aeb89a286df5e..a739fb5d704bbe449bd7dac344678606854f76cf 100644 (file)
@@ -12,6 +12,15 @@ add_executable(cephfs-journal-tool ${cephfs_journal_tool_srcs})
 target_link_libraries(cephfs-journal-tool librados mds osdc global
   ${BLKID_LIBRARIES} ${CMAKE_DL_LIBS})
 
+set(cephfs-meta-injection_srcs
+  cephfs-meta-injection.cc
+  MetaTool.cc
+  RoleSelector.cc
+  MDSUtility.cc)
+add_executable(cephfs-meta-injection ${cephfs-meta-injection_srcs})
+target_link_libraries(cephfs-meta-injection librados mds osdc global
+  ${BLKID_LIBRARIES} ${CMAKE_DL_LIBS})
+
 set(cephfs_table_tool_srcs
   cephfs-table-tool.cc
   TableTool.cc
diff --git a/src/tools/cephfs/MetaTool.cc b/src/tools/cephfs/MetaTool.cc
new file mode 100644 (file)
index 0000000..1b36603
--- /dev/null
@@ -0,0 +1,814 @@
+#include <string.h>
+#include <map>
+#include <sstream>
+#include <fstream>
+
+#include "include/types.h"
+#include "common/Formatter.h"
+#include "common/ceph_argparse.h"
+#include "common/errno.h"
+#include "osdc/Journaler.h"
+#include "mds/mdstypes.h"
+#include "mds/LogEvent.h"
+#include "mds/InoTable.h"
+
+#include "mds/events/ENoOp.h"
+#include "mds/events/EUpdate.h"
+
+#include "mds/JournalPointer.h"
+// #include "JournalScanner.h"
+// #include "EventOutput.h"
+// #include "Dumper.h"
+// #include "Resetter.h"
+
+// #include "JournalTool.h"
+#include "MetaTool.h"
+#include "type_helper.hpp"
+#include "include/object.h"
+
+WRITE_RAW_ENCODER(char)
+WRITE_RAW_ENCODER(unsigned char)
+
+#define dout_context g_ceph_context
+#define dout_subsys ceph_subsys_mds
+#undef dout_prefix
+#define dout_prefix *_dout << __func__ << ": "
+
+
+void MetaTool::meta_op::release(){
+    for (const auto& i : inodes) {
+        delete i.second;
+    }
+    while(!sub_ops.empty()){
+        delete sub_ops.top();
+        sub_ops.pop();
+    }
+}
+
+void MetaTool::inode_meta_t::decode_json(JSONObj *obj){
+    unsigned long long tmp;
+    JSONDecoder::decode_json("snapid_t", tmp, obj, true);
+    _f.val = tmp;
+    JSONDecoder::decode_json("itype", tmp, obj, true);
+    _t = tmp;
+    if (NULL == _i)
+        _i = new InodeStore;
+    JSONDecoder::decode_json("store", *_i, obj, true);
+}
+
+void MetaTool::usage(){
+    generic_client_usage();
+}
+
+int MetaTool::main(string& mode,
+                   string& rank_str,
+                   string& minfo,
+                   string&ino,
+                   string& out,
+                   string& in,
+                   bool confirm
+                   ){
+    int r = 0;
+
+    std::string manual_meta_pool;
+    std::string manual_data_pool;
+    std::string manual_rank_num;
+    bool manual_mode = false;
+    if (minfo != ""){
+        vector<string> v;
+        string_split(minfo, v);
+        manual_meta_pool = v.size() >= 1?v[0]:"";
+        manual_data_pool = v.size() >= 2?v[1]:"";
+        manual_rank_num = v.size() >= 3?v[2]:"";
+        std::cout << "("<< minfo<< ")=>" 
+                  << " mpool: " << manual_meta_pool
+                  << " dpool: " << manual_data_pool
+                  << " rank: " << manual_rank_num
+                  << std::endl;
+        if (!manual_meta_pool.empty() && !manual_data_pool.empty() && !manual_rank_num.empty()){
+            std::cout << "you specify rank: " << manual_rank_num
+                      << " mpool: " << manual_meta_pool
+                      << " dpool: " << manual_data_pool
+                      << "\nstart manual mode!!"<< std::endl;
+            manual_mode = true;
+        }    
+    }
+
+   // RADOS init
+    r = rados.init_with_context(g_ceph_context);
+    if (r < 0) {
+        cerr << "RADOS unavailable" << std::endl;
+        return r;
+    }
+
+    if(_debug)
+        cout << "MetaTool: connecting to RADOS..." << std::endl;
+    r = rados.connect();
+    if (r < 0) {
+        cerr << "couldn't connect to cluster: " << cpp_strerror(r) << std::endl;
+        return r;
+    }
+
+    if (!manual_mode){
+        r = role_selector.parse(*fsmap, rank_str);
+        if (r != 0) {
+            cerr << "Couldn't determine MDS rank." << std::endl;
+            return r;
+        }
+    
+        auto fs = fsmap->get_filesystem(role_selector.get_ns());
+        assert(fs != nullptr);
+        
+        // prepare io for meta pool
+        int64_t const pool_id = fs->mds_map.get_metadata_pool();
+        features = fs->mds_map.get_up_features();
+        if (features == 0)
+            features = CEPH_FEATURES_SUPPORTED_DEFAULT;
+        else if (features != CEPH_FEATURES_SUPPORTED_DEFAULT){
+            cout << "I think we need to check the feature! : " << features << std::endl;
+            return -1;
+        }
+        
+        std::string pool_name;
+        r = rados.pool_reverse_lookup(pool_id, &pool_name);
+        if (r < 0) {
+            cerr << "Pool " << pool_id << " named in MDS map not found in RADOS!" << std::endl;
+            return r;
+        }
+        
+        if (_debug)
+            cout << "MetaTool: creating IoCtx.." << std::endl;
+        r = rados.ioctx_create(pool_name.c_str(), io_meta);
+        assert(r == 0);
+        output.dup(io_meta);
+        
+        // prepare io for data pool
+        const std::vector<int64_t>  & data_pools_v = fs->mds_map.get_data_pools();
+        for(auto i = data_pools_v.begin(); i != data_pools_v.end(); ++i){
+            r = rados.pool_reverse_lookup(*i, &pool_name);
+            if (r < 0) {
+                cerr << "Pool " << pool_id << " named in MDS map not found in RADOS!" << std::endl;
+                return r;
+            }
+            librados::IoCtx* io_data = new librados::IoCtx;
+            r = rados.ioctx_create(pool_name.c_str(), *io_data);
+            assert(r == 0);
+            io_data_v.push_back(io_data);
+        }
+        
+        for (auto role : role_selector.get_roles()) {
+            rank = role.rank;
+
+            int ret =  process(mode, ino, out, in, confirm);
+            cout << "executing for rank " << rank << " op[" <<mode<< "] ret : " << ret << std::endl;
+        }
+        
+    }else{
+        features = CEPH_FEATURES_SUPPORTED_DEFAULT;
+        r = rados.ioctx_create(manual_meta_pool.c_str(), io_meta);
+        assert(r == 0);
+
+        librados::IoCtx* io_data = new librados::IoCtx;
+        r = rados.ioctx_create(manual_data_pool.c_str(), *io_data);
+        assert(r == 0);
+        io_data_v.push_back(io_data);
+
+
+        rank = conv_t<int>(manual_rank_num);
+        int ret = process(mode, ino, out, in, confirm);
+        cout << "op[" << mode << "] ret : " << ret << std::endl;
+    }
+    return r;
+}
+    
+int MetaTool::process(string& mode, string& ino, string out, string in, bool confirm){
+    if (mode == "showm") {
+        return show_meta_info(ino, out);
+    }else if (mode == "listc") {
+        return list_meta_info(ino, out);
+    }else if (mode == "amend") {
+        return amend_meta_info(ino, in, confirm);
+    }else {
+        cerr << "bad command '" << mode << "'" << std::endl;
+        return -EINVAL;
+    }
+}
+    
+int MetaTool::amend_meta_info(string& ino, string& in, bool confirm){
+    if (ino != "0" && in != ""){
+        inodeno_t i_ino = conv2hexino(ino.c_str());
+        meta_op op(_debug, "", in, confirm);
+        meta_op::sub_op* nsop = new meta_op::sub_op(&op);
+        nsop->sub_op_t = meta_op::OP_AMEND;
+        nsop->sub_ino_t = meta_op::INO_DIR;
+        nsop->ino = i_ino;
+        op.push_op(nsop);
+        return op_process(op);
+    }else{
+        cerr << "parameter error? : ino = " << ino << std::endl;
+    }
+    return 0;
+}
+int MetaTool::list_meta_info(string& ino, string& out){
+    if (ino != "0") {
+        inodeno_t i_ino = conv2hexino(ino.c_str());
+        meta_op op(_debug, out);
+        meta_op::sub_op* nsop = new meta_op::sub_op(&op);
+        nsop->sub_op_t = meta_op::OP_LIST;
+        nsop->sub_ino_t = meta_op::INO_DIR;
+        nsop->ino = i_ino;
+        op.push_op(nsop);
+        return op_process(op);
+    }else{
+        cerr << "parameter error? : ino = " << ino << std::endl;
+    }
+    return 0;
+}
+int MetaTool::show_meta_info(string& ino, string& out){
+    if (ino != "0"){
+        inodeno_t i_ino = conv2hexino(ino.c_str());
+        meta_op op(_debug, out);
+
+        meta_op::sub_op* nsop = new meta_op::sub_op(&op);
+        nsop->sub_op_t = meta_op::OP_SHOW;
+        nsop->sub_ino_t = meta_op::INO_DIR;
+        nsop->ino = i_ino;
+        op.push_op(nsop);
+        return op_process(op);
+    }else{
+        cerr << "parameter error? : ino = " << ino << std::endl;
+    }
+    return 0;
+}
+
+int MetaTool::op_process(meta_op& op){
+    int r = 0;
+    while(!op.no_sops()){
+        if (_debug)
+            std::cout << "process : " << op.top_op()->detail() << std::endl;
+        switch(op.top_op()->sub_op_t){
+        case meta_op::OP_LIST:
+            r = list_meta(op);
+            break;
+        case meta_op::OP_LTRACE:
+            r = file_meta(op);
+            break;
+        case meta_op::OP_SHOW:
+            r = show_meta(op);
+            break;
+        case meta_op::OP_AMEND:
+            r = amend_meta(op);
+            break;
+        default:
+            cerr << "unknow op" << std::endl;
+        }
+        if (r == 0)
+            op.pop_op();
+        else if (r < 0)
+            op.clear_sops();
+    }
+    op.release();
+    return r;
+}
+
+int MetaTool::amend_meta(meta_op &op){
+    meta_op::sub_op* sop = op.top_op();
+    auto item = op.inodes.find(sop->ino);
+    auto item_k = op.okeys.find(sop->ino);
+    if (item != op.inodes.end() && item_k != op.okeys.end()){
+        if (_amend_meta(item_k->second, *(item->second), op.infile(), op) < 0)
+            return -1;
+    }else{
+        if (op.inodes.empty()){
+            meta_op::sub_op* nsop = new meta_op::sub_op(&op);
+            nsop->sub_op_t = meta_op::OP_LIST;
+            nsop->sub_ino_t = meta_op::INO_DIR;
+            nsop->trace_level = 0;
+            nsop->ino_c = sop->ino;
+            op.push_op(nsop);
+            return 1;
+        }else{
+            return -1;
+        }
+    }
+    return 0;
+}
+
+void MetaTool::inode_meta_t::encode(::ceph::bufferlist& bl, uint64_t features){
+    ::encode(_f, bl);
+    ::encode(_t, bl);
+    _i->encode_bare(bl, features);
+}
+int MetaTool::_amend_meta(string& k, inode_meta_t& inode_meta, const string& fn, meta_op& op){
+    JSONParser parser;
+    if(!parser.parse(fn.c_str())) {
+        cout << "Error parsing create user response" << std::endl;
+        return -1;
+    }
+    try {
+        inode_meta.decode_json(&parser);
+    } catch (JSONDecoder::err& e) {
+        cout << "failed to decode JSON input: " << e.what() << std::endl;
+        return -1;
+    }
+    if (!op.confirm_chg() || op.is_debug()){
+        cout << "you will amend info of inode ==>: " << std::endl;
+        _show_meta(inode_meta, "");
+    }
+    if (!op.confirm_chg()){
+        cout << "warning: this operation is irreversibl!!!\n"
+             << "         You must confirm that all logs of mds have been flushed!!!\n"
+             << "         if you want amend it, please add --yes-i-really-really-mean-it!!!"
+             << std::endl;
+            
+    }
+    bufferlist bl;
+    inode_meta.encode(bl, features);
+    map<string, bufferlist> to_set;
+    to_set[k].swap(bl);
+    inode_backpointer_t bp;
+    if (!op.top_op()->get_ancestor(bp))
+        return false;
+    frag_t frag;
+    auto item = op.inodes.find(bp.dirino);
+    if (item != op.inodes.end()){
+        frag = item->second->get_meta()->pick_dirfrag(bp.dname);
+    }
+    string oid = obj_name(bp.dirino, frag);
+    int ret = io_meta.omap_set(oid, to_set);
+    to_set.clear();
+    return ret;
+}
+
+int MetaTool::show_meta(meta_op &op){
+    meta_op::sub_op* sop = op.top_op();
+    auto item = op.inodes.find(sop->ino);
+    if (item != op.inodes.end()){
+        if (_show_meta(*(item->second), op.outfile()) < 0)
+            return -1;
+    }else{
+        if (op.inodes.empty()){
+            meta_op::sub_op* nsop = new meta_op::sub_op(&op);
+            nsop->sub_op_t = meta_op::OP_LIST;
+            nsop->sub_ino_t = meta_op::INO_DIR;
+            nsop->trace_level = 0;
+            nsop->ino_c = sop->ino;
+            op.push_op(nsop);
+            return 1;
+        }else{
+            return -1;
+        }
+    }
+    return 0;
+}
+int MetaTool::_show_meta(inode_meta_t& inode_meta, const string& fn){
+    std::stringstream ds;
+    std::string format = "json";
+    InodeStore& inode_data = *inode_meta.get_meta();
+    Formatter* f = Formatter::create(format);
+    f->enable_line_break();
+    f->open_object_section("meta");
+    f->dump_unsigned("snapid_t", inode_meta.get_snapid());
+    f->dump_unsigned("itype", inode_meta.get_type());
+    f->open_object_section("store");
+    inode_data.dump(f);
+    try {
+        if (inode_data.snap_blob.length()){
+            sr_t srnode;
+            auto p = inode_data.snap_blob.cbegin();
+            decode(srnode, p);
+            f->open_object_section("snap_blob");
+            srnode.dump(f);
+            f->close_section();    
+        }
+    }catch (const buffer::error &err){
+        cerr << "corrupt decode in snap_blob" 
+             << ": " << err << std::endl;
+        return -1;
+    }
+    
+    f->close_section();
+    f->close_section();
+    f->flush(ds);
+
+    if (fn != ""){
+        ofstream o;
+        o.open(fn);
+        if (o){
+            o << ds.str();
+            o.close();
+        }
+        else{
+            cout << "out to file (" << fn << ") failed" << std::endl;
+            cout << ds.str() << std::endl;
+        }
+            
+    }else
+        std::cout << ds.str() << std::endl;
+    return 0;
+}
+int MetaTool::list_meta(meta_op &op){
+    meta_op::sub_op* sop = op.top_op();
+
+    bool list_all = false;
+    string oid;
+    inodeno_t ino = sop->ino_c;
+    frag_t frag = sop->frag;
+    
+    if (sop->ino_c == 0){
+        list_all = true;
+        oid = obj_name(sop->ino, frag);
+    }else {
+        if (_debug)
+            std::cout << __func__ << " : " << sop->trace_level << " " << op.ancestors.size() << std::endl;
+       inode_backpointer_t bp;
+       if (sop->get_c_ancestor(bp)){
+           auto item = op.inodes.find(bp.dirino);
+           if (item != op.inodes.end()){
+               frag = item->second->get_meta()->pick_dirfrag(bp.dname);
+           }
+           oid = obj_name(bp.dirino, frag);
+       }else{
+           meta_op::sub_op* nsop = new meta_op::sub_op(&op);
+           nsop->ino = sop->ino_c;
+           nsop->sub_op_t = meta_op::OP_LTRACE;
+           nsop->sub_ino_t = meta_op::INO_DIR;
+           op.push_op(nsop);
+           return 1;
+       }
+    }
+    if (_debug)
+        std::cout << __func__ << " : " << string(list_all?"listall ":"info ") << oid << " "<< ino << std::endl;
+    bufferlist hbl;
+    int ret = io_meta.omap_get_header(oid, &hbl);
+    if (ret < 0){
+        std::cerr << __func__ << " : cantn't find it, maybe it (ino:"<< sop->ino<< ")isn't a normal dir!" << std::endl;
+        return -1;
+    }
+    
+    if (hbl.length() == 0){   // obj has splite
+        if (list_all){
+            if (frag == frag_t()){
+                auto item = op.inodes.find(sop->ino);
+                if (item != op.inodes.end()){
+                    inodeno_t tmp = sop->ino;
+                    op.pop_op();
+                    std::list<frag_t> frags;
+                    item->second->get_meta()->dirfragtree.get_leaves(frags);
+                    for (const auto &frag : frags){
+                        meta_op::sub_op* nsop = new meta_op::sub_op(&op);
+                        nsop->ino = tmp;
+                        nsop->sub_op_t = meta_op::OP_LIST;
+                        nsop->sub_ino_t = meta_op::INO_DIR;
+                        nsop->frag = frag;
+                        op.push_op(nsop);
+                    }
+                }else{
+                    meta_op::sub_op* nsop = new meta_op::sub_op(&op);
+                    nsop->ino_c = sop->ino;
+                    nsop->sub_op_t = meta_op::OP_LIST;
+                    nsop->sub_ino_t = meta_op::INO_DIR;
+                    op.push_op(nsop);
+                }
+                return 1;
+            }else{
+                cerr << __func__ << " missing some data (" << oid << ")???" << std::endl;
+                return -1;
+            }
+        }else{
+            if (frag == frag_t()){
+               inode_backpointer_t bp;
+               if (sop->get_c_ancestor(bp)){
+                   meta_op::sub_op* nsop = new meta_op::sub_op(&op);
+                   nsop->ino_c = bp.dirino;
+                   nsop->sub_op_t = meta_op::OP_LIST;
+                   nsop->sub_ino_t = meta_op::INO_DIR;
+                   nsop->trace_level = sop->trace_level + 1;
+                   op.push_op(nsop);
+                   return 1;
+               }else{
+                    cerr << __func__ << "cann't find obj(" << oid << ") ,miss ancestors or miss some objs??? " << std::endl;
+                    return -1;
+                }
+            }else{
+                cerr << __func__ << "missing some objs(" << oid << ")??? " << std::endl;
+                return -1;
+            }
+        }
+    }
+
+    fnode_t got_fnode;
+    try {
+        auto p = hbl.cbegin();
+        ::decode(got_fnode, p);
+    }catch (const buffer::error &err){
+        cerr << "corrupt fnode header in " << oid
+             << ": " << err << std::endl;
+        return -1;
+    }
+
+    if (_debug){
+        std::string format = "json";
+        Formatter* f = Formatter::create(format);
+        f->enable_line_break();
+        f->dump_string("type", "--fnode--");
+        f->open_object_section("fnode");
+        got_fnode.dump(f);
+        f->close_section();
+        f->flush(std::cout);
+        std::cout << std::endl;
+    }
+
+    // print children
+    std::map<string, bufferlist> out_vals;
+    int max_vals = 5;
+    io_meta.omap_get_vals(oid, "", max_vals, &out_vals);
+    
+    bool force_dirty = false;
+    const set<snapid_t> *snaps = NULL;
+    unsigned pos = out_vals.size() - 1;
+    std::string last_dname;
+    for (map<string, bufferlist>::iterator p = out_vals.begin();
+         p != out_vals.end();
+         ++p, --pos) {
+        string dname;
+        snapid_t last;
+        dentry_key_t::decode_helper(p->first, dname, last);
+        if(_debug)
+            
+        last_dname = dname;
+        try {
+            if (!list_all){
+                if (show_child(p->first, dname, last, p->second, pos, snaps,
+                               &force_dirty, ino, &op) == 1){
+                    return 0;
+                }
+            }else {
+                cout << "dname : " << dname << " " << last << std::endl;
+                if (show_child(p->first, dname, last, p->second, pos, snaps,
+                               &force_dirty) == 1)
+                    return 0;
+            }
+        } catch (const buffer::error &err) {
+            derr << "Corrupt dentry '" << dname << "' : "
+                 << err << "(" << "" << ")" << dendl;
+            return -1;
+        }  
+    }
+    while(out_vals.size() == (size_t)max_vals){
+        out_vals.clear();
+        io_meta.omap_get_vals(oid, last_dname, max_vals, &out_vals);
+        pos = out_vals.size() - 1;
+        for (map<string, bufferlist>::iterator p = (++out_vals.begin());
+             p != out_vals.end();
+             ++p, --pos) {
+            string dname;
+            snapid_t last;
+            dentry_key_t::decode_helper(p->first, dname, last);
+            last_dname = dname;
+            try {
+                if (!list_all){
+                    if (show_child(p->first, dname, last, p->second, pos, snaps,
+                                   &force_dirty, ino, &op) == 1){
+                        return 0;
+                    }
+                }else {
+                    cout << "dname : " << dname << " " << last << std::endl;
+                    if (show_child(p->first, dname, last, p->second, pos, snaps,
+                                   &force_dirty) == 1)
+                        return 0;
+                }
+            } catch (const buffer::error &err) {
+                derr << "Corrupt dentry '" << dname << "' : "
+                     << err << "(" << "" << ")" << dendl;
+                return -1;
+            }
+        }    
+    }
+
+    if (!list_all){
+        cerr << __func__ << "miss obj(ino:" << ino << ")??? " << std::endl;
+        return -1;
+    }
+    return 0;
+}
+unsigned long long MetaTool::conv2hexino(const char* ino){
+    unsigned long long iino = 0;
+    std::stringstream conv;
+    conv << ino;
+    conv >> iino;
+    printf("convert to hexadecimal ino  %s => %llx \n", ino, iino);
+    return iino;
+}
+
+int MetaTool::file_meta(meta_op &op){
+    int r = 0;
+    if (op.top_op()->sub_ino_t ==  meta_op::INO_DIR){
+        r = _file_meta(op, io_meta);
+    }else if (op.top_op()->sub_ino_t == meta_op::INO_F){
+        for (auto i = io_data_v.begin(); i != io_data_v.end(); ++i)
+            if ((r = _file_meta(op, **i)) == 1)
+                break;
+    }
+    if (r == 1){
+        inode_backpointer_t bp;
+        if (op.top_op()->get_ancestor(bp)){
+            return 0;
+        }else {
+            std::cerr << "no trace for obj (ino:" << op.top_op()->ino <<")??" << std::endl;
+            return -1;
+        }
+    }else if (op.top_op()->sub_ino_t == meta_op::INO_DIR){
+        std::cerr << "\tmaybe it's a file(ino:" << op.top_op()->ino << ")" << std::endl;
+        op.top_op()->sub_ino_t = meta_op::INO_F;
+        return 1;
+    }
+    
+    std::cerr << "can't get (ino:" << op.top_op()->ino <<")trace??" << std::endl;
+    return -1;
+}
+
+int MetaTool::_file_meta(meta_op &op, librados::IoCtx& io){
+    inodeno_t ino = op.top_op()->ino;
+    std::string oid = obj_name(ino);
+    bufferlist pointer_bl;
+    std::map<std::string, bufferlist> attrset;
+    int r = 0;
+    bool have_data = false;
+    r = io.getxattrs (oid.c_str(), attrset);
+    if (0 == r){
+        std::stringstream ds;
+        std::string format = "json";
+        Formatter* f = Formatter::create(format);
+        auto item = attrset.find("parent");
+        if (item != attrset.end()){
+            inode_backtrace_t i_bt;
+            try {
+                bufferlist::const_iterator q = item->second.cbegin();
+                i_bt.decode(q);
+                f->open_array_section("info");
+                have_data = true;
+                if (i_bt.ancestors.size() > 0)
+                    op.ancestors[ino] = i_bt.ancestors[0];
+                f->dump_string("type", "--i_bt--");
+                f->open_object_section("parent");
+                i_bt.dump(f);
+                f->close_section();
+            }catch (buffer::error &e){
+                cerr << "failed to decode parent of " << oid << std::endl;
+                return -1;
+            }
+        }else{
+            cerr << oid << " in " << io.get_pool_name()  << " , but no parent" << std::endl;
+            return -1;
+        }
+        
+        item = attrset.find("layout");
+        if (item != attrset.end()){
+            file_layout_t layout;
+            try {
+                auto q = item->second.cbegin();
+                layout.decode(q);
+                f->dump_string("type", "--layout--");
+                f->open_object_section("layout");
+                layout.dump(f);
+                f->close_section();
+                
+            }catch (buffer::error &e){
+                cerr << "failed to decode layout of " << oid << std::endl;
+                return -1;
+            }
+        }else{
+            cerr << oid << " in " << io.get_pool_name()  << " , but no layout" << std::endl;
+        }
+        if (have_data){
+            f->close_section();
+            f->flush(ds);
+            if (_debug)
+                cout << ino << " : "<< ds.str() << std::endl;
+            return 1;
+        }
+    }
+    return 0;
+}
+std::string MetaTool::obj_name(inodeno_t ino, uint64_t offset, const char *suffix) const{
+    char name[60];
+  snprintf(name, sizeof(name), "%llx.%08llx%s", (long long unsigned)ino, (long long unsigned)offset, suffix ? suffix : "");
+  return std::string(name);
+}
+std::string MetaTool::obj_name(inodeno_t ino, frag_t fg, const char *suffix) const
+{
+  char name[60];
+  snprintf(name, sizeof(name), "%llx.%08llx%s", (long long unsigned)ino, (long long unsigned)fg, suffix ? suffix : "");
+  return std::string(name);
+}
+
+std::string MetaTool::obj_name(const char* ino, uint64_t offset, const char *suffix) const
+{
+  char name[60];
+  snprintf(name, sizeof(name), "%s.%08llx%s", ino, (long long unsigned)offset, suffix ? suffix : "");
+  std::string out = name;
+  transform(out.begin(), out.end(), out.begin(),::tolower);
+  return out;
+}
+
+int MetaTool::show_child(std::string_view key,
+                         std::string_view dname,
+                         const snapid_t last,
+                         bufferlist &bl,
+                         const int pos,
+                         const std::set<snapid_t> *snaps,
+                         bool *force_dirty,
+                         inodeno_t sp_ino,
+                         meta_op* op){
+    bufferlist::const_iterator q = bl.cbegin();
+
+    snapid_t first;
+    ::decode(first, q);
+
+    // marker
+    char type;
+    ::decode(type, q);
+    
+    if (_debug)
+        std::cout << pos << " type '" << type << "' dname '" << dname
+                  << " [" << first << "," << last << "]"
+                  << std::endl;
+    // bool stale = false;
+    if (snaps && last != CEPH_NOSNAP){
+        derr << "!!!! erro !!!!" << dendl;
+        return -1;
+    }
+    
+    // CDentry *dn = NULL;
+    // look for existing dentry for _last_ snap, cann't process snap of obj
+    //if *(stale)
+    //    dn = lookup_exact_snap(dname, last);
+    //else
+    //    dn = lookup(dname, last);
+    if (type == 'L'){
+        // hard link
+        inodeno_t ino;
+        unsigned char d_type;
+        ::decode(ino, q);
+        ::decode(d_type, q);
+        if (sp_ino > 0){
+            if (sp_ino == ino){
+                std::cout << "find hard link : " << ino << "," << d_type << std::endl;
+                return 1;
+            }
+        }
+
+        std::cout << "hard link : " << ino << "," << d_type << std::endl;
+    }else if (type == 'I') {
+        // inode
+        // load inode data before lookuping up or constructing CInode
+        InodeStore& inode_data = *(new InodeStore);
+        inode_data.decode_bare(q);
+        
+        std::stringstream ds;
+        //std::string format = "json-pretty";
+        std::string format = "json";
+        // Formatter f = new JSONFormatter(false);
+        Formatter* f = Formatter::create(format);
+        f->enable_line_break();
+        f->open_object_section("meta");
+        f->dump_unsigned("snapid_t", first);
+        f->dump_unsigned("itype", type);
+        f->open_object_section("store");
+        inode_data.dump(f);
+        try {
+            if (inode_data.snap_blob.length()){
+                sr_t srnode;
+                auto p = inode_data.snap_blob.cbegin();
+                srnode.decode(p);
+                f->open_object_section("snap_blob");
+                srnode.dump(f);
+                f->close_section();    
+            }
+        }catch (const buffer::error &err){
+            cerr << "corrupt decode in snap_blob" 
+                 << ": " << err << std::endl;
+        }
+        f->close_section();
+        f->close_section();
+        f->flush(ds);
+        
+        if (sp_ino > 0 && op != NULL && sp_ino == inode_data.inode.ino){
+            inode_meta_t* tmp = new inode_meta_t(first, type, &inode_data);
+            op->inodes[inode_data.inode.ino] = tmp;
+            op->okeys[inode_data.inode.ino] = key.data();
+            return 1;
+        }else{
+            delete &inode_data;
+        }
+        
+        if (sp_ino == 0){
+            cout << ds.str() << std::endl;
+        }
+    }else{
+        std::cerr << __func__ << "unknow type : " << dname << "," << type << std::endl;
+    }
+    return 0;
+}
+
diff --git a/src/tools/cephfs/MetaTool.h b/src/tools/cephfs/MetaTool.h
new file mode 100644 (file)
index 0000000..a51933b
--- /dev/null
@@ -0,0 +1,255 @@
+#ifndef METATOOL_H__
+#define METATOOL_H__
+
+#include "MDSUtility.h"
+#include "RoleSelector.h"
+#include <vector>
+#include <stack>
+using std::stack;
+#include "mds/mdstypes.h"
+#include "mds/LogEvent.h"
+#include "mds/events/EMetaBlob.h"
+
+#include "include/rados/librados.hpp"
+#include "common/ceph_json.h"
+
+using ::ceph::bufferlist;
+class MetaTool : public MDSUtility
+{
+ public:
+    class inode_meta_t{
+    public:
+    inode_meta_t(snapid_t f = CEPH_NOSNAP, char t = 255, InodeStore* i = NULL):
+        _f(f),_t(t),_i(i){
+        };
+        snapid_t get_snapid() const { 
+            return _f;
+        }
+        InodeStore* get_meta() const {
+            if (_t == 'I')
+                return _i;
+            else
+                return NULL;
+        }
+        int get_type() const{
+            return _t;
+        }
+        void decode_json(JSONObj *obj);
+        void encode(::ceph::bufferlist& bl, uint64_t features);
+    private:
+        snapid_t _f;
+        char _t;
+        InodeStore* _i;
+    };
+ private:    
+    class meta_op{
+    public:
+    meta_op(bool debug = false, string out = "", string in = "", bool confirm = false):
+        _debug(debug),
+            _out(out),
+            _in(in),
+            _confirm(confirm)
+            {}
+        void release();
+        typedef enum{
+            OP_LIST = 0,
+            OP_LTRACE,
+            OP_SHOW,
+            OP_AMEND,
+             OP_NO
+        }op_type;
+        typedef enum{
+            INO_DIR = 0,
+            INO_F
+        }ino_type;
+        static string op_type_name(op_type& t){
+            string name;
+            switch (t){
+            case OP_LIST:
+                name = "list dir";
+                break;
+            case OP_LTRACE:
+                name = "load trace";
+                break;
+            case OP_SHOW:
+                name = "show info";
+                break;
+            case OP_AMEND:
+                name = "amend info";
+                break;
+            case OP_NO:
+                name = "noop";
+                break;
+            default:
+                name = "unknow op type";
+            }
+            return name;
+        }
+        static string ino_type_name(ino_type& t){
+            string name;
+            switch (t){
+            case INO_DIR:
+                name = "dir";
+                break;
+            case INO_F:
+                name = "file";
+                break;
+            default:
+                name = "unknow file type";
+            }
+            return name;
+        }
+        class sub_op{
+        public:
+        sub_op(meta_op* mop):
+            trace_level(0),
+                _proc(false),
+                _mop(mop)
+                {}
+            void print(){
+                std::cout << detail() << std::endl;
+            }
+            string detail(){
+                std::stringstream ds;
+                ds << " [sub_op]" << op_type_name(sub_op_t) << "|"
+                   << ino_type_name(sub_ino_t) << "|"
+                   << ino << "|"
+                   << frag << "|"
+                   << ino_c << "|"
+                   << trace_level << "|"
+                   << name;
+                return ds.str();
+            }
+            bool get_c_ancestor(inode_backpointer_t& bp){
+                if (!_mop || !ino_c)
+                    return false;
+                auto item = _mop->ancestors.find(ino_c);
+                if (item != _mop->ancestors.end()){
+                    bp = item->second;
+                    return true;
+                }else
+                    return false;
+            }
+            bool get_ancestor(inode_backpointer_t& bp){
+                if (!_mop || !ino)
+                    return false;
+                auto item = _mop->ancestors.find(ino);
+                if (item != _mop->ancestors.end()){
+                    bp = item->second;
+                    return true;
+                }else
+                    return false;
+            }
+            op_type sub_op_t;
+            ino_type sub_ino_t;
+            inodeno_t ino;
+            frag_t frag;
+            inodeno_t ino_c;
+            unsigned trace_level;
+            std::string name;
+            bool _proc;
+            meta_op* _mop;
+        };
+        
+        std::map<inodeno_t, inode_backpointer_t > ancestors;
+        std::map<inodeno_t, inode_meta_t* > inodes;
+        std::map<inodeno_t, string > okeys;
+
+        void clear_sops(){
+            while(!no_sops())
+                pop_op();
+        }
+        bool no_sops(){
+            return sub_ops.empty();
+        }
+        void push_op(sub_op* sop){
+            if (_debug)
+                std::cout << "<<====" << sop->detail() << std::endl;
+            sub_ops.push(sop);
+        }
+        sub_op* top_op(){
+            return sub_ops.top();
+        }
+        void pop_op(){
+            sub_op* sop = sub_ops.top();
+            if (_debug)
+                std::cout << "====>>" << sop->detail() << std::endl;;
+            delete sop;
+            sub_ops.pop();
+        }
+        string outfile(){
+            return _out;
+        }
+        string infile(){
+            return _in;
+        }
+        bool is_debug(){
+            return _debug;
+        }
+        bool confirm_chg(){
+            return _confirm;
+        }
+    private:
+        stack<sub_op*> sub_ops;
+        bool _debug;
+        string _out;
+        string _in;
+        bool _confirm;
+    };
+    MDSRoleSelector role_selector;
+    mds_rank_t rank;
+    
+    // I/O handles
+    librados::Rados rados;
+    librados::IoCtx io_meta;
+    std::vector<librados::IoCtx*> io_data_v;
+    librados::IoCtx output;
+    bool _debug;
+    uint64_t features;
+    
+    std::string obj_name(inodeno_t ino, frag_t fg = frag_t(), const char *suffix = NULL) const;
+    std::string obj_name(inodeno_t ino, uint64_t offset, const char *suffix = NULL) const;
+    std::string obj_name(const char* ino, uint64_t offset, const char *suffix = NULL) const;
+
+    // 0 : continue to find 
+    // 1 : stop to find it
+    int show_child(std::string_view key,
+                   std::string_view dname,
+                   const snapid_t last,
+                   bufferlist &bl,
+                   const int pos,
+                   const std::set<snapid_t> *snaps,
+                   bool *force_dirty,
+                   inodeno_t sp_ino = 0,
+                   meta_op* op = NULL
+                   );
+
+    int process(string& mode, string& ino, string out, string in, bool confirm);
+    int show_meta_info(string& ino, string& out);
+    int list_meta_info(string& ino, string& out);
+    int amend_meta_info(string& ino, string& in, bool confirm);
+    int op_process(meta_op &op);
+    int list_meta(meta_op &op);
+    int file_meta(meta_op &op);
+    int show_meta(meta_op &op);
+    int amend_meta(meta_op &op);
+
+ public:
+    int _file_meta(meta_op &op, librados::IoCtx& io);
+    int _show_meta(inode_meta_t& i, const string& fn);
+    int _amend_meta(string &k, inode_meta_t& i, const string& fn, meta_op& op);
+    static unsigned long long conv2hexino(const char* ino);
+    void usage();
+ MetaTool(bool debug=false):
+    _debug(debug){}
+    ~MetaTool(){}
+    int main(string& mode,
+             string& rank_str,
+             string& minfo,
+             string&ino,
+             string& out,
+             string& in,
+             bool confirm = false
+            );
+};
+#endif // METATOOL_H__
diff --git a/src/tools/cephfs/cephfs-meta-injection.cc b/src/tools/cephfs/cephfs-meta-injection.cc
new file mode 100644 (file)
index 0000000..b93d921
--- /dev/null
@@ -0,0 +1,99 @@
+#include <include/types.h>
+#include "common/config.h"
+#include "common/ceph_argparse.h"
+#include "common/errno.h"
+#include "global/global_init.h"
+
+#include "MetaTool.h"
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <boost/program_options.hpp>
+namespace po = boost::program_options;
+using std::string;
+using namespace std;
+static string version = "cephfs-meta-injection v1.1";
+
+int main(int argc, const char **argv){
+    vector<const char*> args;
+    argv_to_vec(argc, argv, args);
+    env_to_vec(args);
+    auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT,
+                           CODE_ENVIRONMENT_UTILITY, 0);
+    common_init_finish(g_ceph_context);
+
+    string rank_str, minfo, ino, out,in;
+    po::options_description general("general options");
+    general.add_options()
+        ("help,h", "produce help message")
+        ("debug", "show debug info")
+        ("rank,r", po::value<string>(&rank_str), "the rank of cephfs, default(0) (e.g. -r cephfs_a:0)")
+        ("minfo", po::value<string>(&minfo), "specify metapool, datapools and rank (e.g. cephfs_metadata_a:cephfs_data_a:0)")
+        ("ino,i", po::value<string>(&ino), "specify inode. e.g. 1099511627776, you can find it with cmd, 'ls -i'")
+        ("out,o", po::value<string>(&out), "output file")
+        ("in", po::value<string>(&in), "input file")
+        ("yes-i-really-really-mean-it", "need by amend info")
+        ;
+
+    string mode;
+    po::options_description modeoptions("mode options");
+    modeoptions.add_options()
+        ("mode", po::value<string>(&mode),
+         "\tconv : convert decimal ino to hex\n"  \
+         "\tlistc : list all obj of dir\n" \
+         "\tshowm : show the info of ino\n" \
+         "\tamend : amend part of the meta data\n"
+         );
+
+    po::positional_options_description p;
+    p.add("mode", 1);
+    
+    po::options_description all("all options");
+    all.add(modeoptions).add(general);
+    po::variables_map vm;
+    try{
+        po::store(po::command_line_parser(argc, argv).options(all).positional(p).run(), vm);
+    }catch(exception &e){
+        cerr << "error : " << e.what() << std::endl;
+        return -1;
+    }
+    catch(...){
+        cout << "param error" << std::endl;
+        return 0;
+    }
+    
+    boost::program_options::notify(vm);
+    if (vm.count("help")){
+        std::cout << version << std::endl;
+        std::cout << "usage : \n"
+                  << "  cephfs-meta-injection <conv|listc|showm|amend> -r <fsname:rank> -i <ino>"
+                  << std::endl;
+        std::cout << "example : \n"
+                  << "  amend info of inode(1099531628828)\n"
+                  << "    cephfs-meta-injection showm -r cephfs_a:0 -i 1099531628828 -o out\n"
+                  << "    alter file\n"
+                  << "    cephfs-meta-injection amend -r cephfs_a:0 -i 1099531628828 --in out --yes-i-really-mean-it"
+                  << std::endl;
+        std::cout << all << std::endl;
+        return 0;
+    }
+    
+    if (mode == "conv"){
+        MetaTool::conv2hexino(ino.c_str());
+        return 0;
+    }
+    
+    MetaTool mt(vm.count("debug"));
+    int rc = mt.init();
+    if (rc != 0) {
+        std::cerr << "error in initialization: " << cpp_strerror(rc) << std::endl;
+        return rc;
+    }
+    rc = mt.main(mode, rank_str, minfo, ino, out, in, vm.count("yes-i-really-really-mean-it"));
+    if (rc != 0) {
+        std::cerr << "error (" << cpp_strerror(rc) << ")" << std::endl;
+        return -1;
+    }
+    return rc;
+}
diff --git a/src/tools/cephfs/type_helper.hpp b/src/tools/cephfs/type_helper.hpp
new file mode 100644 (file)
index 0000000..06dfa0a
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef TYPE_HELPER_HPP__
+#define TYPE_HELPER_HPP__
+
+template<typename T1, typename T2>
+T1 conv_t(T2 s){
+    T1 target;
+    std::stringstream conv;
+    conv << s;
+    conv >> target;
+    return target;
+}
+
+void string_split(std::string str, vector<string>& out, string split = ":"){
+    std::cout << str << std::endl;
+    auto pos = str.find(split);
+    while(pos != std::string::npos){
+        std::cout << str.substr(0, pos) << std::endl;
+        out.push_back(str.substr(0, pos));
+        if (str.size() > pos + split.size()){
+            str = str.substr(pos + split.size());
+            pos = str.find(split);
+        }else
+            return;
+    }
+    out.push_back(str.substr());
+    return;
+}
+#endif // TYPE_HELPER_HPP__