From eef90f2951ed757704f168c2bb15389cf556d459 Mon Sep 17 00:00:00 2001
From: Vitaliy Filippov <vitalif@yourcmc.ru>
Date: Fri, 19 Oct 2018 20:36:27 +0300
Subject: [PATCH] Support include-parents in rbd diff and export-diff commands

---
 src/include/rbd/librbd.h       |  7 +++++++
 src/include/rbd/librbd.hpp     |  5 +++++
 src/librbd/api/DiffIterate.cc  |  8 ++++----
 src/librbd/api/DiffIterate.h   |  8 ++++----
 src/librbd/librbd.cc           | 44 ++++++++++++++++++++++++++++++++++++++----
 src/tools/rbd/ArgumentTypes.h  |  1 +
 src/tools/rbd/action/Diff.cc   | 13 ++++++++++---
 src/tools/rbd/action/Export.cc | 36 ++++++++++++++++++++++++----------
 src/tools/rbd/action/Import.cc |  2 +-
 9 files changed, 98 insertions(+), 26 deletions(-)

diff --git a/src/include/rbd/librbd.h b/src/include/rbd/librbd.h
index 90883b6..0a69402 100644
--- a/src/include/rbd/librbd.h
+++ b/src/include/rbd/librbd.h
@@ -745,6 +745,7 @@ CEPH_RBD_API int rbd_read_iterate2(rbd_image_t image, uint64_t ofs, uint64_t len
  * @param ofs start offset
  * @param len len in bytes of region to report on
  * @param include_parent 1 if full history diff should include parent
+ * @param include_parents how much parents should history diff include
  * @param whole_object 1 if diff extents should cover whole object
  * @param cb callback to call for each allocated region
  * @param arg argument to pass to the callback
@@ -761,6 +762,12 @@ CEPH_RBD_API int rbd_diff_iterate2(rbd_image_t image,
                                    uint8_t include_parent, uint8_t whole_object,
 		                   int (*cb)(uint64_t, size_t, int, void *),
                                    void *arg);
+CEPH_RBD_API int rbd_diff_iterate3(rbd_image_t image,
+		                   const char *fromsnapname,
+		                   uint64_t ofs, uint64_t len,
+                                   uint64_t include_parents, uint8_t whole_object,
+		                   int (*cb)(uint64_t, size_t, int, void *),
+                                   void *arg);
 CEPH_RBD_API ssize_t rbd_write(rbd_image_t image, uint64_t ofs, size_t len,
                                const char *buf);
 /*
diff --git a/src/include/rbd/librbd.hpp b/src/include/rbd/librbd.hpp
index 4e034f4..7c99724 100644
--- a/src/include/rbd/librbd.hpp
+++ b/src/include/rbd/librbd.hpp
@@ -418,6 +418,7 @@ public:
    * @param ofs start offset
    * @param len len in bytes of region to report on
    * @param include_parent true if full history diff should include parent
+   * @param include_parents how much parents should history diff include
    * @param whole_object 1 if diff extents should cover whole object
    * @param cb callback to call for each allocated region
    * @param arg argument to pass to the callback
@@ -430,6 +431,10 @@ public:
 		    uint64_t ofs, uint64_t len,
                     bool include_parent, bool whole_object,
 		    int (*cb)(uint64_t, size_t, int, void *), void *arg);
+  int diff_iterate3(const char *fromsnapname,
+		    uint64_t ofs, uint64_t len,
+                    uint64_t include_parents, bool whole_object,
+		    int (*cb)(uint64_t, size_t, int, void *), void *arg);
 
   ssize_t write(uint64_t ofs, size_t len, ceph::bufferlist& bl);
   /* @param op_flags see librados.h constants beginning with LIBRADOS_OP_FLAG */
diff --git a/src/librbd/api/DiffIterate.cc b/src/librbd/api/DiffIterate.cc
index 94e3471..e279448 100644
--- a/src/librbd/api/DiffIterate.cc
+++ b/src/librbd/api/DiffIterate.cc
@@ -231,7 +231,7 @@ int DiffIterate<I>::diff_iterate(I *ictx,
 				 const cls::rbd::SnapshotNamespace& from_snap_namespace,
 				 const char *fromsnapname,
                                  uint64_t off, uint64_t len,
-                                 bool include_parent, bool whole_object,
+                                 uint64_t include_parents, bool whole_object,
                                  int (*cb)(uint64_t, size_t, int, void *),
                                  void *arg)
 {
@@ -267,7 +267,7 @@ int DiffIterate<I>::diff_iterate(I *ictx,
   }
 
   DiffIterate command(*ictx, from_snap_namespace, fromsnapname, off, len,
-		      include_parent, whole_object, cb, arg);
+		      include_parents, whole_object, cb, arg);
   r = command.execute();
   return r;
 }
@@ -330,7 +330,7 @@ int DiffIterate<I>::execute() {
   // check parent overlap only if we are comparing to the beginning of time
   DiffContext diff_context(m_image_ctx, m_callback, m_callback_arg,
                            m_whole_object, from_snap_id, end_snap_id);
-  if (m_include_parent && from_snap_id == 0) {
+  if (m_include_parents > 0 && from_snap_id == 0) {
     RWLock::RLocker l(m_image_ctx.snap_lock);
     RWLock::RLocker l2(m_image_ctx.parent_lock);
     uint64_t overlap = 0;
@@ -340,7 +340,7 @@ int DiffIterate<I>::execute() {
       ldout(cct, 10) << " first getting parent diff" << dendl;
       DiffIterate diff_parent(*m_image_ctx.parent, {},
 			      nullptr, 0, overlap,
-                              m_include_parent, m_whole_object,
+                              m_include_parents-1, m_whole_object,
                               &simple_diff_cb,
                               &diff_context.parent_diff);
       r = diff_parent.execute();
diff --git a/src/librbd/api/DiffIterate.h b/src/librbd/api/DiffIterate.h
index e6074d9..fef329e 100644
--- a/src/librbd/api/DiffIterate.h
+++ b/src/librbd/api/DiffIterate.h
@@ -22,7 +22,7 @@ public:
   static int diff_iterate(ImageCtxT *ictx,
 			  const cls::rbd::SnapshotNamespace& from_snap_namespace,
 			  const char *fromsnapname,
-                          uint64_t off, uint64_t len, bool include_parent,
+                          uint64_t off, uint64_t len, uint64_t include_parents,
                           bool whole_object,
 		          int (*cb)(uint64_t, size_t, int, void *),
 		          void *arg);
@@ -33,7 +33,7 @@ private:
   const char* m_from_snap_name;
   uint64_t m_offset;
   uint64_t m_length;
-  bool m_include_parent;
+  uint64_t m_include_parents;
   bool m_whole_object;
   Callback m_callback;
   void *m_callback_arg;
@@ -41,11 +41,11 @@ private:
   DiffIterate(ImageCtxT &image_ctx, 
 	      const cls::rbd::SnapshotNamespace& from_snap_namespace,
 	      const char *from_snap_name, uint64_t off, uint64_t len,
-	      bool include_parent, bool whole_object, Callback callback,
+	      uint64_t include_parents, bool whole_object, Callback callback,
 	      void *callback_arg)
     : m_image_ctx(image_ctx), m_from_snap_namespace(from_snap_namespace),
       m_from_snap_name(from_snap_name), m_offset(off),
-      m_length(len), m_include_parent(include_parent),
+      m_length(len), m_include_parents(include_parents),
       m_whole_object(whole_object), m_callback(callback),
       m_callback_arg(callback_arg)
   {
diff --git a/src/librbd/librbd.cc b/src/librbd/librbd.cc
index 5593415..b06fdb6 100644
--- a/src/librbd/librbd.cc
+++ b/src/librbd/librbd.cc
@@ -1765,11 +1765,28 @@ namespace librbd {
     ImageCtx *ictx = (ImageCtx *)ctx;
     tracepoint(librbd, diff_iterate_enter, ictx, ictx->name.c_str(),
               ictx->snap_name.c_str(), ictx->read_only, fromsnapname, ofs, len,
-              include_parent, whole_object);
+              include_parent ? 1 : 0, whole_object);
     int r = librbd::api::DiffIterate<>::diff_iterate(ictx,
 						     cls::rbd::UserSnapshotNamespace(),
 						     fromsnapname, ofs,
-                                                     len, include_parent,
+                                                     len, include_parent ? 1 : 0,
+                                                     whole_object, cb, arg);
+    tracepoint(librbd, diff_iterate_exit, r);
+    return r;
+  }
+
+  int Image::diff_iterate3(const char *fromsnapname, uint64_t ofs, uint64_t len,
+                           uint64_t include_parents, bool whole_object,
+                           int (*cb)(uint64_t, size_t, int, void *), void *arg)
+  {
+    ImageCtx *ictx = (ImageCtx *)ctx;
+    tracepoint(librbd, diff_iterate_enter, ictx, ictx->name.c_str(),
+              ictx->snap_name.c_str(), ictx->read_only, fromsnapname, ofs, len,
+              include_parents, whole_object);
+    int r = librbd::api::DiffIterate<>::diff_iterate(ictx,
+						     cls::rbd::UserSnapshotNamespace(),
+						     fromsnapname, ofs,
+                                                     len, include_parents,
                                                      whole_object, cb, arg);
     tracepoint(librbd, diff_iterate_exit, r);
     return r;
@@ -3926,11 +3943,30 @@ extern "C" int rbd_diff_iterate2(rbd_image_t image, const char *fromsnapname,
   librbd::ImageCtx *ictx = (librbd::ImageCtx *)image;
   tracepoint(librbd, diff_iterate_enter, ictx, ictx->name.c_str(),
             ictx->snap_name.c_str(), ictx->read_only, fromsnapname, ofs, len,
-            include_parent != 0, whole_object != 0);
+            include_parent != 0 ? 1 : 0, whole_object != 0);
+  int r = librbd::api::DiffIterate<>::diff_iterate(ictx,
+						   cls::rbd::UserSnapshotNamespace(),
+						   fromsnapname, ofs, len,
+                                                   include_parent != 0 ? 1 : 0, whole_object,
+                                                   cb, arg);
+  tracepoint(librbd, diff_iterate_exit, r);
+  return r;
+}
+
+extern "C" int rbd_diff_iterate3(rbd_image_t image, const char *fromsnapname,
+                                uint64_t ofs, uint64_t len,
+                                uint64_t include_parents, uint8_t whole_object,
+                                int (*cb)(uint64_t, size_t, int, void *),
+                                void *arg)
+{
+  librbd::ImageCtx *ictx = (librbd::ImageCtx *)image;
+  tracepoint(librbd, diff_iterate_enter, ictx, ictx->name.c_str(),
+            ictx->snap_name.c_str(), ictx->read_only, fromsnapname, ofs, len,
+            include_parents, whole_object != 0);
   int r = librbd::api::DiffIterate<>::diff_iterate(ictx,
 						   cls::rbd::UserSnapshotNamespace(),
 						   fromsnapname, ofs, len,
-                                                   include_parent, whole_object,
+                                                   include_parents, whole_object,
                                                    cb, arg);
   tracepoint(librbd, diff_iterate_exit, r);
   return r;
diff --git a/src/tools/rbd/ArgumentTypes.h b/src/tools/rbd/ArgumentTypes.h
index 887ae78..5adc0bd 100644
--- a/src/tools/rbd/ArgumentTypes.h
+++ b/src/tools/rbd/ArgumentTypes.h
@@ -58,6 +58,7 @@ static const std::string DEST_SNAPSHOT_NAME("dest-snap");
 static const std::string JOURNAL_NAME("journal");
 static const std::string DEST_JOURNAL_NAME("dest-journal");
 static const std::string PATH("path");
+static const std::string INCLUDE_PARENTS("include-parents");
 static const std::string FROM_SNAPSHOT_NAME("from-snap");
 static const std::string WHOLE_OBJECT("whole-object");
 
diff --git a/src/tools/rbd/action/Diff.cc b/src/tools/rbd/action/Diff.cc
index 212a3de..a510636 100644
--- a/src/tools/rbd/action/Diff.cc
+++ b/src/tools/rbd/action/Diff.cc
@@ -41,7 +41,7 @@ static int diff_cb(uint64_t ofs, size_t len, int exists, void *arg)
   return 0;
 }
 
-static int do_diff(librbd::Image& image, const char *fromsnapname,
+static int do_diff(librbd::Image& image, const char *fromsnapname, uint64_t include_parents,
                    bool whole_object, Formatter *f)
 {
   int r;
@@ -62,7 +62,7 @@ static int do_diff(librbd::Image& image, const char *fromsnapname,
     om.t->define_column("Type", TextTable::LEFT, TextTable::LEFT);
   }
 
-  r = image.diff_iterate2(fromsnapname, 0, info.size, true, whole_object,
+  r = image.diff_iterate3(fromsnapname, 0, info.size, include_parents, whole_object,
                           diff_cb, &om);
   if (f) {
     f->close_section();
@@ -82,6 +82,8 @@ void get_arguments(po::options_description *positional,
   options->add_options()
     (at::FROM_SNAPSHOT_NAME.c_str(), po::value<std::string>(),
      "snapshot starting point")
+    (at::INCLUDE_PARENTS.c_str(), po::value<uint64_t>(),
+      "maximum number of parents to include in diff")
     (at::WHOLE_OBJECT.c_str(), po::bool_switch(), "compare whole object");
   at::add_format_options(options);
 }
@@ -107,6 +109,11 @@ int execute(const po::variables_map &vm,
 
   bool diff_whole_object = vm[at::WHOLE_OBJECT].as<bool>();
 
+  uint64_t include_parents = 0;
+  if (vm.count(at::INCLUDE_PARENTS)) {
+    include_parents = vm[at::INCLUDE_PARENTS].as<uint64_t>();
+  }
+
   at::Format::Formatter formatter;
   r = utils::get_formatter(vm, &formatter);
   if (r < 0) {
@@ -123,7 +130,7 @@ int execute(const po::variables_map &vm,
   }
 
   r = do_diff(image, from_snap_name.empty() ? nullptr : from_snap_name.c_str(),
-              diff_whole_object, formatter.get());
+              include_parents, diff_whole_object, formatter.get());
   if (r < 0) {
     std::cerr << "rbd: diff error: " << cpp_strerror(r) << std::endl;
     return -r;
diff --git a/src/tools/rbd/action/Export.cc b/src/tools/rbd/action/Export.cc
index 8c109e1..37c1ef9 100644
--- a/src/tools/rbd/action/Export.cc
+++ b/src/tools/rbd/action/Export.cc
@@ -120,7 +120,7 @@ private:
 
 
 int do_export_diff_fd(librbd::Image& image, const char *fromsnapname,
-		   const char *endsnapname, bool whole_object,
+		   const char *endsnapname, uint64_t include_parents, bool whole_object,
 		   int fd, bool no_progress, int export_format)
 {
   int r;
@@ -192,7 +192,7 @@ int do_export_diff_fd(librbd::Image& image, const char *fromsnapname,
   ExportDiffContext edc(&image, fd, info.size,
                         g_conf->get_val<int64_t>("rbd_concurrent_management_ops"),
                         no_progress, export_format);
-  r = image.diff_iterate2(fromsnapname, 0, info.size, true, whole_object,
+  r = image.diff_iterate3(fromsnapname, 0, info.size, include_parents, whole_object,
                           &C_ExportDiff::export_diff_cb, (void *)&edc);
   if (r < 0) {
     goto out;
@@ -220,7 +220,7 @@ out:
 }
 
 int do_export_diff(librbd::Image& image, const char *fromsnapname,
-                const char *endsnapname, bool whole_object,
+                const char *endsnapname, uint64_t include_parents, bool whole_object,
                 const char *path, bool no_progress)
 {
   int r;
@@ -233,7 +233,7 @@ int do_export_diff(librbd::Image& image, const char *fromsnapname,
   if (fd < 0)
     return -errno;
 
-  r = do_export_diff_fd(image, fromsnapname, endsnapname, whole_object, fd, no_progress, 1);
+  r = do_export_diff_fd(image, fromsnapname, endsnapname, include_parents, whole_object, fd, no_progress, 1);
 
   if (fd != 1)
     close(fd);
@@ -257,6 +257,8 @@ void get_arguments_diff(po::options_description *positional,
   options->add_options()
     (at::FROM_SNAPSHOT_NAME.c_str(), po::value<std::string>(),
      "snapshot starting point")
+    (at::INCLUDE_PARENTS.c_str(), po::value<uint64_t>(),
+      "maximum number of parents to include in diff")
     (at::WHOLE_OBJECT.c_str(), po::bool_switch(), "compare whole object");
   at::add_no_progress_option(options);
 }
@@ -295,9 +297,15 @@ int execute_diff(const po::variables_map &vm,
     return r;
   }
 
+  uint64_t include_parents = 0;
+  if (vm.count(at::INCLUDE_PARENTS)) {
+    include_parents = vm[at::INCLUDE_PARENTS].as<uint64_t>();
+  }
+
   r = do_export_diff(image,
                      from_snap_name.empty() ? nullptr : from_snap_name.c_str(),
                      snap_name.empty() ? nullptr : snap_name.c_str(),
+                     include_parents,
                      vm[at::WHOLE_OBJECT].as<bool>(), path.c_str(),
                      vm[at::NO_PROGRESS].as<bool>());
   if (r < 0) {
@@ -388,7 +396,7 @@ private:
 const uint32_t MAX_KEYS = 64;
 
 static int do_export_v2(librbd::Image& image, librbd::image_info_t &info, int fd,
-		        uint64_t period, int max_concurrent_ops, utils::ProgressContext &pc)
+		        uint64_t include_parents, uint64_t period, int max_concurrent_ops, utils::ProgressContext &pc)
 {
   int r = 0;
   // header
@@ -500,7 +508,7 @@ static int do_export_v2(librbd::Image& image, librbd::image_info_t &info, int fd
   const char *last_snap = NULL;
   for (size_t i = 0; i < snaps.size(); ++i) {
     utils::snap_set(image, snaps[i].name.c_str());
-    r = do_export_diff_fd(image, last_snap, snaps[i].name.c_str(), false, fd, true, 2);
+    r = do_export_diff_fd(image, last_snap, snaps[i].name.c_str(), i == 0 ? include_parents : 0, false, fd, true, 2);
     if (r < 0) {
       return r;
     }
@@ -508,7 +516,7 @@ static int do_export_v2(librbd::Image& image, librbd::image_info_t &info, int fd
     last_snap = snaps[i].name.c_str();
   }
   utils::snap_set(image, std::string(""));
-  r = do_export_diff_fd(image, last_snap, nullptr, false, fd, true, 2);
+  r = do_export_diff_fd(image, last_snap, nullptr, snaps.size() > 0 ? 0 : include_parents, false, fd, true, 2);
   if (r < 0) {
     return r;
   }
@@ -550,7 +558,7 @@ static int do_export_v1(librbd::Image& image, librbd::image_info_t &info, int fd
   return r;
 }
 
-static int do_export(librbd::Image& image, const char *path, bool no_progress, int export_format)
+static int do_export(librbd::Image& image, const char *path, uint64_t include_parents, bool no_progress, int export_format)
 {
   librbd::image_info_t info;
   int64_t r = image.stat(info, sizeof(info));
@@ -580,7 +588,7 @@ static int do_export(librbd::Image& image, const char *path, bool no_progress, i
   if (export_format == 1)
     r = do_export_v1(image, info, fd, period, max_concurrent_ops, pc);
   else
-    r = do_export_v2(image, info, fd, period, max_concurrent_ops, pc);
+    r = do_export_v2(image, info, fd, include_parents, period, max_concurrent_ops, pc);
 
   if (r < 0)
     pc.fail();
@@ -597,6 +605,9 @@ void get_arguments(po::options_description *positional,
                                      at::ARGUMENT_MODIFIER_SOURCE);
   at::add_path_options(positional, options,
                        "export file (or '-' for stdout)");
+  options->add_options()
+    (at::INCLUDE_PARENTS.c_str(), po::value<uint64_t>(),
+      "maximum number of parents to include in diff");
   at::add_no_progress_option(options);
   at::add_export_format_option(options);
 }
@@ -621,6 +632,11 @@ int execute(const po::variables_map &vm,
     return r;
   }
 
+  uint64_t include_parents = 0;
+  if (vm.count(at::INCLUDE_PARENTS)) {
+    include_parents = vm[at::INCLUDE_PARENTS].as<uint64_t>();
+  }
+
   librados::Rados rados;
   librados::IoCtx io_ctx;
   librbd::Image image;
@@ -634,7 +650,7 @@ int execute(const po::variables_map &vm,
   if (vm.count("export-format"))
     format = vm["export-format"].as<uint64_t>();
 
-  r = do_export(image, path.c_str(), vm[at::NO_PROGRESS].as<bool>(), format);
+  r = do_export(image, path.c_str(), include_parents, vm[at::NO_PROGRESS].as<bool>(), format);
   if (r < 0) {
     std::cerr << "rbd: export error: " << cpp_strerror(r) << std::endl;
     return r;
diff --git a/src/tools/rbd/action/Import.cc b/src/tools/rbd/action/Import.cc
index a90913b..b75af3c 100644
--- a/src/tools/rbd/action/Import.cc
+++ b/src/tools/rbd/action/Import.cc
@@ -892,7 +892,7 @@ static int do_import(librados::Rados &rados, librbd::RBD &rbd,
   }
 
   r = rbd.create4(io_ctx, imgname, size, opts);
-  if (r < 0) {
+  if (r < 0 && r != -EEXIST) {
     std::cerr << "rbd: image creation failed" << std::endl;
     goto done;
   }
-- 
1.8.3.1

