]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
rgw: implement CopyObject for encrypted object
authorSeena Fallah <seenafallah@gmail.com>
Sat, 7 Jun 2025 20:15:55 +0000 (22:15 +0200)
committerSeena Fallah <seenafallah@gmail.com>
Wed, 19 Nov 2025 19:00:46 +0000 (20:00 +0100)
Pass RGWCopyObjDPF to copy_object() for RGWCopyObj so it can decrypt
and/or encrypt the object on copy if needed.

Co-authored-by: Marcus Watts <mwatts@redhat.com>
Signed-off-by: Seena Fallah <seenafallah@gmail.com>
src/rgw/driver/rados/rgw_rados.cc
src/rgw/driver/rados/rgw_rados.h
src/rgw/driver/rados/rgw_sal_rados.cc
src/rgw/rgw_op.cc
src/rgw/rgw_op.h
src/rgw/rgw_rest_s3.cc
src/test/rgw/test_rgw_posix_driver.cc

index 2a1b5d541ea9df288a06e70c64bb0bc370735be3..54ab9fa0e9195186c8f654e6bf0dcab9c6d8be15 100644 (file)
@@ -3087,6 +3087,7 @@ int RGWRados::swift_versioning_copy(RGWObjectCtx& obj_ctx,
                NULL, /* string *petag */
                NULL, /* void (*progress_cb)(off_t, void *) */
                NULL, /* void *progress_data */
+               NULL, /* rgw::sal::DataProcessorFactory *dp_factory */
                dpp,
                y,
                no_trace);
@@ -3187,6 +3188,7 @@ int RGWRados::swift_versioning_restore(RGWObjectCtx& obj_ctx,
                        nullptr,       /* string *petag */
                        nullptr,       /* void (*progress_cb)(off_t, void *) */
                        nullptr,       /* void *progress_data */
+                       nullptr,       /* rgw::sal::DataProcessorFactory *dp_factory */
                        dpp,
                        y,
                        no_trace);
@@ -3897,7 +3899,7 @@ int RGWRados::rewrite_obj(RGWBucketInfo& dest_bucket_info, const rgw_obj& obj, c
   return copy_obj_data(octx, owner, dest_bucket_info,
                        dest_bucket_info.placement_rule,
                        read_op, obj_size - 1, obj, NULL, mtime,
-                       attrset, 0, real_time(), NULL, dpp, y);
+                       attrset, 0, real_time(), NULL, NULL, dpp, y);
 }
 
 
@@ -4959,6 +4961,7 @@ int RGWRados::copy_obj(RGWObjectCtx& src_obj_ctx,
                string *petag,
                void (*progress_cb)(off_t, void *),
                void *progress_data,
+               rgw::sal::DataProcessorFactory *dp_factory,
                const DoutPrefixProvider *dpp,
                optional_yield y,
                jspan_context& trace)
@@ -5034,15 +5037,6 @@ int RGWRados::copy_obj(RGWObjectCtx& src_obj_ctx,
   if (ret < 0) {
     return ret;
   }
-  if (src_attrs.count(RGW_ATTR_CRYPT_MODE)) {
-    // Current implementation does not follow S3 spec and even
-    // may result in data corruption silently when copying
-    // multipart objects across pools. So reject COPY operations
-    //on encrypted objects before it is fully functional.
-    ldpp_dout(dpp, 0) << "ERROR: copy op for encrypted object " << src_obj
-                  << " has not been implemented." << dendl;
-    return -ERR_NOT_IMPLEMENTED;
-  }
 
   src_attrs[RGW_ATTR_ACL] = attrs[RGW_ATTR_ACL];
   src_attrs.erase(RGW_ATTR_DELETE_AT);
@@ -5062,10 +5056,21 @@ int RGWRados::copy_obj(RGWObjectCtx& src_obj_ctx,
     src_attrs.erase(RGW_ATTR_OLH_VER);
   }
 
+  src_attrs.erase(RGW_ATTR_STORAGE_CLASS);
+
   src_attrs.erase(RGW_ATTR_OBJ_REPLICATION_TRACE);
   src_attrs.erase(RGW_ATTR_OBJ_REPLICATION_TIMESTAMP);
   src_attrs.erase(RGW_ATTR_OBJ_REPLICATION_STATUS);
 
+  // drop encryption attributes
+  // will be generated by copy_obj_data() if encryption is requested
+  src_attrs.erase(RGW_ATTR_CRYPT_KEYSEL);
+  src_attrs.erase(RGW_ATTR_CRYPT_CONTEXT);
+  src_attrs.erase(RGW_ATTR_CRYPT_MODE);
+  src_attrs.erase(RGW_ATTR_CRYPT_KEYID);
+  src_attrs.erase(RGW_ATTR_CRYPT_KEYMD5);
+  src_attrs.erase(RGW_ATTR_CRYPT_DATAKEY);
+
   set_copy_attrs(src_attrs, attrs, attrs_mod);
   attrs.erase(RGW_ATTR_ID_TAG);
   attrs.erase(RGW_ATTR_PG_VER);
@@ -5147,6 +5152,10 @@ int RGWRados::copy_obj(RGWObjectCtx& src_obj_ctx,
     }
   }
 
+  if (dp_factory && dp_factory->need_copy_data()) {
+    copy_data = true;
+  }
+
   if (petag) {
     const auto iter = attrs.find(RGW_ATTR_ETAG);
     if (iter != attrs.end()) {
@@ -5157,7 +5166,7 @@ int RGWRados::copy_obj(RGWObjectCtx& src_obj_ctx,
   if (copy_data) { /* refcounting tail wouldn't work here, just copy the data */
     attrs.erase(RGW_ATTR_TAIL_TAG);
     return copy_obj_data(dest_obj_ctx, owner, dest_bucket_info, dest_placement, read_op, obj_size - 1, dest_obj,
-                         mtime, real_time(), attrs, olh_epoch, delete_at, petag, dpp, y);
+                         mtime, real_time(), attrs, olh_epoch, delete_at, petag, dp_factory, dpp, y);
   }
 
   /* This has been in for 2 years, so we can safely assume amanifest is not NULL */
@@ -5325,6 +5334,7 @@ int RGWRados::copy_obj_data(RGWObjectCtx& obj_ctx,
                uint64_t olh_epoch,
               real_time delete_at,
                string *petag,
+               rgw::sal::DataProcessorFactory *dp_factory,
                const DoutPrefixProvider *dpp,
                optional_yield y,
                bool log_op)
@@ -5335,34 +5345,37 @@ int RGWRados::copy_obj_data(RGWObjectCtx& obj_ctx,
   auto aio = rgw::make_throttle(cct->_conf->rgw_put_obj_min_window_size, y);
   using namespace rgw::putobj;
   jspan_context no_trace{false, false};
-  AtomicObjectProcessor processor(aio.get(), this, dest_bucket_info,
-                                  &dest_placement, owner,
-                                  obj_ctx, dest_obj, olh_epoch, tag, dpp, y, no_trace);
-  int ret = processor.prepare(y);
+  AtomicObjectProcessor aoproc(aio.get(), this, dest_bucket_info,
+                               &dest_placement, owner,
+                               obj_ctx, dest_obj, olh_epoch, tag, dpp, y, no_trace);
+
+  DataProcessorFilter aoproc_filter(&aoproc);
+  RGWGetObj_Filter *filter = &aoproc_filter;
+  if (dp_factory) {
+    int ret = dp_factory->set_writer(&aoproc, attrs, dpp, y);
+    if (ret < 0) {
+      return ret;
+    }
+
+    filter = dp_factory->get_filter();
+  }
+
+  int ret = aoproc.prepare(y);
   if (ret < 0)
     return ret;
 
   off_t ofs = 0;
 
-  do {
-    bufferlist bl;
-    ret = read_op.read(ofs, end, bl, y, dpp);
-    if (ret < 0) {
-      ldpp_dout(dpp, 0) << "ERROR: fail to read object data, ret = " << ret << dendl;
-      return ret;
-    }
-
-    uint64_t read_len = ret;
-    ret = processor.process(std::move(bl), ofs);
-    if (ret < 0) {
-      return ret;
-    }
+  ret = read_op.iterate(dpp, ofs, end, filter, y);
+  if (ret < 0) {
+    ldpp_dout(dpp, 0) << "ERROR: failed to read object data ret=" << ret << dendl;
+    return ret;
+  }
 
-    ofs += read_len;
-  } while (ofs <= end);
+  ofs = end;
 
   // flush
-  ret = processor.process({}, ofs);
+  ret = filter->flush();
   if (ret < 0) {
     return ret;
   }
@@ -5391,7 +5404,7 @@ int RGWRados::copy_obj_data(RGWObjectCtx& obj_ctx,
   }
 
   const req_context rctx{dpp, y, nullptr};
-  return processor.complete(accounted_size, etag, mtime, set_mtime, attrs,
+  return aoproc.complete(accounted_size, etag, mtime, set_mtime, attrs,
                            rgw::cksum::no_cksum, delete_at,
                             nullptr, nullptr, nullptr, nullptr, nullptr, rctx,
                             log_op ? rgw::sal::FLAG_LOG_OP : 0);
@@ -5456,6 +5469,7 @@ int RGWRados::transition_obj(RGWObjectCtx& obj_ctx,
                       olh_epoch,
                       real_time(),
                       nullptr /* petag */,
+                      nullptr, /* dp_factory */
                       dpp,
                       y,
                       log_op);
index 38e2f3dcb7da18cbbcb40692a22a799871644822..5340bb1ce20998bc412635c7cdaa120b53df567e 100644 (file)
@@ -1235,6 +1235,7 @@ public:
                std::string *petag,
                void (*progress_cb)(off_t, void *),
                void *progress_data,
+               rgw::sal::DataProcessorFactory *dp_factory,
                const DoutPrefixProvider *dpp,
                optional_yield y,
                jspan_context& trace);
@@ -1251,6 +1252,7 @@ public:
                uint64_t olh_epoch,
               ceph::real_time delete_at,
                std::string *petag,
+               rgw::sal::DataProcessorFactory *dp_factory,
                const DoutPrefixProvider *dpp,
                optional_yield y,
                bool log_op = true);
index 90813103ef863788efc9eabea011492e321a3192..bb6689d6fc4ee4daad256f51d81c0fde88569db8 100644 (file)
@@ -3736,6 +3736,7 @@ int RadosObject::copy_object(const ACLOwner& owner,
                                     etag,
                                     progress_cb,
                                     progress_data,
+                                    dp_factory,
                                     dpp,
                                     y,
                                      dest_object->get_trace());
index c2293309ac793fd2b82912be73b66638bf89fc76..85d6c1290cc0fc94ce627f8c893c5678cd55cedc 100644 (file)
@@ -6224,6 +6224,8 @@ void RGWCopyObj::execute(optional_yield y)
     return;
   }
 
+  RGWCopyObjDPF copy_obj_dpf(driver, s, obj_size, crypt_http_responses);
+
   op_ret = s->src_object->copy_object(s->owner,
           s->user->get_id(),
           &s->info,
@@ -6249,6 +6251,7 @@ void RGWCopyObj::execute(optional_yield y)
           &s->req_id, /* use req_id as tag */
           &etag,
           copy_obj_progress_cb, (void *)this,
+          &copy_obj_dpf,
           this,
           s->yield);
 
index 71c158f489d68bd07f5d24b01d752cbff9281814..5dac376e018bc9b91489db8cd69f6a0cc17a8d73 100644 (file)
@@ -1610,6 +1610,7 @@ protected:
   std::string_view copy_source;
   // Not actually required
   std::optional<std::string_view> md_directive;
+  std::map<std::string, std::string> crypt_http_responses;
 
   off_t ofs;
   off_t len;
index 23f1c9542893bb1a0705c977c17e4679bea841b3..c5b05107069ced7be5279fcefdc77a7d13c546ef 100644 (file)
@@ -3892,6 +3892,9 @@ void RGWCopyObj_ObjStore_S3::send_partial_response(off_t ofs)
     set_req_state_err(s, op_ret);
     dump_errno(s);
 
+    for (auto &it : crypt_http_responses)
+      dump_header(s, it.first, it.second);
+
     // Explicitly use chunked transfer encoding so that we can stream the result
     // to the user without having to wait for the full length of it.
     end_header(s, this, to_mime_type(s->format), CHUNKED_TRANSFER_ENCODING);
index 4daf0337a1d988cea13701e991a05a9f8a64836e..909bf4acee296150be080d2726bf6b857890485b 100644 (file)
@@ -1541,6 +1541,7 @@ TEST_F(POSIXObjectTest, ObjectCopy)
           &tag,
           nullptr,
           nullptr,
+          nullptr,
           env->dpp,
           null_yield);
   EXPECT_EQ(ret, 0);
@@ -1849,6 +1850,7 @@ TEST_F(POSIXMPObjectTest, MPUploadCopy)
           &tag,
           nullptr,
           nullptr,
+          nullptr,
           env->dpp,
           null_yield);
   EXPECT_EQ(ret, 0);
@@ -2298,6 +2300,7 @@ TEST_F(POSIXVerObjectTest, ObjectCopy)
           &tag,
           nullptr,
           nullptr,
+          nullptr,
           env->dpp,
           null_yield);
   EXPECT_EQ(ret, 0);
@@ -2375,6 +2378,7 @@ TEST_F(POSIXVerObjectTest, CopyVersion)
           &tag,
           nullptr,
           nullptr,
+          nullptr,
           env->dpp,
           null_yield);
   EXPECT_EQ(ret, 0);