From e7988e1c9119f408924e029853033ed676747f1e Mon Sep 17 00:00:00 2001 From: Mark Kogan Date: Mon, 5 Apr 2021 15:49:42 +0300 Subject: [PATCH] rgw: return OK on consecutive complete-multipart reqs Fixes: https://tracker.ceph.com/issues/50141 Signed-off-by: Mark Kogan fixup! rgw: return OK on consecutive complete-multipart reqs (cherry picked from commit 324c377849a5d246f689f6e7a2862f42f1504d2c) Cherry-pick notes: - rgw::sal::Attrs is named rgw::sal::RGWAttrs in Pacific --- src/rgw/rgw_op.cc | 43 +++++++++++++++++++++++++++++++++++++++++++ src/rgw/rgw_op.h | 1 + 2 files changed, 44 insertions(+) diff --git a/src/rgw/rgw_op.cc b/src/rgw/rgw_op.cc index 655f057f76205..2596c60c415f7 100644 --- a/src/rgw/rgw_op.cc +++ b/src/rgw/rgw_op.cc @@ -6235,6 +6235,11 @@ void RGWCompleteMultipart::execute(optional_yield y) op_ret = serializer->try_lock(this, dur, y); if (op_ret < 0) { ldpp_dout(this, 0) << "failed to acquire lock" << dendl; + if (op_ret == -ENOENT && check_previously_completed(this, parts)) { + ldpp_dout(this, 1) << "NOTICE: This multipart completion is already completed" << dendl; + op_ret = 0; + return; + } op_ret = -ERR_INTERNAL_ERROR; s->err.message = "This multipart completion is already in progress"; return; @@ -6435,6 +6440,44 @@ void RGWCompleteMultipart::execute(optional_yield y) } } // RGWCompleteMultipart::execute +bool RGWCompleteMultipart::check_previously_completed(const DoutPrefixProvider* dpp, const RGWMultiCompleteUpload* parts) +{ + // re-calculate the etag from the parts and compare to the existing object + s->object->set_bucket(s->bucket.get()); + int ret = s->object->get_obj_attrs(s->obj_ctx, s->yield, this); + if (ret < 0) { + ldpp_dout(dpp, 0) << __func__ << "() ERROR: get_obj_attrs() returned ret=" << ret << dendl; + return false; + } + rgw::sal::RGWAttrs sattrs = s->object->get_attrs(); + string oetag = sattrs[RGW_ATTR_ETAG].to_str(); + + MD5 hash; + for (const auto& [index, part] : parts->parts) { + std::string partetag = rgw_string_unquote(part); + char petag[CEPH_CRYPTO_MD5_DIGESTSIZE]; + hex_to_buf(partetag.c_str(), petag, CEPH_CRYPTO_MD5_DIGESTSIZE); + hash.Update((const unsigned char *)petag, sizeof(petag)); + ldpp_dout(dpp, 20) << __func__ << "() re-calculating multipart etag: part: " + << index << ", etag: " << partetag << dendl; + } + + unsigned char final_etag[CEPH_CRYPTO_MD5_DIGESTSIZE]; + char final_etag_str[CEPH_CRYPTO_MD5_DIGESTSIZE * 2 + 16]; + hash.Final(final_etag); + buf_to_hex(final_etag, CEPH_CRYPTO_MD5_DIGESTSIZE, final_etag_str); + snprintf(&final_etag_str[CEPH_CRYPTO_MD5_DIGESTSIZE * 2], sizeof(final_etag_str) - CEPH_CRYPTO_MD5_DIGESTSIZE * 2, + "-%lld", (long long)parts->parts.size()); + + if (oetag.compare(final_etag_str) != 0) { + ldpp_dout(dpp, 1) << __func__ << "() NOTICE: etag mismatch: object etag:" + << oetag << ", re-calculated etag:" << final_etag_str << dendl; + return false; + } + ldpp_dout(dpp, 5) << __func__ << "() object etag and re-calculated etag match, etag: " << oetag << dendl; + return true; +} + void RGWCompleteMultipart::complete() { /* release exclusive lock iff not already */ diff --git a/src/rgw/rgw_op.h b/src/rgw/rgw_op.h index db73d65cf5420..828d59e7b5f79 100644 --- a/src/rgw/rgw_op.h +++ b/src/rgw/rgw_op.h @@ -1752,6 +1752,7 @@ public: int verify_permission(optional_yield y) override; void pre_exec() override; void execute(optional_yield y) override; + bool check_previously_completed(const DoutPrefixProvider* dpp, const RGWMultiCompleteUpload* parts); void complete() override; virtual int get_params(optional_yield y) = 0; -- 2.39.5