{"action":"created","comment":{"url":"https://api.github.com/repos/ceph/ceph/pulls/comments/32610989","id":32610989,"diff_hunk":"@@ -0,0 +1,1267 @@\n+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-\n+// vim: ts=8 sw=2 smarttab\n+/*\n+ * Ceph - scalable distributed file system\n+ *\n+ * Copyright (C) 2015 Red Hat\n+ *\n+ * This is free software; you can redistribute it and/or\n+ * modify it under the terms of the GNU Lesser General Public\n+ * License version 2.1, as published by the Free Software\n+ * Foundation.  See file COPYING.\n+ *\n+ */\n+\n+#include \"common/errno.h\"\n+#include \"common/ceph_argparse.h\"\n+#include <fstream>\n+#include \"include/util.h\"\n+\n+#include \"mds/CInode.h\"\n+\n+#include \"DataScan.h\"\n+\n+#define dout_subsys ceph_subsys_mds\n+#undef dout_prefix\n+#define dout_prefix *_dout << \"datascan.\" << __func__ << \": \"\n+\n+void DataScan::usage()\n+{\n+  std::cout << \"Usage: \\n\"\n+    << \"  cephfs-data-scan init\\n\"\n+    << \"  cephfs-data-scan scan_extents <data pool name>\\n\"\n+    << \"  cephfs-data-scan scan_inodes <data pool name>\\n\"\n+    << std::endl;\n+\n+  generic_client_usage();\n+}\n+\n+int DataScan::main(const std::vector<const char*> &args)\n+{\n+  // Parse args\n+  // ==========\n+  if (args.size() < 1) {\n+    usage();\n+    return -EINVAL;\n+  }\n+\n+  // Common RADOS init: open metadata pool\n+  // =====================================\n+  librados::Rados rados;\n+  int r = rados.init_with_context(g_ceph_context);\n+  if (r < 0) {\n+    derr << \"RADOS unavailable\" << dendl;\n+    return r;\n+  }\n+\n+  // Consume any known key-value arguments\n+  for (std::vector<const char *>::const_iterator i = args.begin();\n+       i != args.end(); ++i) {\n+    const std::string arg(*i);\n+\n+    if (arg.substr(0, 1) == std::string(\"-\") && (i + 1 != args.end())) {\n+      i++;\n+      const std::string val(*i);\n+      if (arg == std::string(\"--output-dir\")) {\n+        driver = new LocalFileDriver(val, data_io);\n+      } else if (arg == std::string(\"-n\")) {\n+        std::string err;\n+        n = strict_strtoll(val.c_str(), 10, &err);\n+        if (!err.empty()) {\n+          std::cerr << \"Invalid worker number '\" << val << \"'\" << std::endl;\n+          return -EINVAL;\n+        }\n+      } else if (arg == std::string(\"-m\")) {\n+        std::string err;\n+        m = strict_strtoll(val.c_str(), 10, &err);\n+        if (!err.empty()) {\n+          std::cerr << \"Invalid worker count '\" << val << \"'\" << std::endl;\n+          return -EINVAL;\n+        }\n+      } else {\n+        std::cerr << \"Unknown argument '\" << arg << \"'\" << std::endl;\n+        return -EINVAL;\n+      }\n+    }\n+  }\n+\n+  // Default to output to metadata pool\n+  if (driver == NULL) {\n+    driver = new MetadataDriver();\n+  }\n+\n+  dout(4) << \"connecting to RADOS...\" << dendl;\n+  rados.connect();\n+  r = driver->init(rados, mdsmap);\n+  if (r < 0) {\n+    return r;\n+  }\n+\n+  std::string const &command = args[0];\n+\n+  // Initialize data_io for those commands that need it\n+  if (command == \"scan_inodes\"\n+     || command == \"scan_extents\") {\n+    if (args.size() < 2) {\n+      usage();\n+      return -EINVAL;\n+    }\n+\n+    const std::string data_pool_name = args[args.size() - 1];\n+    {\n+      data_pool_id = rados.pool_lookup(data_pool_name.c_str());\n+      if (data_pool_id < 0) {\n+        std::cerr << \"Data pool '\" << data_pool_name << \"' not found!\" << std::endl;\n+        return -ENOENT;\n+      } else {\n+        dout(4) << \"data pool '\" << data_pool_name\n+          << \"' has ID \" << data_pool_id << dendl;\n+      }\n+\n+      if (!mdsmap->is_data_pool(data_pool_id)) {\n+        std::cerr << \"Warning: pool '\" << data_pool_name << \"' is not a \"\n+          \"CephFS data pool!\" << std::endl;\n+      }\n+\n+      dout(4) << \"opening data pool '\" << data_pool_name << \"'\" << dendl;\n+      r = rados.ioctx_create(data_pool_name.c_str(), data_io);\n+      if (r != 0) {\n+        return r;\n+      }\n+    }\n+  }\n+\n+  // Finally, dispatch command\n+  if (command == \"scan_inodes\") {\n+    return recover();\n+  } else if (command == \"scan_extents\") {\n+    return recover_extents();\n+  } else if (command == \"init\") {\n+    return driver->init_roots(mdsmap->get_first_data_pool());\n+  } else {\n+    std::cerr << \"Unknown command '\" << command << \"'\" << std::endl;\n+    return -EINVAL;\n+  }\n+}\n+\n+int MetadataDriver::inject_unlinked_inode(\n+    inodeno_t inono, int mode, int64_t data_pool_id)\n+{\n+  // Compose\n+  object_t oid = InodeStore::get_object_name(inono, frag_t(), \".inode\");\n+  InodeStore inode;\n+  inode.inode.ino = inono;\n+  inode.inode.version = 1;\n+  inode.inode.xattr_version = 1;\n+  inode.inode.mode = 0500 | mode;\n+  // Fake size to 1, so that the directory doesn't appear to be empty\n+  // (we won't actually give the *correct* size here though)\n+  inode.inode.size = 1;\n+  inode.inode.dirstat.nfiles = 1;\n+\n+  inode.inode.ctime = \n+    inode.inode.mtime = ceph_clock_now(g_ceph_context);\n+  inode.inode.nlink = 1;\n+  inode.inode.truncate_size = -1ull;\n+  inode.inode.truncate_seq = 1;\n+\n+  // Force layout to default: should we let users override this so that\n+  // they don't have to mount the filesystem to correct it?\n+  inode.inode.layout = g_default_file_layout;\n+  inode.inode.layout.fl_pg_pool = data_pool_id;\n+\n+  // Serialize\n+  bufferlist inode_bl;\n+  ::encode(std::string(CEPH_FS_ONDISK_MAGIC), inode_bl);\n+  inode.encode(inode_bl);\n+\n+  // Write\n+  int r = metadata_io.write_full(oid.name, inode_bl);\n+  if (r != 0) {\n+    derr << \"Error writing '\" << oid.name << \"': \" << cpp_strerror(r) << dendl;\n+    return r;\n+  }\n+\n+  return r;\n+}\n+\n+int MetadataDriver::root_exists(inodeno_t ino, bool *result)\n+{\n+  object_t oid = InodeStore::get_object_name(ino, frag_t(), \".inode\");\n+  uint64_t size;\n+  time_t mtime;\n+  int r = metadata_io.stat(oid.name, &size, &mtime);\n+  if (r == -ENOENT) {\n+    *result = false;\n+    return 0;\n+  } else if (r < 0) {\n+    return r;\n+  }\n+\n+  *result = true;\n+  return 0;\n+}\n+\n+int MetadataDriver::init_roots(int64_t data_pool_id)\n+{\n+  int r = 0;\n+  r = inject_unlinked_inode(MDS_INO_ROOT, S_IFDIR|0755, data_pool_id);\n+  if (r != 0) {\n+    return r;\n+  }\n+  r = inject_unlinked_inode(MDS_INO_MDSDIR(0), S_IFDIR, data_pool_id);\n+  if (r != 0) {\n+    return r;\n+  }\n+\n+  return 0;\n+}\n+\n+int MetadataDriver::check_roots(bool *result)\n+{\n+  int r;\n+  r = root_exists(MDS_INO_ROOT, result);\n+  if (r != 0) {\n+    return r;\n+  }\n+  if (!*result) {\n+    return 0;\n+  }\n+\n+  r = root_exists(MDS_INO_MDSDIR(0), result);\n+  if (r != 0) {\n+    return r;\n+  }\n+  if (!*result) {\n+    return 0;\n+  }\n+\n+  return 0;\n+}\n+\n+/**\n+ * Stages:\n+ * PARALLEL\n+ *  1. Size and mtime recovery: scan ALL objects, and update 0th\n+ *   objects with max size and max mtime seen.\n+ * PARALLEL\n+ *  2. Inode recovery: scan ONLY 0th objects, and inject metadata\n+ *   into dirfrag OMAPs, creating blank dirfrags as needed.  No stats\n+ *   or rstats at this stage.  Inodes without backtraces go into\n+ *   lost+found\n+ * SERIAL\n+ *  3. Dirfrag statistics: depth first traverse into metadata tree,\n+ *    rebuilding dir sizes.\n+ *    XXX hmm, *or* we could use an object class to accumulate\n+ *    the number of insertions we've done on any given frag, and\n+ *    then do a pass where we just inject that accumulated number\n+ *    of insertions into the directory's size and the dirfrags stats?\n+ * PARALLEL\n+ *  4. Cleanup; go over all 0th objects (and dirfrags if we tagged\n+ *   anything onto them) and remove any of the xattrs that we\n+ *   used for accumulating.\n+ */\n+\n+\n+int parse_oid(const std::string &oid, uint64_t *inode_no, uint64_t *obj_id)\n+{\n+  if (oid.find(\".\") == std::string::npos || oid.find(\".\") == oid.size() - 1) {\n+    return -EINVAL;\n+  }\n+\n+  std::string err;\n+  std::string inode_str = oid.substr(0, oid.find(\".\"));\n+  *inode_no = strict_strtoll(inode_str.c_str(), 16, &err);\n+  if (!err.empty()) {\n+    return -EINVAL;\n+  }\n+\n+  std::string pos_string = oid.substr(oid.find(\".\") + 1);\n+  *obj_id = strict_strtoll(pos_string.c_str(), 16, &err);\n+  if (!err.empty()) {\n+    return -EINVAL;\n+  }\n+\n+  return 0;\n+}\n+\n+// Pending sharded pgls & add in progress mechanism for that\n+#undef SHARDEDPGLS\n+\n+int DataScan::recover_extents()\n+{\n+#ifdef SHARDED_PGLS\n+  float progress = 0.0;\n+  librados::NObjectIterator i = data_io.nobjects_begin(n, m);\n+#else\n+  librados::NObjectIterator i = data_io.nobjects_begin();\n+#endif\n+\n+  librados::NObjectIterator i_end = data_io.nobjects_end();\n+  int r = 0;\n+\n+  for (; i != i_end; ++i) {\n+    const std::string oid = i->get_oid();\n+#ifdef SHARDED_PGLS\n+    if (i.get_progress() != progress) {\n+      if (int(i.get_progress() * 100) / 5 != int(progress * 100) / 5) {\n+        std::cerr << percentify(i.get_progress()) << \"%\" << std::endl;\n+      }\n+      progress = i.get_progress();\n+    }\n+#endif\n+\n+    // Read size\n+    uint64_t size;\n+    time_t mtime;\n+    // FIXME: by using librados, I throw away precision because it wants\n+    // to make me use time_t instead of utime_t :-(\n+    r = data_io.stat(oid, &size, &mtime);\n+    if (r != 0) {\n+      dout(4) << \"Cannot stat '\" << oid << \"': skipping\" << dendl;\n+      continue;\n+    }\n+\n+    // I need to keep track of\n+    //  * The highest object ID seen\n+    //  * The size of the highest object ID seen\n+    //  * The largest object seen\n+    //\n+    //  Given those things, I can later infer the object chunking\n+    //  size, the offset of the last object (chunk size * highest ID seen)\n+    //  and the actual size (offset of last object + size of highest ID seen)\n+    //\n+    //  This logic doesn't take account of striping.\n+    uint64_t inode_no = 0;\n+    uint64_t obj_id = 0;\n+    r = parse_oid(oid, &inode_no, &obj_id);\n+    if (r != 0) {\n+      dout(4) << \"Bad object name '\" << oid << \"' skipping\" << dendl;\n+      continue;\n+    }\n+\n+    // Generate 0th object name, where we will accumulate sizes/mtimes\n+    object_t zeroth_object = InodeStore::get_object_name(inode_no, frag_t(), \"\");\n+\n+    // Construct a librados operation invoking our class method\n+    librados::ObjectReadOperation op;\n+    bufferlist inbl;\n+    ::encode(std::string(\"scan_size\"), inbl);\n+    ::encode(std::string(\"scan_mtime\"), inbl);\n+    ::encode(obj_id, inbl);\n+    ::encode(size, inbl);\n+    ::encode(mtime, inbl);\n+\n+    op.exec(\"cephfs\", \"accumulate_inode_metadata\", inbl);\n+\n+    bufferlist outbl;\n+    int r = data_io.operate(zeroth_object.name, &op, &outbl);\n+    if (r < 0) {\n+      derr << \"Failed to store size data from '\"\n+        << oid << \"' to '\" << zeroth_object.name << \"': \"\n+        << cpp_strerror(r) << dendl;\n+      continue;\n+    }\n+  }\n+\n+  return 0;\n+}\n+\n+/*\n+ * Other handy commands:\n+ *  A Find inode by backtrace expression, e.g. \"myimportantfile\"\n+ *  B Selectively recover only particular inodes, e.g. those identified\n+ *    by A.\n+ *  C When recovering inodes, write files out to local disk instead of\n+ *    trying to inject them into the CephFS metadata pool.\n+ */\n+\n+int DataScan::recover()\n+{\n+#ifdef SHARDED_PGLS\n+  float progress = 0.0;\n+  librados::NObjectIterator i = data_io.nobjects_begin(n, m);\n+#else\n+  librados::NObjectIterator i = data_io.nobjects_begin();\n+#endif\n+  librados::NObjectIterator i_end = data_io.nobjects_end();\n+\n+  bool roots_present;\n+  int r = driver->check_roots(&roots_present);\n+  if (r != 0) {\n+    derr << \"Unexpected error checking roots: '\"\n+      << cpp_strerror(r) << \"'\" << dendl;\n+    return r;\n+  }\n+\n+  if (!roots_present) {\n+    std::cerr << \"Some or all system inodes are absent.  Run 'init' from \"\n+      \"one node before running 'recover'\" << std::endl;\n+    return -EIO;\n+  }\n+\n+  for (; i != i_end; ++i) {\n+    const std::string oid = i->get_oid();\n+#ifdef SHARDED_PGLS\n+    if (i.get_progress() != progress) {\n+      if (int(i.get_progress() * 100) / 5 != int(progress * 100) / 5) {\n+        std::cerr << percentify(i.get_progress()) << \"%\" << std::endl;\n+      }\n+      progress = i.get_progress();\n+    }\n+#endif\n+\n+    uint64_t obj_name_ino = 0;\n+    uint64_t obj_name_offset = 0;\n+    r = parse_oid(oid, &obj_name_ino, &obj_name_offset);\n+    if (r != 0) {\n+      dout(4) << \"Bad object name '\" << oid << \"', skipping\" << dendl;\n+      continue;\n+    }\n+\n+    // We are only interested in 0th objects during this phase: we touched\n+    // the other objects during recover_extents\n+    if (obj_name_offset != 0) {\n+      continue;\n+    }\n+\n+    // Read backtrace\n+    bool have_backtrace = false;\n+    bufferlist parent_bl;\n+    int r = data_io.getxattr(oid, \"parent\", parent_bl);\n+    if (r == -ENODATA) {\n+      dout(10) << \"No backtrace on '\" << oid << \"'\" << dendl;\n+    } else if (r < 0) {\n+      // TODO: accumulate these errors in a structure we can output\n+      dout(4) << \"Unexpected error on '\" << oid << \"': \" << cpp_strerror(r) << dendl;\n+      continue;\n+    }\n+\n+    // Deserialize backtrace\n+    inode_backtrace_t backtrace;\n+    if (parent_bl.length()) {\n+      try {\n+        bufferlist::iterator q = parent_bl.begin();\n+        backtrace.decode(q);\n+        have_backtrace = true;\n+      } catch (buffer::error &e) {\n+        dout(4) << \"Corrupt backtrace on '\" << oid << \"': \" << e << dendl;\n+      }\n+    }\n+\n+    // Santity checking backtrace ino against object name\n+    if (have_backtrace && backtrace.ino != obj_name_ino) {\n+      dout(4) << \"Backtrace ino 0x\" << std::hex << backtrace.ino\n+        << \" doesn't match object name ino 0x\" << obj_name_ino\n+        << std::dec << dendl;\n+      have_backtrace = false;\n+    }\n+\n+    // Read size\n+    uint64_t obj_size = 0;\n+    time_t obj_mtime = 0;\n+    r = data_io.stat(oid, &obj_size, &obj_mtime);\n+    if (r != 0) {\n+      dout(4) << \"Cannot stat '\" << oid << \"': skipping\" << dendl;\n+      continue;\n+    }\n+\n+    // Read accumulated size from scan_extents phase\n+    uint64_t file_size = 0;\n+    uint32_t chunk_size = g_default_file_layout.fl_object_size;\n+    bufferlist scan_size_bl;\n+    r = data_io.getxattr(oid, \"scan_size\", scan_size_bl);\n+    if (r >= 0) {\n+      try {\n+        bufferlist::iterator scan_size_bl_iter = scan_size_bl.begin();\n+        uint64_t max_obj_id;\n+        uint64_t max_obj_size;\n+        ::decode(max_obj_id, scan_size_bl_iter);\n+        ::decode(max_obj_size, scan_size_bl_iter);\n+\n+        if (max_obj_id > 0) {\n+          // If there are more objects, and this object is a power of two\n+          // that is greater than the size of the last object, then make\n+          // an educated guess that this object's size is probably the chunk\n+          // size.\n+        \n+          // TODO: provide a means for the user to specify the layout, as\n+          // for e.g. striped layouts there is no way we can infer it.\n+          if ((obj_size & (obj_size - 1)) == 0 && obj_size >= max_obj_size) {\n+            chunk_size = obj_size;\n+          } else {\n+            chunk_size = g_default_file_layout.fl_object_size;\n+          }\n+          file_size = chunk_size * max_obj_id + max_obj_size;\n+        } else {\n+          file_size = obj_size;\n+        }\n+      } catch (const buffer::error &err) {\n+        dout(4) << \"Invalid size attr on '\" << oid << \"'\" << dendl;\n+        file_size = obj_size;\n+      }\n+    } else {\n+      file_size = obj_size;\n+    }\n+\n+    // Read accumulated mtime from scan_extents phase\n+    time_t file_mtime = 0;\n+    bufferlist scan_mtime_bl;\n+    r = data_io.getxattr(oid, \"scan_mtime\", scan_mtime_bl);\n+    if (r >= 0) {\n+      try {\n+        bufferlist::iterator scan_mtime_bl_iter = scan_mtime_bl.begin();\n+        ::decode(file_mtime, scan_mtime_bl_iter);\n+      } catch (const buffer::error &err) {\n+        dout(4) << \"Invalid mtime attr on '\" << oid << \"'\" << dendl;\n+        file_mtime = obj_mtime;\n+      }\n+    } else {\n+      file_mtime = obj_mtime;\n+    }\n+\n+    // Inject inode to the metadata pool\n+    if (have_backtrace) {\n+      // TODO: inspect backtrace and fall back to lost+found\n+      // for some or all stray cases.\n+      r = driver->inject_with_backtrace(\n+          backtrace, file_size, file_mtime, chunk_size, data_pool_id);\n+      if (r < 0) {\n+        dout(4) << \"Error injecting 0x\" << std::hex << backtrace.ino\n+          << std::dec << \" with backtrace: \" << cpp_strerror(r) << dendl;\n+      }\n+    } else {\n+      r = driver->inject_lost_and_found(\n+          obj_name_ino, file_size, file_mtime, chunk_size, data_pool_id);\n+      if (r < 0) {\n+        dout(4) << \"Error injecting 0x\" << std::hex << obj_name_ino\n+          << std::dec << \" into lost+found: \" << cpp_strerror(r) << dendl;\n+      }\n+    }\n+  }\n+\n+  return 0;\n+}\n+\n+int MetadataDriver::read_fnode(inodeno_t ino, frag_t frag, fnode_t *fnode)\n+{\n+  assert(fnode != NULL);\n+\n+  object_t frag_oid = InodeStore::get_object_name(ino, frag, \"\");\n+  bufferlist old_fnode_bl;\n+  int r = metadata_io.omap_get_header(frag_oid.name, &old_fnode_bl);\n+  if (r < 0) {\n+    return r;\n+  }\n+\n+  bufferlist::iterator old_fnode_iter = old_fnode_bl.begin();\n+  try {\n+    fnode_t old_fnode;\n+    old_fnode.decode(old_fnode_iter);\n+  } catch (const buffer::error &err) {\n+    return -EINVAL;\n+  }\n+\n+  return 0;\n+}\n+\n+int MetadataDriver::read_dentry(inodeno_t parent_ino, frag_t frag,\n+                const std::string &dname, InodeStore *inode)\n+{\n+  assert(inode != NULL);\n+\n+\n+  std::string key;\n+  dentry_key_t dn_key(CEPH_NOSNAP, dname.c_str());\n+  dn_key.encode(key);\n+\n+  std::set<std::string> keys;\n+  keys.insert(key);\n+  std::map<std::string, bufferlist> vals;\n+  object_t frag_oid = InodeStore::get_object_name(parent_ino, frag, \"\");\n+  int r = metadata_io.omap_get_vals_by_keys(frag_oid.name, keys, &vals);  \n+  dout(20) << \"oid=\" << frag_oid.name\n+           << \" dname=\" << dname\n+           << \" frag=\" << frag\n+           << \", r=\" << r << dendl;\n+  if (r < 0) {\n+    return r;\n+  }\n+\n+  if (vals.find(key) == vals.end()) {\n+    dout(20) << key << \" not found in result\" << dendl;\n+    return -ENOENT;\n+  }\n+\n+  try {\n+    bufferlist::iterator q = vals[key].begin();\n+    snapid_t dnfirst;\n+    ::decode(dnfirst, q);\n+    char dentry_type;\n+    ::decode(dentry_type, q);\n+    if (dentry_type == 'I') {\n+      inode->decode_bare(q);\n+      return 0;\n+    } else {\n+      dout(20) << \"dentry type '\" << dentry_type << \"': cannot\"\n+                  \"read an inode out of that\" << dendl;\n+      return -EINVAL;\n+    }\n+  } catch (const buffer::error &err) {\n+    dout(20) << \"encoding error in dentry 0x\" << std::hex << parent_ino\n+             << std::dec << \"/\" << dname << dendl;\n+    return -EINVAL;\n+  }\n+\n+  return 0;\n+}\n+\n+int MetadataDriver::inject_lost_and_found(\n+    inodeno_t ino, uint64_t file_size, time_t file_mtime, uint32_t chunk_size,\n+    int64_t data_pool_id)\n+{\n+  // Create lost+found if doesn't exist\n+  bool created = false;\n+  int r = find_or_create_dirfrag(CEPH_INO_ROOT, frag_t(), &created);\n+  if (r < 0) {\n+    return r;\n+  }\n+  InodeStore lf_ino;\n+  r = read_dentry(CEPH_INO_ROOT, frag_t(), \"lost+found\", &lf_ino);\n+  if (r == -ENOENT || r == -EINVAL) {\n+    // Inject dentry\n+    lf_ino.inode.mode = 0755 | S_IFDIR;\n+    lf_ino.inode.dirstat.nfiles = 1;\n+    lf_ino.inode.size = 1;\n+    lf_ino.inode.nlink = 1;\n+    lf_ino.inode.ino = CEPH_INO_LOST_AND_FOUND;\n+    lf_ino.inode.version = 1;\n+    lf_ino.inode.backtrace_version = 1;\n+    // TODO: we should probably fragment this by default, as we will\n+    // potentially write very many dentries.\n+    r = inject_linkage(CEPH_INO_ROOT, \"lost+found\", frag_t(), lf_ino);\n+    if (r < 0) {\n+      return r;\n+    }\n+  } else {\n+    if (!(lf_ino.inode.mode & S_IFDIR)) {\n+      derr << \"lost+found exists but is not a directory!\" << dendl;\n+      // In this case we error out, and the user should do something about\n+      // this problem.\n+      return -EINVAL;\n+    }\n+  }\n+\n+  r = find_or_create_dirfrag(CEPH_INO_LOST_AND_FOUND, frag_t(), &created);\n+  if (r < 0) {\n+    return r;\n+  }\n+\n+  InodeStore recovered_ino;\n+  recovered_ino.inode.mode = 0500 | S_IFREG;\n+  recovered_ino.inode.size = file_size;\n+  recovered_ino.inode.max_size_ever = file_size;\n+  recovered_ino.inode.mtime.tv.tv_sec = file_mtime;\n+  recovered_ino.inode.atime.tv.tv_sec = file_mtime;\n+  recovered_ino.inode.ctime.tv.tv_sec = file_mtime;\n+\n+  recovered_ino.inode.layout = g_default_file_layout;\n+  recovered_ino.inode.layout.fl_object_size = chunk_size;\n+  recovered_ino.inode.layout.fl_stripe_unit = chunk_size;\n+  recovered_ino.inode.layout.fl_pg_pool = data_pool_id;\n+\n+  recovered_ino.inode.truncate_seq = 1;\n+  recovered_ino.inode.truncate_size = -1ull;\n+\n+  recovered_ino.inode.inline_data.version = CEPH_INLINE_NONE;","path":"src/tools/cephfs/DataScan.cc","position":676,"original_position":676,"commit_id":"cc6d9e058ede24110fac6c545e049d06c7f0fe46","original_commit_id":"cc6d9e058ede24110fac6c545e049d06c7f0fe46","user":{"login":"gregsfortytwo","id":908479,"avatar_url":"https://avatars.githubusercontent.com/u/908479?v=3","gravatar_id":"","url":"https://api.github.com/users/gregsfortytwo","html_url":"https://github.com/gregsfortytwo","followers_url":"https://api.github.com/users/gregsfortytwo/followers","following_url":"https://api.github.com/users/gregsfortytwo/following{/other_user}","gists_url":"https://api.github.com/users/gregsfortytwo/gists{/gist_id}","starred_url":"https://api.github.com/users/gregsfortytwo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gregsfortytwo/subscriptions","organizations_url":"https://api.github.com/users/gregsfortytwo/orgs","repos_url":"https://api.github.com/users/gregsfortytwo/repos","events_url":"https://api.github.com/users/gregsfortytwo/events{/privacy}","received_events_url":"https://api.github.com/users/gregsfortytwo/received_events","type":"User","site_admin":false},"body":"Ungh. We need a ticket to do something about inline files during this process.","created_at":"2015-06-17T09:54:43Z","updated_at":"2015-06-17T09:54:43Z","html_url":"https://github.com/ceph/ceph/pull/4939#discussion_r32610989","pull_request_url":"https://api.github.com/repos/ceph/ceph/pulls/4939","_links":{"self":{"href":"https://api.github.com/repos/ceph/ceph/pulls/comments/32610989"},"html":{"href":"https://github.com/ceph/ceph/pull/4939#discussion_r32610989"},"pull_request":{"href":"https://api.github.com/repos/ceph/ceph/pulls/4939"}}},"pull_request":{"url":"https://api.github.com/repos/ceph/ceph/pulls/4939","id":37563590,"html_url":"https://github.com/ceph/ceph/pull/4939","diff_url":"https://github.com/ceph/ceph/pull/4939.diff","patch_url":"https://github.com/ceph/ceph/pull/4939.patch","issue_url":"https://api.github.com/repos/ceph/ceph/issues/4939","number":4939,"state":"open","locked":false,"title":"RFC: cephfs-data-scan (offline backward file recovery)","user":{"login":"jcsp","id":944640,"avatar_url":"https://avatars.githubusercontent.com/u/944640?v=3","gravatar_id":"","url":"https://api.github.com/users/jcsp","html_url":"https://github.com/jcsp","followers_url":"https://api.github.com/users/jcsp/followers","following_url":"https://api.github.com/users/jcsp/following{/other_user}","gists_url":"https://api.github.com/users/jcsp/gists{/gist_id}","starred_url":"https://api.github.com/users/jcsp/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jcsp/subscriptions","organizations_url":"https://api.github.com/users/jcsp/orgs","repos_url":"https://api.github.com/users/jcsp/repos","events_url":"https://api.github.com/users/jcsp/events{/privacy}","received_events_url":"https://api.github.com/users/jcsp/received_events","type":"User","site_admin":false},"body":"","created_at":"2015-06-12T13:32:15Z","updated_at":"2015-06-17T09:54:43Z","closed_at":null,"merged_at":null,"merge_commit_sha":"b1be70f2b02495b8a5e18e5db554191958c44c75","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/ceph/ceph/pulls/4939/commits","review_comments_url":"https://api.github.com/repos/ceph/ceph/pulls/4939/comments","review_comment_url":"https://api.github.com/repos/ceph/ceph/pulls/comments{/number}","comments_url":"https://api.github.com/repos/ceph/ceph/issues/4939/comments","statuses_url":"https://api.github.com/repos/ceph/ceph/statuses/cc6d9e058ede24110fac6c545e049d06c7f0fe46","head":{"label":"ceph:wip-offline-backward","ref":"wip-offline-backward","sha":"cc6d9e058ede24110fac6c545e049d06c7f0fe46","user":{"login":"ceph","id":1015767,"avatar_url":"https://avatars.githubusercontent.com/u/1015767?v=3","gravatar_id":"","url":"https://api.github.com/users/ceph","html_url":"https://github.com/ceph","followers_url":"https://api.github.com/users/ceph/followers","following_url":"https://api.github.com/users/ceph/following{/other_user}","gists_url":"https://api.github.com/users/ceph/gists{/gist_id}","starred_url":"https://api.github.com/users/ceph/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ceph/subscriptions","organizations_url":"https://api.github.com/users/ceph/orgs","repos_url":"https://api.github.com/users/ceph/repos","events_url":"https://api.github.com/users/ceph/events{/privacy}","received_events_url":"https://api.github.com/users/ceph/received_events","type":"Organization","site_admin":false},"repo":{"id":2310495,"name":"ceph","full_name":"ceph/ceph","owner":{"login":"ceph","id":1015767,"avatar_url":"https://avatars.githubusercontent.com/u/1015767?v=3","gravatar_id":"","url":"https://api.github.com/users/ceph","html_url":"https://github.com/ceph","followers_url":"https://api.github.com/users/ceph/followers","following_url":"https://api.github.com/users/ceph/following{/other_user}","gists_url":"https://api.github.com/users/ceph/gists{/gist_id}","starred_url":"https://api.github.com/users/ceph/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ceph/subscriptions","organizations_url":"https://api.github.com/users/ceph/orgs","repos_url":"https://api.github.com/users/ceph/repos","events_url":"https://api.github.com/users/ceph/events{/privacy}","received_events_url":"https://api.github.com/users/ceph/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/ceph/ceph","description":"Ceph is a distributed object, block, and file storage platform ","fork":false,"url":"https://api.github.com/repos/ceph/ceph","forks_url":"https://api.github.com/repos/ceph/ceph/forks","keys_url":"https://api.github.com/repos/ceph/ceph/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ceph/ceph/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ceph/ceph/teams","hooks_url":"https://api.github.com/repos/ceph/ceph/hooks","issue_events_url":"https://api.github.com/repos/ceph/ceph/issues/events{/number}","events_url":"https://api.github.com/repos/ceph/ceph/events","assignees_url":"https://api.github.com/repos/ceph/ceph/assignees{/user}","branches_url":"https://api.github.com/repos/ceph/ceph/branches{/branch}","tags_url":"https://api.github.com/repos/ceph/ceph/tags","blobs_url":"https://api.github.com/repos/ceph/ceph/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ceph/ceph/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ceph/ceph/git/refs{/sha}","trees_url":"https://api.github.com/repos/ceph/ceph/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ceph/ceph/statuses/{sha}","languages_url":"https://api.github.com/repos/ceph/ceph/languages","stargazers_url":"https://api.github.com/repos/ceph/ceph/stargazers","contributors_url":"https://api.github.com/repos/ceph/ceph/contributors","subscribers_url":"https://api.github.com/repos/ceph/ceph/subscribers","subscription_url":"https://api.github.com/repos/ceph/ceph/subscription","commits_url":"https://api.github.com/repos/ceph/ceph/commits{/sha}","git_commits_url":"https://api.github.com/repos/ceph/ceph/git/commits{/sha}","comments_url":"https://api.github.com/repos/ceph/ceph/comments{/number}","issue_comment_url":"https://api.github.com/repos/ceph/ceph/issues/comments{/number}","contents_url":"https://api.github.com/repos/ceph/ceph/contents/{+path}","compare_url":"https://api.github.com/repos/ceph/ceph/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ceph/ceph/merges","archive_url":"https://api.github.com/repos/ceph/ceph/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ceph/ceph/downloads","issues_url":"https://api.github.com/repos/ceph/ceph/issues{/number}","pulls_url":"https://api.github.com/repos/ceph/ceph/pulls{/number}","milestones_url":"https://api.github.com/repos/ceph/ceph/milestones{/number}","notifications_url":"https://api.github.com/repos/ceph/ceph/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ceph/ceph/labels{/name}","releases_url":"https://api.github.com/repos/ceph/ceph/releases{/id}","created_at":"2011-09-01T21:41:26Z","updated_at":"2015-06-17T08:21:44Z","pushed_at":"2015-06-17T09:45:54Z","git_url":"git://github.com/ceph/ceph.git","ssh_url":"git@github.com:ceph/ceph.git","clone_url":"https://github.com/ceph/ceph.git","svn_url":"https://github.com/ceph/ceph","homepage":"http://ceph.com","size":436693,"stargazers_count":1376,"watchers_count":1376,"language":"C++","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":754,"mirror_url":null,"open_issues_count":163,"forks":754,"open_issues":163,"watchers":1376,"default_branch":"master"}},"base":{"label":"ceph:master","ref":"master","sha":"80e29f19f1f09e63ff9c2b4489dc6784647a0628","user":{"login":"ceph","id":1015767,"avatar_url":"https://avatars.githubusercontent.com/u/1015767?v=3","gravatar_id":"","url":"https://api.github.com/users/ceph","html_url":"https://github.com/ceph","followers_url":"https://api.github.com/users/ceph/followers","following_url":"https://api.github.com/users/ceph/following{/other_user}","gists_url":"https://api.github.com/users/ceph/gists{/gist_id}","starred_url":"https://api.github.com/users/ceph/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ceph/subscriptions","organizations_url":"https://api.github.com/users/ceph/orgs","repos_url":"https://api.github.com/users/ceph/repos","events_url":"https://api.github.com/users/ceph/events{/privacy}","received_events_url":"https://api.github.com/users/ceph/received_events","type":"Organization","site_admin":false},"repo":{"id":2310495,"name":"ceph","full_name":"ceph/ceph","owner":{"login":"ceph","id":1015767,"avatar_url":"https://avatars.githubusercontent.com/u/1015767?v=3","gravatar_id":"","url":"https://api.github.com/users/ceph","html_url":"https://github.com/ceph","followers_url":"https://api.github.com/users/ceph/followers","following_url":"https://api.github.com/users/ceph/following{/other_user}","gists_url":"https://api.github.com/users/ceph/gists{/gist_id}","starred_url":"https://api.github.com/users/ceph/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ceph/subscriptions","organizations_url":"https://api.github.com/users/ceph/orgs","repos_url":"https://api.github.com/users/ceph/repos","events_url":"https://api.github.com/users/ceph/events{/privacy}","received_events_url":"https://api.github.com/users/ceph/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/ceph/ceph","description":"Ceph is a distributed object, block, and file storage platform ","fork":false,"url":"https://api.github.com/repos/ceph/ceph","forks_url":"https://api.github.com/repos/ceph/ceph/forks","keys_url":"https://api.github.com/repos/ceph/ceph/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ceph/ceph/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ceph/ceph/teams","hooks_url":"https://api.github.com/repos/ceph/ceph/hooks","issue_events_url":"https://api.github.com/repos/ceph/ceph/issues/events{/number}","events_url":"https://api.github.com/repos/ceph/ceph/events","assignees_url":"https://api.github.com/repos/ceph/ceph/assignees{/user}","branches_url":"https://api.github.com/repos/ceph/ceph/branches{/branch}","tags_url":"https://api.github.com/repos/ceph/ceph/tags","blobs_url":"https://api.github.com/repos/ceph/ceph/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ceph/ceph/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ceph/ceph/git/refs{/sha}","trees_url":"https://api.github.com/repos/ceph/ceph/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ceph/ceph/statuses/{sha}","languages_url":"https://api.github.com/repos/ceph/ceph/languages","stargazers_url":"https://api.github.com/repos/ceph/ceph/stargazers","contributors_url":"https://api.github.com/repos/ceph/ceph/contributors","subscribers_url":"https://api.github.com/repos/ceph/ceph/subscribers","subscription_url":"https://api.github.com/repos/ceph/ceph/subscription","commits_url":"https://api.github.com/repos/ceph/ceph/commits{/sha}","git_commits_url":"https://api.github.com/repos/ceph/ceph/git/commits{/sha}","comments_url":"https://api.github.com/repos/ceph/ceph/comments{/number}","issue_comment_url":"https://api.github.com/repos/ceph/ceph/issues/comments{/number}","contents_url":"https://api.github.com/repos/ceph/ceph/contents/{+path}","compare_url":"https://api.github.com/repos/ceph/ceph/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ceph/ceph/merges","archive_url":"https://api.github.com/repos/ceph/ceph/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ceph/ceph/downloads","issues_url":"https://api.github.com/repos/ceph/ceph/issues{/number}","pulls_url":"https://api.github.com/repos/ceph/ceph/pulls{/number}","milestones_url":"https://api.github.com/repos/ceph/ceph/milestones{/number}","notifications_url":"https://api.github.com/repos/ceph/ceph/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ceph/ceph/labels{/name}","releases_url":"https://api.github.com/repos/ceph/ceph/releases{/id}","created_at":"2011-09-01T21:41:26Z","updated_at":"2015-06-17T08:21:44Z","pushed_at":"2015-06-17T09:45:54Z","git_url":"git://github.com/ceph/ceph.git","ssh_url":"git@github.com:ceph/ceph.git","clone_url":"https://github.com/ceph/ceph.git","svn_url":"https://github.com/ceph/ceph","homepage":"http://ceph.com","size":436693,"stargazers_count":1376,"watchers_count":1376,"language":"C++","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":754,"mirror_url":null,"open_issues_count":163,"forks":754,"open_issues":163,"watchers":1376,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/ceph/ceph/pulls/4939"},"html":{"href":"https://github.com/ceph/ceph/pull/4939"},"issue":{"href":"https://api.github.com/repos/ceph/ceph/issues/4939"},"comments":{"href":"https://api.github.com/repos/ceph/ceph/issues/4939/comments"},"review_comments":{"href":"https://api.github.com/repos/ceph/ceph/pulls/4939/comments"},"review_comment":{"href":"https://api.github.com/repos/ceph/ceph/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/ceph/ceph/pulls/4939/commits"},"statuses":{"href":"https://api.github.com/repos/ceph/ceph/statuses/cc6d9e058ede24110fac6c545e049d06c7f0fe46"}}}}