From: Seena Fallah Date: Sat, 7 Jun 2025 20:15:55 +0000 (+0200) Subject: rgw: implement CopyObject for encrypted object X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=fdb78c7fb071a63e318d902be10bf17e48f9fa0d;p=ceph.git rgw: implement CopyObject for encrypted object 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 Signed-off-by: Seena Fallah --- diff --git a/src/rgw/driver/rados/rgw_rados.cc b/src/rgw/driver/rados/rgw_rados.cc index 2a1b5d541ea..54ab9fa0e91 100644 --- a/src/rgw/driver/rados/rgw_rados.cc +++ b/src/rgw/driver/rados/rgw_rados.cc @@ -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); diff --git a/src/rgw/driver/rados/rgw_rados.h b/src/rgw/driver/rados/rgw_rados.h index 38e2f3dcb7d..5340bb1ce20 100644 --- a/src/rgw/driver/rados/rgw_rados.h +++ b/src/rgw/driver/rados/rgw_rados.h @@ -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); diff --git a/src/rgw/driver/rados/rgw_sal_rados.cc b/src/rgw/driver/rados/rgw_sal_rados.cc index 90813103ef8..bb6689d6fc4 100644 --- a/src/rgw/driver/rados/rgw_sal_rados.cc +++ b/src/rgw/driver/rados/rgw_sal_rados.cc @@ -3736,6 +3736,7 @@ int RadosObject::copy_object(const ACLOwner& owner, etag, progress_cb, progress_data, + dp_factory, dpp, y, dest_object->get_trace()); diff --git a/src/rgw/rgw_op.cc b/src/rgw/rgw_op.cc index c2293309ac7..85d6c1290cc 100644 --- a/src/rgw/rgw_op.cc +++ b/src/rgw/rgw_op.cc @@ -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, + ©_obj_dpf, this, s->yield); diff --git a/src/rgw/rgw_op.h b/src/rgw/rgw_op.h index 71c158f489d..5dac376e018 100644 --- a/src/rgw/rgw_op.h +++ b/src/rgw/rgw_op.h @@ -1610,6 +1610,7 @@ protected: std::string_view copy_source; // Not actually required std::optional md_directive; + std::map crypt_http_responses; off_t ofs; off_t len; diff --git a/src/rgw/rgw_rest_s3.cc b/src/rgw/rgw_rest_s3.cc index 23f1c954289..c5b05107069 100644 --- a/src/rgw/rgw_rest_s3.cc +++ b/src/rgw/rgw_rest_s3.cc @@ -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); diff --git a/src/test/rgw/test_rgw_posix_driver.cc b/src/test/rgw/test_rgw_posix_driver.cc index 4daf0337a1d..909bf4acee2 100644 --- a/src/test/rgw/test_rgw_posix_driver.cc +++ b/src/test/rgw/test_rgw_posix_driver.cc @@ -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);