]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
librbd: remap resize target size if encryption is loaded
authorIlya Dryomov <idryomov@gmail.com>
Thu, 20 Oct 2022 15:25:46 +0000 (17:25 +0200)
committerIlya Dryomov <idryomov@gmail.com>
Sun, 4 Dec 2022 17:19:19 +0000 (18:19 +0100)
When encryption is loaded, rbd_get_size() and Image::size() return
"effective" size, but rbd_resize() and Image::resize() continue to take
raw size.  The user has to constantly keep these domains in mind.

Saying that resize must be done without encryption loaded is not an
answer because shrinking a clone that has snapshots involves copying up
objects in the affected range (identical to flattening).  In addition,
even if a clone doesn't have snapshots, shrinking it to a size that
isn't an object boundary is going to involve a copyup for the victim
object as well.

To avoid subtle data corruption on shrink, treat resize operation the
same as flatten operation (including on the CLI).

Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
doc/man/8/rbd.rst
src/librbd/Operations.cc
src/test/cli/rbd/help.t
src/test/librbd/test_librbd.cc
src/tools/rbd/action/Resize.cc

index 2427df67f66cbda2e4e65e33be16f49afb404eab..59e3462097530ea38c7fc967bd60b28e81ceb112 100644 (file)
@@ -606,7 +606,7 @@ Commands
   Initialize pool for use by RBD. Newly created pools must initialized
   prior to use.
 
-:command:`resize` (-s | --size *size-in-M/G/T*) [--allow-shrink] *image-spec*
+:command:`resize` (-s | --size *size-in-M/G/T*) [--allow-shrink] [--encryption-format *encryption-format* --encryption-passphrase-file *passphrase-file*]... *image-spec*
   Resize rbd image. The size parameter also needs to be specified.
   The --allow-shrink option lets the size be reduced.
   
index 30bb7efb3a6b4823625107b72768a81614a0f491..bab2ef4d18cbade74bcf8e1e5678fddc65e1a1e4 100644 (file)
@@ -17,6 +17,7 @@
 #include "librbd/Utils.h"
 #include "librbd/api/Config.h"
 #include "librbd/asio/ContextWQ.h"
+#include "librbd/io/Utils.h"
 #include "librbd/journal/DisabledPolicy.h"
 #include "librbd/journal/StandardPolicy.h"
 #include "librbd/operation/DisableFeaturesRequest.h"
@@ -742,9 +743,12 @@ int Operations<I>::resize(uint64_t size, bool allow_shrink, ProgressContext& pro
   CephContext *cct = m_image_ctx.cct;
 
   m_image_ctx.image_lock.lock_shared();
-  ldout(cct, 5) << this << " " << __func__ << ": "
-                << "size=" << m_image_ctx.size << ", "
-                << "new_size=" << size << dendl;
+  uint64_t raw_size = io::util::area_to_raw_offset(m_image_ctx, size,
+                                                   io::ImageArea::DATA);
+  ldout(cct, 5) << this << " " << __func__
+                << ": size=" << size
+                << " raw_size=" << m_image_ctx.size
+                << " new_raw_size=" << raw_size << dendl;
   m_image_ctx.image_lock.unlock_shared();
 
   int r = m_image_ctx.state->refresh_if_required();
@@ -753,7 +757,7 @@ int Operations<I>::resize(uint64_t size, bool allow_shrink, ProgressContext& pro
   }
 
   if (m_image_ctx.test_features(RBD_FEATURE_OBJECT_MAP) &&
-      !ObjectMap<>::is_compatible(m_image_ctx.layout, size)) {
+      !ObjectMap<>::is_compatible(m_image_ctx.layout, raw_size)) {
     lderr(cct) << "New size not compatible with object map" << dendl;
     return -EINVAL;
   }
@@ -783,9 +787,12 @@ void Operations<I>::execute_resize(uint64_t size, bool allow_shrink, ProgressCon
 
   CephContext *cct = m_image_ctx.cct;
   m_image_ctx.image_lock.lock_shared();
-  ldout(cct, 5) << this << " " << __func__ << ": "
-                << "size=" << m_image_ctx.size << ", "
-                << "new_size=" << size << dendl;
+  uint64_t raw_size = io::util::area_to_raw_offset(m_image_ctx, size,
+                                                   io::ImageArea::DATA);
+  ldout(cct, 5) << this << " " << __func__
+                << ": size=" << size
+                << " raw_size=" << m_image_ctx.size
+                << " new_raw_size=" << raw_size << dendl;
 
   if (m_image_ctx.snap_id != CEPH_NOSNAP || m_image_ctx.read_only ||
       m_image_ctx.operations_disabled) {
@@ -794,7 +801,7 @@ void Operations<I>::execute_resize(uint64_t size, bool allow_shrink, ProgressCon
     return;
   } else if (m_image_ctx.test_features(RBD_FEATURE_OBJECT_MAP,
                                        m_image_ctx.image_lock) &&
-             !ObjectMap<>::is_compatible(m_image_ctx.layout, size)) {
+             !ObjectMap<>::is_compatible(m_image_ctx.layout, raw_size)) {
     m_image_ctx.image_lock.unlock_shared();
     on_finish->complete(-EINVAL);
     return;
@@ -802,8 +809,8 @@ void Operations<I>::execute_resize(uint64_t size, bool allow_shrink, ProgressCon
   m_image_ctx.image_lock.unlock_shared();
 
   operation::ResizeRequest<I> *req = new operation::ResizeRequest<I>(
-    m_image_ctx, new C_NotifyUpdate<I>(m_image_ctx, on_finish), size, allow_shrink,
-    prog_ctx, journal_op_tid, false);
+      m_image_ctx, new C_NotifyUpdate<I>(m_image_ctx, on_finish), raw_size,
+      allow_shrink, prog_ctx, journal_op_tid, false);
   req->send();
 }
 
index 34f35aed2ffb6df1685b57be3f8567b74ec91a01..5c2a38031a5fb3093e45319b0d4dc1e4e560ade8 100644 (file)
   rbd help resize
   usage: rbd resize [--pool <pool>] [--namespace <namespace>] 
                     [--image <image>] --size <size> [--allow-shrink] 
-                    [--no-progress] 
+                    [--no-progress] [--encryption-format <encryption-format>] 
+                    [--encryption-passphrase-file <encryption-passphrase-file>] 
                     <image-spec> 
   
   Resize (expand or shrink) image.
   
   Positional arguments
-    <image-spec>         image specification
-                         (example: [<pool-name>/[<namespace>/]]<image-name>)
+    <image-spec>                     image specification
+                                     (example:
+                                     [<pool-name>/[<namespace>/]]<image-name>)
   
   Optional arguments
-    -p [ --pool ] arg    pool name
-    --namespace arg      namespace name
-    --image arg          image name
-    -s [ --size ] arg    image size (in M/G/T) [default: M]
-    --allow-shrink       permit shrinking
-    --no-progress        disable progress output
+    -p [ --pool ] arg                pool name
+    --namespace arg                  namespace name
+    --image arg                      image name
+    -s [ --size ] arg                image size (in M/G/T) [default: M]
+    --allow-shrink                   permit shrinking
+    --no-progress                    disable progress output
+    --encryption-format arg          encryption formats [possible values: luks]
+    --encryption-passphrase-file arg path to file containing passphrase for
+                                     unlocking the image
   
   rbd help snap create
   usage: rbd snap create [--pool <pool>] [--namespace <namespace>] 
index be9ffabc4c509b2dcc9b81c24c430f051f2ce75d..543a580568da6cb7eb4d396612b04811844906ad 100644 (file)
@@ -2705,6 +2705,105 @@ TEST_F(TestLibRBD, EncryptionLoadBadStripePattern)
   }
 }
 
+TEST_F(TestLibRBD, EncryptedResize)
+{
+  REQUIRE(!is_feature_enabled(RBD_FEATURE_JOURNALING));
+
+  librados::IoCtx ioctx;
+  ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+  librbd::RBD rbd;
+  auto name = get_temp_image_name();
+  uint64_t luks2_meta_size = 16 << 20;
+  uint64_t data_size = 10 << 20;
+  std::string passphrase = "some passphrase";
+
+  {
+    int order = 0;
+    ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(),
+                                 luks2_meta_size + data_size, &order));
+    librbd::Image image;
+    ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), nullptr));
+
+    uint64_t size;
+    ASSERT_EQ(0, image.size(&size));
+    ASSERT_EQ(luks2_meta_size + data_size, size);
+
+    librbd::encryption_luks2_format_options_t opts = {
+        RBD_ENCRYPTION_ALGORITHM_AES256, passphrase};
+    ASSERT_EQ(0, image.encryption_format(RBD_ENCRYPTION_FORMAT_LUKS2, &opts,
+                                         sizeof(opts)));
+    ASSERT_EQ(0, image.size(&size));
+    ASSERT_EQ(data_size, size);
+    ASSERT_EQ(0, image.resize(data_size * 3));
+    ASSERT_EQ(0, image.size(&size));
+    ASSERT_EQ(data_size * 3, size);
+  }
+
+  {
+    librbd::Image image;
+    ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), nullptr));
+
+    uint64_t size;
+    ASSERT_EQ(0, image.size(&size));
+    ASSERT_EQ(luks2_meta_size + data_size * 3, size);
+
+    librbd::encryption_luks_format_options_t opts = {passphrase};
+    ASSERT_EQ(0, image.encryption_load(RBD_ENCRYPTION_FORMAT_LUKS, &opts,
+                                       sizeof(opts)));
+    ASSERT_EQ(0, image.size(&size));
+    ASSERT_EQ(data_size * 3, size);
+    ASSERT_EQ(0, image.resize(data_size / 2));
+    ASSERT_EQ(0, image.size(&size));
+    ASSERT_EQ(data_size / 2, size);
+  }
+
+  {
+    librbd::Image image;
+    ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), nullptr));
+
+    uint64_t size;
+    ASSERT_EQ(0, image.size(&size));
+    ASSERT_EQ(luks2_meta_size + data_size / 2, size);
+
+    librbd::encryption_luks_format_options_t opts = {passphrase};
+    ASSERT_EQ(0, image.encryption_load(RBD_ENCRYPTION_FORMAT_LUKS, &opts,
+                                       sizeof(opts)));
+    ASSERT_EQ(0, image.size(&size));
+    ASSERT_EQ(data_size / 2, size);
+    ASSERT_EQ(0, image.resize(0));
+    ASSERT_EQ(0, image.size(&size));
+    ASSERT_EQ(0, size);
+  }
+
+  {
+    librbd::Image image;
+    ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), nullptr));
+
+    uint64_t size;
+    ASSERT_EQ(0, image.size(&size));
+    ASSERT_EQ(luks2_meta_size, size);
+
+    librbd::encryption_luks_format_options_t opts = {passphrase};
+    ASSERT_EQ(0, image.encryption_load(RBD_ENCRYPTION_FORMAT_LUKS, &opts,
+                                       sizeof(opts)));
+    ASSERT_EQ(0, image.size(&size));
+    ASSERT_EQ(0, size);
+    ASSERT_EQ(0, image.resize(data_size));
+    ASSERT_EQ(0, image.size(&size));
+    ASSERT_EQ(data_size, size);
+  }
+
+  {
+    librbd::Image image;
+    ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), nullptr));
+
+    uint64_t size;
+    ASSERT_EQ(0, image.size(&size));
+    ASSERT_EQ(luks2_meta_size + data_size, size);
+  }
+}
+
 #endif
 
 TEST_F(TestLibRBD, TestIOWithIOHint)
index 60c16429b79e33924c774eb9fea700a346c560d7..79fbbd127d7a2243a51117762b0b6ea42b806654 100644 (file)
@@ -34,6 +34,7 @@ void get_arguments(po::options_description *positional,
   options->add_options()
     ("allow-shrink", po::bool_switch(), "permit shrinking");
   at::add_no_progress_option(options);
+  at::add_encryption_options(options);
 }
 
 int execute(const po::variables_map &vm,
@@ -57,6 +58,12 @@ int execute(const po::variables_map &vm,
     return r;
   }
 
+  utils::EncryptionOptions encryption_options;
+  r = utils::get_encryption_options(vm, &encryption_options);
+  if (r < 0) {
+    return r;
+  }
+
   librados::Rados rados;
   librados::IoCtx io_ctx;
   librbd::Image image;
@@ -66,6 +73,16 @@ int execute(const po::variables_map &vm,
     return r;
   }
 
+  if (!encryption_options.specs.empty()) {
+    r = image.encryption_load2(encryption_options.specs.data(),
+                               encryption_options.specs.size());
+    if (r < 0) {
+      std::cerr << "rbd: encryption load failed: " << cpp_strerror(r)
+                << std::endl;
+      return r;
+    }
+  }
+
   librbd::image_info_t info;
   r = image.stat(info, sizeof(info));
   if (r < 0) {