]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
rgw_cksum: multipart upload checksums
authorMatt Benjamin <mbenjamin@redhat.com>
Wed, 10 Apr 2024 21:58:15 +0000 (17:58 -0400)
committerMatt Benjamin <mbenjamin@redhat.com>
Wed, 3 Jul 2024 18:38:14 +0000 (14:38 -0400)
includes commits:

* fixes init-multipart header return
* introduce checksum to SAL MultipartPart interface
* thread optional checksum through DataProcessor
* code complete multipart checksum verify
* fix formatter
* fix ckecksum format for multipart objects in GET/HEAD ops
* always return parts_count from ReadOp::prepare() if applicable
      This behavior is used when returning the checksum of a multipart
      upload object.
* tweak conditional multipart_parts_count
* add checksum output to ListMultipart
* fix nil-return from GetHeaderCksumResult
* re-arm truncated if re-entering list-parts
* complete-multipart w/list-parts
* validate supplied checksum in CompleteMultipart
* verify checksum type against initial checksum algorithm
* rgw_op: suppress more x-amz headers
* final fixes and cleanups
* remove unused t0

Signed-off-by: Matt Benjamin <mbenjamin@redhat.com>
29 files changed:
src/rgw/driver/d4n/rgw_sal_d4n.cc
src/rgw/driver/d4n/rgw_sal_d4n.h
src/rgw/driver/daos/rgw_sal_daos.cc
src/rgw/driver/daos/rgw_sal_daos.h
src/rgw/driver/motr/rgw_sal_motr.h
src/rgw/driver/posix/rgw_sal_posix.cc
src/rgw/driver/posix/rgw_sal_posix.h
src/rgw/driver/rados/rgw_putobj_processor.cc
src/rgw/driver/rados/rgw_putobj_processor.h
src/rgw/driver/rados/rgw_rados.cc
src/rgw/driver/rados/rgw_sal_rados.cc
src/rgw/driver/rados/rgw_sal_rados.h
src/rgw/rgw_basic_types.h
src/rgw/rgw_cksum.h
src/rgw/rgw_cksum_pipe.cc
src/rgw/rgw_cksum_pipe.h
src/rgw/rgw_common.h
src/rgw/rgw_data_access.cc
src/rgw/rgw_file.cc
src/rgw/rgw_op.cc
src/rgw/rgw_op.h
src/rgw/rgw_process.cc
src/rgw/rgw_rest_s3.cc
src/rgw/rgw_sal.h
src/rgw/rgw_sal_dbstore.cc
src/rgw/rgw_sal_dbstore.h
src/rgw/rgw_sal_filter.cc
src/rgw/rgw_sal_filter.h
src/test/rgw/test_rgw_cksum.cc

index 0f3c0ddb224fa9f38f90d9e70104aa7c3531fb96..712cebbfc051feb0000c496ff15b6184b2a5732c 100644 (file)
@@ -811,6 +811,7 @@ int D4NFilterWriter::process(bufferlist&& data, uint64_t offset)
 int D4NFilterWriter::complete(size_t accounted_size, const std::string& etag,
                        ceph::real_time *mtime, ceph::real_time set_mtime,
                        std::map<std::string, bufferlist>& attrs,
+                      const std::optional<rgw::cksum::Cksum>& cksum,
                        ceph::real_time delete_at,
                        const char *if_match, const char *if_nomatch,
                        const std::string *user_data,
@@ -830,7 +831,7 @@ int D4NFilterWriter::complete(size_t accounted_size, const std::string& etag,
     ldpp_dout(save_dpp, 10) << "D4NFilterWriter::" << __func__ << "(): ObjectDirectory set method failed." << dendl;
    
   /* Retrieve complete set of attrs */
-  int ret = next->complete(accounted_size, etag, mtime, set_mtime, attrs,
+  int ret = next->complete(accounted_size, etag, mtime, set_mtime, attrs, cksum,
                        delete_at, if_match, if_nomatch, user_data, zones_trace,
                        canceled, rctx, flags);
   obj->get_obj_attrs(rctx.y, save_dpp, NULL);
index 5c71582406f1406760d073735507c9000cbb40d5..e7d041d2a195a5ff9cd7ef03936ee5c9a2b3d553 100644 (file)
@@ -219,14 +219,15 @@ class D4NFilterWriter : public FilterWriter {
     virtual int prepare(optional_yield y);
     virtual int process(bufferlist&& data, uint64_t offset) override;
     virtual int complete(size_t accounted_size, const std::string& etag,
-                       ceph::real_time *mtime, ceph::real_time set_mtime,
-                       std::map<std::string, bufferlist>& attrs,
-                       ceph::real_time delete_at,
-                       const char *if_match, const char *if_nomatch,
-                       const std::string *user_data,
-                       rgw_zone_set *zones_trace, bool *canceled,
-                       const req_context& rctx,
-                       uint32_t flags) override;
+                        ceph::real_time *mtime, ceph::real_time set_mtime,
+                        std::map<std::string, bufferlist>& attrs,
+                        const std::optional<rgw::cksum::Cksum>& cksum,
+                        ceph::real_time delete_at,
+                        const char *if_match, const char *if_nomatch,
+                        const std::string *user_data,
+                        rgw_zone_set *zones_trace, bool *canceled,
+                        const req_context& rctx,
+                        uint32_t flags) override;
    bool is_atomic() { return atomic; };
    const DoutPrefixProvider* dpp() { return save_dpp; } 
 };
index 703e8dd77bf8a7a658ca2df5ae1438cf2a1c5be1..6d83c7d159aed4b0f542402ea23faf0029b15fd1 100644 (file)
@@ -1596,6 +1596,7 @@ int DaosMultipartUpload::init(const DoutPrefixProvider* dpp, optional_yield y,
 
   multipart_upload_info upload_info;
   upload_info.dest_placement = dest_placement;
+  upload_info.cksum_type = cksum_type;
 
   ent.encode(bl);
   encode(attrs, bl);
@@ -1968,6 +1969,7 @@ int DaosMultipartUpload::get_info(const DoutPrefixProvider* dpp,
 
   // Now decode the placement rule
   decode(upload_info, iter);
+  cksum_type = upload_info.cksum_type;
   placement = upload_info.dest_placement;
   *rule = &placement;
 
index 39749a0d33ceb2e28b93e88e9b0ba60ff9dd4a72..8cff96088ee634c96042b07d81cd097e90b48b3b 100644 (file)
@@ -742,6 +742,7 @@ class DaosAtomicWriter : public StoreWriter {
   virtual int complete(size_t accounted_size, const std::string& etag,
                        ceph::real_time* mtime, ceph::real_time set_mtime,
                        std::map<std::string, bufferlist>& attrs,
+                      const std::optional<rgw::cksum::Cksum>& cksum,
                        ceph::real_time delete_at, const char* if_match,
                        const char* if_nomatch, const std::string* user_data,
                        rgw_zone_set* zones_trace, bool* canceled,
@@ -788,6 +789,7 @@ class DaosMultipartWriter : public StoreWriter {
   virtual int complete(size_t accounted_size, const std::string& etag,
                        ceph::real_time* mtime, ceph::real_time set_mtime,
                        std::map<std::string, bufferlist>& attrs,
+                      const std::optional<rgw::cksum::Cksum>& cksum,
                        ceph::real_time delete_at, const char* if_match,
                        const char* if_nomatch, const std::string* user_data,
                        rgw_zone_set* zones_trace, bool* canceled,
@@ -810,6 +812,10 @@ class DaosMultipartPart : public StoreMultipartPart {
   virtual const std::string& get_etag() { return info.etag; }
   virtual ceph::real_time& get_mtime() { return info.modified; }
 
+  virtual const std::optional<rgw::cksum::Cksum>& get_cksum() {
+    return info.cksum;
+  }
+
   friend class DaosMultipartUpload;
 };
 
index ac1af5376f32e3834d5a3029f0475c9f87082855..4d9c189993b5bd6b8e95116a700ea79b98a4f6b0 100644 (file)
@@ -791,6 +791,7 @@ class MotrAtomicWriter : public StoreWriter {
   virtual int complete(size_t accounted_size, const std::string& etag,
                        ceph::real_time *mtime, ceph::real_time set_mtime,
                        std::map<std::string, bufferlist>& attrs,
+                      const std::optional<rgw::cksum::Cksum>& cksum,
                        ceph::real_time delete_at,
                        const char *if_match, const char *if_nomatch,
                        const std::string *user_data,
@@ -839,6 +840,7 @@ public:
   virtual int complete(size_t accounted_size, const std::string& etag,
                        ceph::real_time *mtime, ceph::real_time set_mtime,
                        std::map<std::string, bufferlist>& attrs,
+                      const std::optional<rgw::cksum::Cksum>& cksum,
                        ceph::real_time delete_at,
                        const char *if_match, const char *if_nomatch,
                        const std::string *user_data,
@@ -888,6 +890,9 @@ public:
   virtual uint64_t get_size() { return info.accounted_size; }
   virtual const std::string& get_etag() { return info.etag; }
   virtual ceph::real_time& get_mtime() { return info.modified; }
+  virtual const std::optional<rgw::cksum::Cksum>& get_cksum() {
+    return info.cksum;
+  }
 
   RGWObjManifest& get_manifest() { return info.manifest; }
 
index 6f5289f58bfd0399d16c94ca5290b0b5a1ad03bd..145a578965354cf6490e8ae90e9213bd3bf30293 100644 (file)
@@ -2509,6 +2509,7 @@ int POSIXMultipartUpload::init(const DoutPrefixProvider *dpp, optional_yield y,
 
   meta_obj = get_meta_obj();
 
+  mp_obj.upload_info.cksum_type = cksum_type;
   mp_obj.upload_info.dest_placement = dest_placement;
 
   bufferlist bl;
@@ -2796,9 +2797,12 @@ int POSIXMultipartWriter::process(bufferlist&& data, uint64_t offset)
   return obj->write(offset, data, dpp, null_yield);
 }
 
-int POSIXMultipartWriter::complete(size_t accounted_size, const std::string& etag,
+int POSIXMultipartWriter::complete(
+                      size_t accounted_size,
+                      const std::string& etag,
                        ceph::real_time *mtime, ceph::real_time set_mtime,
                        std::map<std::string, bufferlist>& attrs,
+                      const std::optional<rgw::cksum::Cksum>& cksum,
                        ceph::real_time delete_at,
                        const char *if_match, const char *if_nomatch,
                        const std::string *user_data,
@@ -2828,6 +2832,7 @@ int POSIXMultipartWriter::complete(size_t accounted_size, const std::string& eta
 
   info.num = part_num;
   info.etag = etag;
+  info.cksum = cksum;
   info.mtime = set_mtime;
 
   bufferlist bl;
@@ -2867,6 +2872,7 @@ int POSIXAtomicWriter::process(bufferlist&& data, uint64_t offset)
 int POSIXAtomicWriter::complete(size_t accounted_size, const std::string& etag,
                        ceph::real_time *mtime, ceph::real_time set_mtime,
                        std::map<std::string, bufferlist>& attrs,
+                      const std::optional<rgw::cksum::Cksum>& cksum,
                        ceph::real_time delete_at,
                        const char *if_match, const char *if_nomatch,
                        const std::string *user_data,
index d2ea90b01093d84b3b61d9ae8c7be0288686d483..7483139da33e561ab8ba0b444d8f134971b821d3 100644 (file)
@@ -479,19 +479,24 @@ struct POSIXUploadPartInfo {
   uint32_t num{0};
   std::string etag;
   ceph::real_time mtime;
+  std::optional<rgw::cksum::Cksum> cksum;
 
   void encode(bufferlist& bl) const {
-    ENCODE_START(1, 1, bl);
+    ENCODE_START(2, 1, bl);
     encode(num, bl);
     encode(etag, bl);
     encode(mtime, bl);
+    encode(cksum, bl);
     ENCODE_FINISH(bl);
   }
   void decode(bufferlist::const_iterator& bl) {
-    DECODE_START(1, bl);
+    DECODE_START_LEGACY_COMPAT_LEN(2, 1, 1, bl);
     decode(num, bl);
     decode(etag, bl);
     decode(mtime, bl);
+    if (struct_v > 1) {
+      decode(cksum, bl);
+    }
     DECODE_FINISH(bl);
   }
 };
@@ -514,8 +519,12 @@ public:
   virtual uint64_t get_size() { return shadow->get_size(); }
   virtual const std::string& get_etag() { return info.etag; }
   virtual ceph::real_time& get_mtime() { return info.mtime; }
+  virtual const std::optional<rgw::cksum::Cksum>& get_cksum() {
+    return info.cksum;
+  }
 
-  int load(const DoutPrefixProvider* dpp, optional_yield y, POSIXDriver* driver, rgw_obj_key& key);
+  int load(const DoutPrefixProvider* dpp, optional_yield y, POSIXDriver* driver,
+          rgw_obj_key& key);
 
   friend class POSIXMultipartUpload;
 };
@@ -605,6 +614,7 @@ public:
   virtual int complete(size_t accounted_size, const std::string& etag,
                        ceph::real_time *mtime, ceph::real_time set_mtime,
                       std::map<std::string, bufferlist>& attrs,
+                      const std::optional<rgw::cksum::Cksum>& cksum,
                       ceph::real_time delete_at,
                       const char *if_match, const char *if_nomatch,
                       const std::string *user_data,
@@ -645,6 +655,7 @@ public:
   virtual int complete(size_t accounted_size, const std::string& etag,
                        ceph::real_time *mtime, ceph::real_time set_mtime,
                       std::map<std::string, bufferlist>& attrs,
+                      const std::optional<rgw::cksum::Cksum>& cksum,
                       ceph::real_time delete_at,
                       const char *if_match, const char *if_nomatch,
                       const std::string *user_data,
index d41678cdb06b816a94931974819a357c73eb13c4..79be7b3209bcf419396c937529c8176b41e44fd4 100644 (file)
@@ -338,19 +338,21 @@ int AtomicObjectProcessor::prepare(optional_yield y)
   return 0;
 }
 
-int AtomicObjectProcessor::complete(size_t accounted_size,
-                                    const std::string& etag,
-                                    ceph::real_time *mtime,
-                                    ceph::real_time set_mtime,
-                                    rgw::sal::Attrs& attrs,
-                                    ceph::real_time delete_at,
-                                    const char *if_match,
-                                    const char *if_nomatch,
-                                    const std::string *user_data,
-                                    rgw_zone_set *zones_trace,
-                                    bool *pcanceled, 
-                                    const req_context& rctx,
-                                    uint32_t flags)
+int AtomicObjectProcessor::complete(
+                               size_t accounted_size,
+                               const std::string& etag,
+                               ceph::real_time *mtime,
+                               ceph::real_time set_mtime,
+                               rgw::sal::Attrs& attrs,
+                               const std::optional<rgw::cksum::Cksum>& cksum,
+                               ceph::real_time delete_at,
+                               const char *if_match,
+                               const char *if_nomatch,
+                               const std::string *user_data,
+                               rgw_zone_set *zones_trace,
+                               bool *pcanceled, 
+                               const req_context& rctx,
+                               uint32_t flags)
 {
   int r = writer.drain();
   if (r < 0) {
@@ -486,19 +488,21 @@ int MultipartObjectProcessor::prepare(optional_yield y)
   return prepare_head();
 }
 
-int MultipartObjectProcessor::complete(size_t accounted_size,
-                                       const std::string& etag,
-                                       ceph::real_time *mtime,
-                                       ceph::real_time set_mtime,
-                                       std::map<std::string, bufferlist>& attrs,
-                                       ceph::real_time delete_at,
-                                       const char *if_match,
-                                       const char *if_nomatch,
-                                       const std::string *user_data,
-                                       rgw_zone_set *zones_trace,
-                                       bool *pcanceled, 
-                                       const req_context& rctx,
-                                       uint32_t flags)
+int MultipartObjectProcessor::complete(
+                              size_t accounted_size,
+                              const std::string& etag,
+                              ceph::real_time *mtime,
+                              ceph::real_time set_mtime,
+                              std::map<std::string, bufferlist>& attrs,
+                              const std::optional<rgw::cksum::Cksum>& cksum,
+                              ceph::real_time delete_at,
+                              const char *if_match,
+                              const char *if_nomatch,
+                              const std::string *user_data,
+                              rgw_zone_set *zones_trace,
+                              bool *pcanceled, 
+                              const req_context& rctx,
+                              uint32_t flags)
 {
   int r = writer.drain();
   if (r < 0) {
@@ -541,6 +545,7 @@ int MultipartObjectProcessor::complete(size_t accounted_size,
   }
   info.num = part_num;
   info.etag = etag;
+  info.cksum = cksum;
   info.size = actual_size;
   info.accounted_size = accounted_size;
   info.modified = real_clock::now();
@@ -701,11 +706,16 @@ int AppendObjectProcessor::prepare(optional_yield y)
   return 0;
 }
 
-int AppendObjectProcessor::complete(size_t accounted_size, const string &etag, ceph::real_time *mtime,
-                                    ceph::real_time set_mtime, rgw::sal::Attrs& attrs,
-                                    ceph::real_time delete_at, const char *if_match, const char *if_nomatch,
-                                    const string *user_data, rgw_zone_set *zones_trace, bool *pcanceled,
-                                    const req_context& rctx, uint32_t flags)
+int AppendObjectProcessor::complete(
+                           size_t accounted_size,
+                           const string &etag, ceph::real_time *mtime,
+                           ceph::real_time set_mtime, rgw::sal::Attrs& attrs,
+                           const std::optional<rgw::cksum::Cksum>& cksum,
+                           ceph::real_time delete_at, const char *if_match,
+                           const char *if_nomatch,
+                           const string *user_data, rgw_zone_set *zones_trace,
+                           bool *pcanceled,
+                           const req_context& rctx, uint32_t flags)
 {
   int r = writer.drain();
   if (r < 0)
@@ -764,7 +774,8 @@ int AppendObjectProcessor::complete(size_t accounted_size, const string &etag, c
   }
   r = obj_op.write_meta(actual_size + cur_size,
                        accounted_size + *cur_accounted_size,
-                       attrs, rctx, writer.get_trace(), flags & rgw::sal::FLAG_LOG_OP);
+                       attrs, rctx, writer.get_trace(),
+                       flags & rgw::sal::FLAG_LOG_OP);
   if (r < 0) {
     return r;
   }
index 655428f83e9bd959b8373a2cafb3bf77bd27ec61..3f3b0b31fcff3430855853314f11b49a4066b30e 100644 (file)
@@ -193,6 +193,7 @@ class AtomicObjectProcessor : public ManifestObjectProcessor {
   int complete(size_t accounted_size, const std::string& etag,
                ceph::real_time *mtime, ceph::real_time set_mtime,
                std::map<std::string, bufferlist>& attrs,
+              const std::optional<rgw::cksum::Cksum>& cksum,
                ceph::real_time delete_at,
                const char *if_match, const char *if_nomatch,
                const std::string *user_data,
@@ -241,6 +242,7 @@ class MultipartObjectProcessor : public ManifestObjectProcessor {
   int complete(size_t accounted_size, const std::string& etag,
                ceph::real_time *mtime, ceph::real_time set_mtime,
                std::map<std::string, bufferlist>& attrs,
+              const std::optional<rgw::cksum::Cksum>& cksum,
                ceph::real_time delete_at,
                const char *if_match, const char *if_nomatch,
                const std::string *user_data,
@@ -279,7 +281,9 @@ class MultipartObjectProcessor : public ManifestObjectProcessor {
     int prepare(optional_yield y) override;
     int complete(size_t accounted_size, const std::string& etag,
                  ceph::real_time *mtime, ceph::real_time set_mtime,
-                 std::map<std::string, bufferlist>& attrs, ceph::real_time delete_at,
+                 std::map<std::string, bufferlist>& attrs,
+                const std::optional<rgw::cksum::Cksum>& cksum,
+                ceph::real_time delete_at,
                  const char *if_match, const char *if_nomatch, const std::string *user_data,
                  rgw_zone_set *zones_trace, bool *canceled,
                  const req_context& rctx,
index 5e0f2c30ca9740ce7a69c292bec071e39b14c735..088435d857382395b9c4d74c353fdb9aa1c6d846 100644 (file)
@@ -23,6 +23,7 @@
 #include "common/BackTrace.h"
 #include "common/ceph_time.h"
 
+#include "rgw_cksum.h"
 #include "rgw_sal.h"
 #include "rgw_zone.h"
 #include "rgw_cache.h"
@@ -4489,8 +4490,9 @@ int RGWRados::fetch_remote_obj(RGWObjectCtx& obj_ctx,
   for (i = 0; i < MAX_COMPLETE_RETRY; i++) {
     bool canceled = false;
     ret = processor.complete(accounted_size, etag, mtime, set_mtime,
-                             attrs, delete_at, nullptr, nullptr, nullptr,
-                             zones_trace, &canceled, rctx, rgw::sal::FLAG_LOG_OP);
+                             attrs, rgw::cksum::no_cksum, delete_at, nullptr, nullptr,
+                            nullptr, zones_trace, &canceled, rctx,
+                            rgw::sal::FLAG_LOG_OP);
     if (ret < 0) {
       goto set_err_state;
     }
@@ -5047,7 +5049,8 @@ 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, delete_at,
+  return processor.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);
 }
index 0addbe01923a8ad5a891b0c4b30c9d9357abc8c2..b6eab4357d39362fb9301d863766f6db4b0e8bc5 100644 (file)
@@ -3135,6 +3135,7 @@ int RadosMultipartUpload::init(const DoutPrefixProvider *dpp, optional_yield y,
 
     multipart_upload_info upload_info;
     upload_info.dest_placement = dest_placement;
+    upload_info.cksum_type = cksum_type;
     
     if (obj_legal_hold) {
       upload_info.obj_legal_hold_exist = true;
@@ -3527,6 +3528,7 @@ int RadosMultipartUpload::get_info(const DoutPrefixProvider *dpp, optional_yield
     ldpp_dout(dpp, 0) << "ERROR: failed to decode multipart upload info" << dendl;
     return -EIO;
   }
+  cksum_type = upload_info.cksum_type;
   placement = upload_info.dest_placement;
   upload_information = upload_info;
   *rule = &placement;
@@ -3726,6 +3728,7 @@ int RadosAtomicWriter::process(bufferlist&& data, uint64_t offset)
 int RadosAtomicWriter::complete(size_t accounted_size, const std::string& etag,
                        ceph::real_time *mtime, ceph::real_time set_mtime,
                        std::map<std::string, bufferlist>& attrs,
+                      const std::optional<rgw::cksum::Cksum>& cksum,
                        ceph::real_time delete_at,
                        const char *if_match, const char *if_nomatch,
                        const std::string *user_data,
@@ -3733,8 +3736,9 @@ int RadosAtomicWriter::complete(size_t accounted_size, const std::string& etag,
                        const req_context& rctx,
                        uint32_t flags)
 {
-  return processor.complete(accounted_size, etag, mtime, set_mtime, attrs, delete_at,
-                           if_match, if_nomatch, user_data, zones_trace, canceled, rctx, flags);
+  return processor.complete(accounted_size, etag, mtime, set_mtime, attrs,
+                           cksum, delete_at, if_match, if_nomatch,
+                           user_data, zones_trace, canceled, rctx, flags);
 }
 
 int RadosAppendWriter::prepare(optional_yield y)
@@ -3750,6 +3754,7 @@ int RadosAppendWriter::process(bufferlist&& data, uint64_t offset)
 int RadosAppendWriter::complete(size_t accounted_size, const std::string& etag,
                        ceph::real_time *mtime, ceph::real_time set_mtime,
                        std::map<std::string, bufferlist>& attrs,
+                      const std::optional<rgw::cksum::Cksum>& cksum,
                        ceph::real_time delete_at,
                        const char *if_match, const char *if_nomatch,
                        const std::string *user_data,
@@ -3757,8 +3762,9 @@ int RadosAppendWriter::complete(size_t accounted_size, const std::string& etag,
                        const req_context& rctx,
                        uint32_t flags)
 {
-  return processor.complete(accounted_size, etag, mtime, set_mtime, attrs, delete_at,
-                           if_match, if_nomatch, user_data, zones_trace, canceled, rctx, flags);
+  return processor.complete(accounted_size, etag, mtime, set_mtime, attrs,
+                           cksum, delete_at, if_match, if_nomatch,
+                           user_data, zones_trace, canceled, rctx, flags);
 }
 
 int RadosMultipartWriter::prepare(optional_yield y)
@@ -3771,9 +3777,12 @@ int RadosMultipartWriter::process(bufferlist&& data, uint64_t offset)
   return processor.process(std::move(data), offset);
 }
 
-int RadosMultipartWriter::complete(size_t accounted_size, const std::string& etag,
+int RadosMultipartWriter::complete(
+                      size_t accounted_size,
+                      const std::string& etag,
                        ceph::real_time *mtime, ceph::real_time set_mtime,
                        std::map<std::string, bufferlist>& attrs,
+                      const std::optional<rgw::cksum::Cksum>& cksum,
                        ceph::real_time delete_at,
                        const char *if_match, const char *if_nomatch,
                        const std::string *user_data,
@@ -3781,8 +3790,9 @@ int RadosMultipartWriter::complete(size_t accounted_size, const std::string& eta
                        const req_context& rctx,
                        uint32_t flags)
 {
-  return processor.complete(accounted_size, etag, mtime, set_mtime, attrs, delete_at,
-                            if_match, if_nomatch, user_data, zones_trace, canceled, rctx, flags);
+  return processor.complete(accounted_size, etag, mtime, set_mtime, attrs,
+                           cksum, delete_at, if_match, if_nomatch,
+                           user_data, zones_trace, canceled, rctx, flags);
 }
 
 bool RadosZoneGroup::placement_target_exists(std::string& target) const
index ff36c2edfd8e92dd1ec730641c28db43787947d8..4abc7112c9cb30fc4af91ae625a9304e6b3459a5 100644 (file)
@@ -776,6 +776,9 @@ public:
   virtual uint64_t get_size() { return info.accounted_size; }
   virtual const std::string& get_etag() { return info.etag; }
   virtual ceph::real_time& get_mtime() { return info.modified; }
+  virtual const std::optional<rgw::cksum::Cksum>& get_cksum() {
+    return info.cksum;
+  }
 
   /* For RadosStore code */
   RGWObjManifest& get_manifest() { return info.manifest; }
@@ -980,6 +983,7 @@ public:
   virtual int complete(size_t accounted_size, const std::string& etag,
                        ceph::real_time *mtime, ceph::real_time set_mtime,
                        std::map<std::string, bufferlist>& attrs,
+                      const std::optional<rgw::cksum::Cksum>& cksum,
                        ceph::real_time delete_at,
                        const char *if_match, const char *if_nomatch,
                        const std::string *user_data,
@@ -1029,6 +1033,7 @@ public:
   virtual int complete(size_t accounted_size, const std::string& etag,
                        ceph::real_time *mtime, ceph::real_time set_mtime,
                        std::map<std::string, bufferlist>& attrs,
+                      const std::optional<rgw::cksum::Cksum>& cksum,
                        ceph::real_time delete_at,
                        const char *if_match, const char *if_nomatch,
                        const std::string *user_data,
@@ -1075,6 +1080,7 @@ public:
   virtual int complete(size_t accounted_size, const std::string& etag,
                        ceph::real_time *mtime, ceph::real_time set_mtime,
                        std::map<std::string, bufferlist>& attrs,
+                      const std::optional<rgw::cksum::Cksum>& cksum,
                        ceph::real_time delete_at,
                        const char *if_match, const char *if_nomatch,
                        const std::string *user_data,
index 771466ffae5f63f60776a528a6ad0c705c4ebe45..d09f06a4a98f38761498e3994fca2b26c32005ac 100644 (file)
@@ -21,6 +21,7 @@
 #pragma once
 
 #include <string>
+#include <optional>
 #include <fmt/format.h>
 
 #include "include/types.h"
@@ -256,10 +257,10 @@ struct RGWUploadPartInfo {
   uint64_t size;
   uint64_t accounted_size{0};
   std::string etag;
-  rgw::cksum::Cksum cksum;
   ceph::real_time modified;
   RGWObjManifest manifest;
   RGWCompressionInfo cs_info;
+  std::optional<rgw::cksum::Cksum> cksum;
 
   // Previous part obj prefixes. Recorded here for later cleanup.
   std::set<std::string> past_prefixes; 
index 06ccacf05e0cd6d7bc70ac43d8a829270570de44..c0a314d467f8da782044b63c846f7fbad9c4e412 100644 (file)
@@ -15,6 +15,8 @@
 
 #include <boost/algorithm/string/predicate.hpp>
 #include <cstdint>
+#include <cstring>
+#include <optional>
 #include <stdint.h>
 #include <string>
 #include <string_view>
@@ -81,6 +83,11 @@ namespace rgw { namespace cksum {
 
   namespace  ba = boost::algorithm;
 
+  static inline std::string safe_upcase_str(std::string s) {
+    std::transform(s.begin(), s.end(), s.begin(), ::toupper);
+    return s;
+  }
+
   class Cksum {
   public:
     static constexpr std::array<Desc, 8> checksums =
@@ -102,23 +109,41 @@ namespace rgw { namespace cksum {
     value_type digest;
 
     Cksum(Type _type = Type::none) : type(_type) {}
+    Cksum(Type _type, const char* _armored_text)
+      : type(_type) {
+      const auto& ckd = checksums[uint16_t(type)];
+      (void) ceph_unarmor((char*) digest.begin(), (char*) digest.begin() + ckd.digest_size,
+                         _armored_text, _armored_text + std::strlen(_armored_text));
+    }
 
-    static const char* type_string(const Type type) {
+    const char* type_string() const {
       return (Cksum::checksums[uint16_t(type)]).name;
     }
 
-    std::string aws_name() {
-      return fmt::format("x-amz-checksum-{}", type_string(type));
+    const bool aws() const {
+      return (Cksum::checksums[uint16_t(type)]).aws();
+    }
+
+    std::string aws_name() const {
+      return fmt::format("x-amz-checksum-{}", type_string());
     }
 
-    std::string rgw_name() {
-      return fmt::format("x-rgw-checksum-{}", type_string(type));
+    std::string rgw_name() const {
+      return fmt::format("x-rgw-checksum-{}", type_string());
     }
 
-    std::string header_name() {
-      return ((Cksum::checksums[uint16_t(type)]).aws()) ?
-       aws_name() :
-       rgw_name();
+    std::string header_name() const {
+      return (aws()) ? aws_name() : rgw_name();
+    }
+
+    std::string element_name() const {
+      std::string ts{type_string()};
+      return fmt::format("Checksum{}", safe_upcase_str(ts));
+    }
+
+    std::string_view raw() const {
+      const auto& ckd = checksums[uint16_t(type)];
+      return std::string_view((char*) digest.begin(), ckd.digest_size);
     }
 
     std::string to_armor() const {
@@ -172,6 +197,8 @@ namespace rgw { namespace cksum {
   }; /* Cksum */
   WRITE_CLASS_ENCODER(Cksum);
 
+  static inline const std::optional<rgw::cksum::Cksum> no_cksum{std::nullopt};
+
   static inline Type parse_cksum_type(const char* name)
   {
     for (const auto& ck : Cksum::checksums) {
index c11d821d7ec6434e2164d6cb97442ff9cd8a61eb..f9250303bceeac3d472c5b8b529f14afd4bc2160 100644 (file)
@@ -32,45 +32,19 @@ namespace rgw::putobj {
       dv(rgw::cksum::digest_factory(_type)),
       _digest(cksum::get_digest(dv)), cksum_hdr(_hdr),
       _state(State::DIGEST)
-  {
-    cksum::Digest* digest = cksum::get_digest(dv);
-    /* XXXX remove this */
-    std::cout << "ctor had digest " << _digest
-             << " and got digest: " << digest
-             << std::endl;
-  }
+  {}
 
   std::unique_ptr<RGWPutObj_Cksum> RGWPutObj_Cksum::Factory(
     rgw::sal::DataProcessor* next, const RGWEnv& env)
   {
     /* look for matching headers */
-    auto match = [&env] () -> const cksum_hdr_t {
-      /* If the individual checksum value you provide through
-      x-amz-checksum-algorithm doesn't match the checksum algorithm
-      you set through x-amz-sdk-checksum-algorithm, Amazon S3 ignores
-      any provided ChecksumAlgorithm parameter and uses the checksum
-      algorithm that matches the provided value in
-      x-amz-checksum-algorithm.
-      https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html
-      */
-      for (const auto hk : {"HTTP_X_AMZ_CHECKSUM_ALGORITHM",
-                           "HTTP_X_AMZ_SDK_CHECKSUM_ALGORITHM"}) {
-       auto hv = env.get(hk);
-       if (hv) {
-         return cksum_hdr_t(hk, hv);
-       }
-      }
-      return cksum_hdr_t(nullptr, nullptr);
-    };
-
-    auto algo_header = match();
+    auto algo_header = cksum_algorithm_hdr(env);
     if (algo_header.first) {
       if (algo_header.second) {
        auto cksum_type = cksum::parse_cksum_type(algo_header.second);
-       return  std::make_unique<RGWPutObj_Cksum>(
-                                                 next,
-                                                 cksum_type,
-                                                 std::move(algo_header));
+       return
+         std::make_unique<RGWPutObj_Cksum>(
+                                   next, cksum_type, std::move(algo_header));
       }
       /* malformed checksum algorithm header(s) */
       throw rgw::io::Exception(EINVAL, std::system_category());
index 047fbcb4ddd6b5ee5deefdf67205292ca8d1c1c8..dce389ec8ffd0a2ef23c43f04fe6db083d99a057 100644 (file)
 
 #pragma once
 
+#include <cstdint>
 #include <utility>
 #include <tuple>
 #include <cstring>
 #include "rgw_cksum.h"
+#include "rgw_common.h"
 #include "rgw_putobj.h"
 
 namespace rgw::putobj {
 
   namespace cksum = rgw::cksum;
   using cksum_hdr_t = std::pair<const char*, const char*>;
-  
+
+  static inline const cksum_hdr_t cksum_algorithm_hdr(const RGWEnv& env) {
+    /* If the individual checksum value you provide through
+       x-amz-checksum-algorithm doesn't match the checksum algorithm
+       you set through x-amz-sdk-checksum-algorithm, Amazon S3 ignores
+       any provided ChecksumAlgorithm parameter and uses the checksum
+       algorithm that matches the provided value in
+       x-amz-checksum-algorithm.
+       https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html
+    */
+    for (const auto hk : {"HTTP_X_AMZ_CHECKSUM_ALGORITHM",
+                         "HTTP_X_AMZ_SDK_CHECKSUM_ALGORITHM"}) {
+      auto hv = env.get(hk);
+      if (hv) {
+       return cksum_hdr_t(hk, hv);
+      }
+    }
+    return cksum_hdr_t(nullptr, nullptr);
+  } /* cksum_algorithm_hdr */
+
+  using GetHeaderCksumResult = std::pair<cksum::Cksum, std::string_view>;
+
+  static inline GetHeaderCksumResult get_hdr_cksum(const RGWEnv& env) {
+    cksum::Type cksum_type;
+    auto algo_hdr = cksum_algorithm_hdr(env);
+    if (algo_hdr.first) {
+      if (algo_hdr.second) {
+       cksum_type = cksum::parse_cksum_type(algo_hdr.second);
+       auto hk = fmt::format("HTTP_X_AMZ_CHECKSUM_{}", algo_hdr.second);
+       auto hv = env.get(hk.c_str());
+       if (hv) {
+         return
+           GetHeaderCksumResult(cksum::Cksum(cksum_type, hv),
+                                std::string_view(hv, std::strlen(hv)));
+       }
+      }
+    }
+    return GetHeaderCksumResult(cksum::Cksum(cksum_type), "");
+  } /* get_hdr_cksum */
+
+  /* CompleteMultipartUpload can have a checksum value but unlike
+   * PutObject, it won't have a checksum algorithm header, so we
+   * need to search for one */
+  static inline GetHeaderCksumResult find_hdr_cksum(const RGWEnv& env) {
+    cksum::Type cksum_type;
+    for (int16_t ix = int16_t(cksum::Type::crc32);
+        ix <= uint16_t(cksum::Type::blake3); ++ix) {
+      cksum_type = cksum::Type(ix);
+      auto hk = fmt::format("HTTP_X_AMZ_CHECKSUM_{}",
+                           safe_upcase_str(to_string(cksum_type)));
+      auto hv = env.get(hk.c_str());
+      if (hv) {
+       return
+         GetHeaderCksumResult(cksum::Cksum(cksum_type, hv),
+                              std::string_view(hv, std::strlen(hv)));
+      }
+    }
+    return GetHeaderCksumResult(cksum::Cksum(cksum_type), "");
+  } /* find_hdr_cksum */
+
   // PutObj filter for streaming checksums
   class RGWPutObj_Cksum : public rgw::putobj::Pipe {
 
index 929ebc60b9208b2402a6d14984dbee0713997c85..da2617f383545d5629eb98f8abeca26dfeee773a 100644 (file)
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <array>
+#include <cstdint>
 #include <string_view>
 #include <atomic>
 #include <unordered_map>
@@ -82,8 +83,9 @@ using ceph::crypto::MD5;
 #define RGW_ATTR_RATELIMIT     RGW_ATTR_PREFIX "ratelimit"
 #define RGW_ATTR_LC            RGW_ATTR_PREFIX "lc"
 #define RGW_ATTR_CORS          RGW_ATTR_PREFIX "cors"
-#define RGW_ATTR_ETAG          RGW_ATTR_PREFIX "etag"
+#define RGW_ATTR_ETAG RGW_ATTR_PREFIX "etag"
 #define RGW_ATTR_CKSUM         RGW_ATTR_PREFIX "cksum"
+#define RGW_ATTR_CKSUM_ALGORITHM     RGW_ATTR_PREFIX "x-amz-checksum-algorithm"
 #define RGW_ATTR_BUCKETS       RGW_ATTR_PREFIX "buckets"
 #define RGW_ATTR_META_PREFIX   RGW_ATTR_PREFIX RGW_AMZ_META_PREFIX
 #define RGW_ATTR_CONTENT_TYPE  RGW_ATTR_PREFIX "content_type"
@@ -162,9 +164,6 @@ using ceph::crypto::MD5;
 #define RGW_ATTR_UNIX_KEY1      RGW_ATTR_PREFIX "unix-key1"
 #define RGW_ATTR_UNIX1          RGW_ATTR_PREFIX "unix1"
 
-/* Content Checksums */
-#define RGW_ATTR_AMZ_CKSUM      RGW_ATTR_PREFIX "x-amz-content-checksum"
-
 #define RGW_ATTR_CRYPT_PREFIX   RGW_ATTR_PREFIX "crypt."
 #define RGW_ATTR_CRYPT_MODE     RGW_ATTR_CRYPT_PREFIX "mode"
 #define RGW_ATTR_CRYPT_KEYMD5   RGW_ATTR_CRYPT_PREFIX "keymd5"
@@ -1519,25 +1518,33 @@ struct multipart_upload_info
   bool obj_legal_hold_exist{false};
   RGWObjectRetention obj_retention;
   RGWObjectLegalHold obj_legal_hold;
+  rgw::cksum::Type cksum_type {rgw::cksum::Type::none};
 
   void encode(bufferlist& bl) const {
-    ENCODE_START(2, 1, bl);
+    ENCODE_START(3, 1, bl);
     encode(dest_placement, bl);
     encode(obj_retention_exist, bl);
     encode(obj_legal_hold_exist, bl);
     encode(obj_retention, bl);
     encode(obj_legal_hold, bl);
+    uint16_t ct{uint16_t(cksum_type)};
+    encode(ct, bl);
     ENCODE_FINISH(bl);
   }
 
   void decode(bufferlist::const_iterator& bl) {
-    DECODE_START(2, bl);
+    DECODE_START_LEGACY_COMPAT_LEN(3, 1, 1, bl);
     decode(dest_placement, bl);
     if (struct_v >= 2) {
       decode(obj_retention_exist, bl);
       decode(obj_legal_hold_exist, bl);
       decode(obj_retention, bl);
       decode(obj_legal_hold, bl);
+      if (struct_v >= 3) {
+       uint16_t ct;
+       decode(ct, bl);
+       cksum_type = rgw::cksum::Type(ct);
+      }
     } else {
       obj_retention_exist = false;
       obj_legal_hold_exist = false;
@@ -1985,6 +1992,16 @@ static inline std::string ys_header_mangle(std::string_view name)
   return out;
 } /* ys_header_mangle */
 
+static inline std::string& upcase_str(std::string& s) {
+  std::transform(s.begin(), s.end(), s.begin(), ::toupper);
+  return s;
+}
+
+static inline std::string safe_upcase_str(std::string s) {
+  std::transform(s.begin(), s.end(), s.begin(), ::toupper);
+  return s;
+}
+
 extern int rgw_bucket_parse_bucket_instance(const std::string& bucket_instance, std::string *bucket_name, std::string *bucket_id, int *shard_id);
 
 boost::intrusive_ptr<CephContext>
index 06e13c3890cfe97595512bda9c6291f5db677dbf..74461a69399f4fc0e606244f7c91105d4715d540 100644 (file)
@@ -1,10 +1,12 @@
 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
 // vim: ts=8 sw=2 smarttab ft=cpp
 
+#include <optional>
 #include "rgw_data_access.h"
 #include "rgw_acl_s3.h"
 #include "rgw_aio_throttle.h"
 #include "rgw_compression.h"
+#include "rgw_cksum.h"
 #include "common/BackTrace.h"
 
 #define dout_subsys ceph_subsys_rgw
@@ -208,12 +210,13 @@ int RGWDataAccess::Object::put(bufferlist& data,
 
   const req_context rctx{dpp, y, nullptr};
   return processor->complete(obj_size, etag,
-                           &mtime, mtime,
-                           attrs, delete_at,
-                            nullptr, nullptr,
-                            puser_data,
-                            nullptr, nullptr,
-                            rctx, rgw::sal::FLAG_LOG_OP);
+                            &mtime, mtime, attrs,
+                            rgw::cksum::no_cksum,
+                            delete_at,
+                            nullptr, nullptr,
+                            puser_data,
+                            nullptr, nullptr,
+                            rctx, rgw::sal::FLAG_LOG_OP);
 }
 
 void RGWDataAccess::Object::set_policy(const RGWAccessControlPolicy& policy)
index 66e883e7257a787b4f4a91885aa7164f05ebfee7..16d0047ff53360d9159b8e969b64a131283199dd 100644 (file)
@@ -2018,9 +2018,9 @@ namespace rgw {
     }
 
     op_ret = processor->complete(state->obj_size, etag, &mtime, real_time(), attrs,
-                                 (delete_at ? *delete_at : real_time()),
-                                if_match, if_nomatch, nullptr, nullptr, nullptr,
-                                rctx, rgw::sal::FLAG_LOG_OP);
+                                 rgw::cksum::no_cksum, (delete_at ? *delete_at : real_time()),
+                                if_match, if_nomatch, nullptr, nullptr, nullptr,
+                                rctx, rgw::sal::FLAG_LOG_OP);
     if (op_ret != 0) {
       /* revert attr updates */
       rgw_fh->set_mtime(omtime);
index 2f8e2abf916ebc81a7ef2f0ed32fc17eece4f843..94a780008faa4630db1284472640fa42fa8fbcd9 100644 (file)
@@ -2,6 +2,7 @@
 // vim: ts=8 sw=2 smarttab ft=cpp
 
 #include <errno.h>
+#include <optional>
 #include <stdlib.h>
 #include <system_error>
 #include <unistd.h>
@@ -24,6 +25,7 @@
 #include "common/ceph_json.h"
 #include "common/static_ptr.h"
 #include "common/perf_counters_key.h"
+#include "rgw_cksum.h"
 #include "rgw_common.h"
 #include "rgw_tracer.h"
 
@@ -4583,10 +4585,13 @@ void RGWPutObj::execute(optional_yield y)
 
   tracepoint(rgw_op, processor_complete_enter, s->req_id.c_str());
   const req_context rctx{this, s->yield, s->trace.get()};
-  op_ret = processor->complete(s->obj_size, etag, &mtime, real_time(), attrs,
-                               (delete_at ? *delete_at : real_time()), if_match, if_nomatch,
-                               (user_data.empty() ? nullptr : &user_data), nullptr, nullptr,
-                               rctx, complete_flags);
+
+  op_ret =
+    processor->complete(s->obj_size, etag, &mtime, real_time(), attrs,
+                       cksum, (delete_at ? *delete_at : real_time()),
+                       if_match, if_nomatch,
+                       (user_data.empty() ? nullptr : &user_data),
+                       nullptr, nullptr, rctx, complete_flags);
   tracepoint(rgw_op, processor_complete_exit, s->req_id.c_str());
   if (op_ret < 0) {
     return;
@@ -4598,7 +4603,7 @@ void RGWPutObj::execute(optional_yield y)
     ldpp_dout(this, 1) << "ERROR: publishing notification failed, with error: " << ret << dendl;
     // too late to rollback operation, hence op_ret is not set here
   }
-}
+} /* RGWPutObj::execute() */
 
 int RGWPostObj::init_processing(optional_yield y)
 {
@@ -4804,11 +4809,13 @@ void RGWPostObj::execute(optional_yield y)
       emplace_attr(RGW_ATTR_COMPRESSION, std::move(tmp));
     }
 
+    /* TODO:  implement POST checksums */
     const req_context rctx{this, s->yield, s->trace.get()};
-    op_ret = processor->complete(s->obj_size, etag, nullptr, real_time(), attrs,
-                                (delete_at ? *delete_at : real_time()),
-                                nullptr, nullptr, nullptr, nullptr, nullptr,
-                                rctx, rgw::sal::FLAG_LOG_OP);
+    op_ret = processor->complete(s->obj_size, etag, nullptr, real_time(),
+                                attrs, rgw::cksum::no_cksum,
+                                (delete_at ? *delete_at : real_time()),
+                                nullptr, nullptr, nullptr, nullptr, nullptr,
+                                rctx, rgw::sal::FLAG_LOG_OP);
     if (op_ret < 0) {
       return;
     }
@@ -6309,7 +6316,6 @@ void RGWInitMultipart::execute(optional_yield y)
 {
   multipart_trace = tracing::rgw::tracer.start_trace(tracing::rgw::MULTIPART, s->trace_enabled);
   bufferlist aclbl, tracebl;
-  rgw::sal::Attrs attrs;
 
   op_ret = get_params(y);
   if (op_ret < 0) {
@@ -6344,6 +6350,7 @@ void RGWInitMultipart::execute(optional_yield y)
                                       upload_id);
   upload->obj_legal_hold = obj_legal_hold;
   upload->obj_retention = obj_retention;
+  upload->cksum_type = cksum_algo;
   op_ret = upload->init(this, s->yield, s->owner, s->dest_placement, attrs);
 
   if (op_ret == 0) {
@@ -6376,9 +6383,110 @@ void RGWCompleteMultipart::pre_exec()
   rgw_bucket_object_pre_exec(s);
 }
 
+static inline int
+try_sum_part_cksums(const DoutPrefixProvider *dpp,
+                   CephContext *cct,
+                   rgw::sal::MultipartUpload* upload,
+                   RGWMultiCompleteUpload* parts,
+                   std::optional<rgw::cksum::Cksum>& out_cksum,
+                   optional_yield y)
+{
+  /* 1. need checksum-algorithm header (if invalid, fail)
+     2. conditional on have-checksum,
+     3. need digest for supplied algo
+     4. iterate over parts, confirm each has same algo, if not, fail
+     5. for each part-checksum, accumlate bytes into new checksum
+     6. return armored and append "-<nparts>"
+     7. verify -- if invalid, fail */
+
+    /* rgw_sal.h says that list_parts is called for the side effect of loading
+     * the parts of an upload into "cache"--the api is strange and truncated
+     * flag suggests that it needs to be called multiple times to handle large
+     * uploads--but does not explain how that affects the hidden cache;  I'm
+     * assuming it turns over? */
+
+  int op_ret = 0;
+  bool truncated = false;
+  int marker = 0;
+  auto num_parts = int(parts->parts.size());
+
+  rgw::cksum::Type& cksum_type = upload->cksum_type;
+
+  int again_count{0};
+ again:
+  op_ret = upload->list_parts(dpp, cct, num_parts, marker,
+                             &marker, &truncated, y);
+  if (op_ret < 0) {
+    return op_ret;
+  }
+
+  if (truncated) {
+    ldpp_dout(dpp, 20)
+      << fmt::format(
+        "WARNING: {} upload->list_parts {} {} truncated, again_count={}!",
+            __func__, num_parts, marker, again_count)
+      << dendl;
+    truncated = false;
+    ++again_count;
+    goto again;
+  }
+
+  if (cksum_type == rgw::cksum::Type::none) [[unlikely]] {
+    /* ordinary, no-checksum case */
+    return 0;
+  }
+
+  rgw::cksum::DigestVariant dv = rgw::cksum::digest_factory(cksum_type);
+  rgw::cksum::Digest* digest = rgw::cksum::get_digest(dv);
+
+  /* returns the parts (currently?) in cache */
+  auto parts_ix{0};
+  auto& parts_map = upload->get_parts();
+  for (auto& part : parts_map) {
+    ++parts_ix;
+    auto& part_cksum = part.second->get_cksum();
+    ldpp_dout(dpp, 16)
+      << fmt::format("INFO: {} iterate part: {} {} {}",
+                    __func__, parts_ix, part_cksum->type_string(),
+                    part_cksum->to_armor())
+      << dendl;
+    if ((part_cksum->type != cksum_type)) {
+      /* if parts have inconsistent checksum, fail now */
+      ldpp_dout(dpp, 4)
+       << fmt::format(
+              "ERROR: multipart part checksum type mismatch\n\tcomplete "
+              "multipart header={} part={}",
+              to_string(part_cksum->type), to_string(cksum_type))
+       << dendl;
+      op_ret = -ERR_INVALID_REQUEST;
+      return op_ret;
+    }
+
+    /* the checksum of the final object is a checksum (of the same type,
+     * presumably) of the concatenated checksum bytes of the parts, plus
+     * "-<num-parts>.  See
+     * https://docs.aws.amazon.com/AmazonS3/latest/userguide/checking-object-integrity.html#large-object-checksums
+     */
+    auto ckr = part_cksum->raw();
+    digest->Update((unsigned char *)ckr.data(), ckr.length());
+  } /* all-parts */
+
+  /* we cannot verify this checksum, only compute it */
+  out_cksum = rgw::cksum::finalize_digest(digest, cksum_type);
+
+  ldpp_dout(dpp, 16)
+    << fmt::format("INFO: {} combined checksum {} {}-{}",
+                  __func__,
+                  out_cksum->type_string(),
+                  out_cksum->to_armor(), num_parts)
+    << dendl;
+
+  return op_ret;
+} /* try_sum_part_chksums */
+
 void RGWCompleteMultipart::execute(optional_yield y)
 {
-  RGWMultiCompleteUpload *parts;
+  RGWMultiCompleteUploadparts;
   RGWMultiXMLParser parser;
   std::unique_ptr<rgw::sal::MultipartUpload> upload;
   off_t ofs = 0;
@@ -6418,7 +6526,6 @@ void RGWCompleteMultipart::execute(optional_yield y)
     return;
   }
 
-
   if ((int)parts->parts.size() >
       s->cct->_conf->rgw_multipart_part_upload_limit) {
     op_ret = -ERANGE;
@@ -6426,6 +6533,23 @@ void RGWCompleteMultipart::execute(optional_yield y)
   }
 
   upload = s->bucket->get_multipart_upload(s->object->get_name(), upload_id);
+  ldpp_dout(this, 16) <<
+    fmt::format("INFO: {}->get_multipart_upload for obj {}, {} cksum_type {}",
+               s->bucket->get_name(),
+               s->object->get_name(), upload_id,
+               (!!upload) ? to_string(upload->cksum_type) : 0)
+               << dendl;
+
+  rgw_placement_rule* dest_placement;
+  op_ret = upload->get_info(this, s->yield, &dest_placement);
+  if (op_ret < 0) {
+    /* XXX this fails inconsistently when !checksum */
+    ldpp_dout(this, 0) <<
+      "ERROR: MultipartUpload::get_info() for placement failed "
+                      << "ret=" << op_ret << dendl;
+    op_ret =  -ERR_INTERNAL_ERROR;
+    return;
+  }
 
   RGWCompressionInfo cs_info;
   bool compressed = false;
@@ -6437,8 +6561,8 @@ void RGWCompleteMultipart::execute(optional_yield y)
   meta_obj->set_in_extra_data(true);
   meta_obj->set_hash_source(s->object->get_name());
 
-  /*take a cls lock on meta_obj to prevent racing completions (or retries)
-    from deleting the parts*/
+  /* take a cls lock on meta_obj to prevent racing completions (or retries)
+     from deleting the parts*/
   int max_lock_secs_mp =
     s->cct->_conf.get_val<int64_t>("rgw_mp_lock_max_time");
   utime_t dur(max_lock_secs_mp, 0);
@@ -6468,6 +6592,14 @@ void RGWCompleteMultipart::execute(optional_yield y)
   extract_span_context(meta_obj->get_attrs(), trace_ctx);
   multipart_trace = tracing::rgw::tracer.add_span(name(), trace_ctx);
 
+  /* checksum computation */
+  if (upload->cksum_type != rgw::cksum::Type::none) {
+    op_ret = try_sum_part_cksums(this, s->cct, upload.get(), parts, cksum, y);
+    if (op_ret < 0) {
+      return;
+    }
+  }
+
   if (s->bucket->versioning_enabled()) {
     if (!version_id.empty()) {
       s->object->set_instance(version_id);
@@ -6476,17 +6608,47 @@ void RGWCompleteMultipart::execute(optional_yield y)
       version_id = s->object->get_instance();
     }
   }
-  s->object->set_attrs(meta_obj->get_attrs());
+
+  auto target_attrs = meta_obj->get_attrs();
+
+  if (cksum) {
+    armored_cksum =
+      fmt::format("{}-{}", cksum->to_armor(), parts->parts.size());
+
+    /* validate computed checksum against supplied checksum, if present */
+    auto [hdr_cksum, supplied_cksum] =
+      rgw::putobj::find_hdr_cksum(*(s->info.env));
+    ldpp_dout(this, 10) << fmt::format("INFO: client supplied checksum {}: {}",
+                                      hdr_cksum.header_name(), supplied_cksum)
+                       << dendl;
+
+    if (! (supplied_cksum.empty()) &&
+       (supplied_cksum != armored_cksum)) {
+      op_ret = -ERR_INVALID_REQUEST;
+      return;
+    }
+
+    buffer::list cksum_bl;
+    cksum->encode(cksum_bl);
+    target_attrs.emplace(RGW_ATTR_CKSUM, std::move(cksum_bl));
+  } /* cksum */
+
+  s->object->set_attrs(target_attrs);
 
   // make reservation for notification if needed
   std::unique_ptr<rgw::sal::Notification> res;
-  res = driver->get_notification(s->object.get(), nullptr, s, rgw::notify::ObjectCreatedCompleteMultipartUpload, y);
+  res = driver->get_notification(
+           s->object.get(), nullptr, s,
+           rgw::notify::ObjectCreatedCompleteMultipartUpload, y);
   op_ret = res->publish_reserve(this);
   if (op_ret < 0) {
     return;
   }
 
-  op_ret = upload->complete(this, y, s->cct, parts->parts, remove_objs, accounted_size, compressed, cs_info, ofs, s->req_id, s->owner, olh_epoch, s->object.get());
+  op_ret =
+    upload->complete(this, y, s->cct, parts->parts, remove_objs, accounted_size,
+                    compressed, cs_info, ofs, s->req_id, s->owner, olh_epoch,
+                    s->object.get());
   if (op_ret < 0) {
     ldpp_dout(this, 0) << "ERROR: upload complete failed ret=" << op_ret << dendl;
     return;
@@ -6533,8 +6695,9 @@ bool RGWCompleteMultipart::check_previously_completed(const RGWMultiCompleteUplo
     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(this, 20) << __func__ << "() re-calculating multipart etag: part: "
-                                   << index << ", etag: " << partetag << dendl;
+    ldpp_dout(this, 20)
+      << __func__ << "() re-calculating multipart etag: part: "
+      << index << ", etag: " << partetag << dendl;
   }
 
   unsigned char final_etag[CEPH_CRYPTO_MD5_DIGESTSIZE];
@@ -6651,6 +6814,21 @@ void RGWListMultipart::execute(optional_yield y)
   if (op_ret < 0)
     return;
 
+  iter = attrs.find(RGW_ATTR_CKSUM);
+  if (iter != attrs.end()) {
+    auto bliter = iter->second.cbegin();
+    try {
+      rgw::cksum::Cksum tcksum;
+      tcksum.decode(bliter);
+      cksum = std::move(tcksum);
+    } catch (buffer::error& err) {
+      ldpp_dout(this, 0) << "ERROR: could not decode stored cksum, caught buffer::error" << dendl;
+      op_ret = -EIO;
+    }
+  }
+  if (op_ret < 0)
+    return;
+
   op_ret = upload->list_parts(this, s->cct, max_parts, marker, NULL, &truncated, y);
 }
 
@@ -7464,12 +7642,15 @@ int RGWBulkUploadOp::handle_file(const std::string_view path,
     attrs.emplace(RGW_ATTR_COMPRESSION, std::move(tmp));
   }
 
+  /* XXX I don't think bulk upload can support checksums */
+
   /* Complete the transaction. */
   const req_context rctx{this, s->yield, s->trace.get()};
   op_ret = processor->complete(size, etag, nullptr, ceph::real_time(),
-                              attrs, ceph::real_time() /* delete_at */,
-                              nullptr, nullptr, nullptr, nullptr, nullptr,
-                              rctx, rgw::sal::FLAG_LOG_OP);
+                              attrs, rgw::cksum::no_cksum,
+                              ceph::real_time() /* delete_at */,
+                              nullptr, nullptr, nullptr, nullptr, nullptr,
+                              rctx, rgw::sal::FLAG_LOG_OP);
   if (op_ret < 0) {
     ldpp_dout(this, 20) << "processor::complete returned op_ret=" << op_ret << dendl;
   }
index 111fef519ace9ab146248ea9b23c3f77d3ee41ce..8f476fb8ff2ba87283309215b0e75a05fa507bcd 100644 (file)
@@ -1254,8 +1254,7 @@ protected:
   RGWObjectRetention *obj_retention;
   RGWObjectLegalHold *obj_legal_hold;
 
-  // optional cksum
-  boost::optional<rgw::cksum::Cksum> cksum;
+  std::optional<rgw::cksum::Cksum> cksum;
 
 public:
   RGWPutObj() : ofs(0),
@@ -1850,6 +1849,8 @@ protected:
   //object lock
   std::optional<RGWObjectRetention> obj_retention = std::nullopt;
   std::optional<RGWObjectLegalHold> obj_legal_hold = std::nullopt;
+  rgw::sal::Attrs attrs;
+  rgw::cksum::Type cksum_algo{rgw::cksum::Type::none};
 
 public:
   RGWInitMultipart() {}
@@ -1876,6 +1877,10 @@ protected:
   jspan_ptr multipart_trace;
   ceph::real_time upload_time;
   std::unique_ptr<rgw::sal::Notification> res;
+  std::unique_ptr<rgw::sal::Object> meta_obj;
+  std::optional<rgw::cksum::Cksum> cksum;
+  std::optional<std::string> armored_cksum;
+  off_t ofs = 0;
 
 public:
   RGWCompleteMultipart() {}
@@ -1919,6 +1924,7 @@ protected:
   RGWAccessControlPolicy policy;
   bool truncated;
   rgw_placement_rule* placement;
+  std::optional<rgw::cksum::Cksum> cksum;
 
 public:
   RGWListMultipart() {
@@ -2156,7 +2162,12 @@ inline int rgw_get_request_metadata(const DoutPrefixProvider *dpp,
       "x-amz-server-side-encryption-customer-algorithm",
       "x-amz-server-side-encryption-customer-key",
       "x-amz-server-side-encryption-customer-key-md5",
+      /* XXX agreed w/cbodley that probably a cleanup is needed here--we probably
+       * don't want to store these, esp. under user.rgw */
       "x-amz-storage-class",
+      "x-amz-content-sha256",
+      "x-amz-checksum-algorithm",
+      "x-amz-date"
   };
 
   size_t valid_meta_count = 0;
index 10e544b577df17017649b57ea8dc3db569019d33..8c93f77278b7d61f3dea79f70670f67259712296 100644 (file)
@@ -351,7 +351,7 @@ int process_request(const RGWProcessEnv& penv,
     goto done;
   }
   req->op = op;
-  ldpp_dout(op, 10) << "op=" << typeid(*op).name() << dendl;
+  ldpp_dout(op, 10) << "op=" << typeid(*op).name() << " " << dendl;
   s->op_type = op->get_type();
 
   try {
index 3dcedb1a28bc88d6bfe779d2e359009704b087f3..8a4218855a476d0435bb09fb9e879732067ec56b 100644 (file)
@@ -15,6 +15,8 @@
 #include "common/safe_io.h"
 #include "common/errno.h"
 #include "auth/Crypto.h"
+#include "rgw_cksum.h"
+#include "rgw_common.h"
 #include <boost/algorithm/string.hpp>
 #include <boost/algorithm/string/replace.hpp>
 #include <boost/algorithm/string/predicate.hpp>
@@ -68,7 +70,7 @@
 #include "rgw_rest_iam.h"
 #include "rgw_sts.h"
 #include "rgw_sal_rados.h"
-
+#include "rgw_cksum_pipe.h"
 #include "rgw_s3select.h"
 
 #define dout_context g_ceph_context
@@ -479,7 +481,7 @@ int RGWGetObj_ObjStore_S3::send_response_data(bufferlist& bl, off_t bl_ofs,
     } catch (const buffer::error&) {}
   }
 
-  if (multipart_parts_count) {
+  if (multipart_parts_count && multipart_parts_count > 0) {
     dump_header(s, "x-amz-mp-parts-count", *multipart_parts_count);
   }
 
@@ -502,7 +504,12 @@ int RGWGetObj_ObjStore_S3::send_response_data(bufferlist& bl, off_t bl_ofs,
        try {
          rgw::cksum::Cksum cksum;
          decode(cksum, i->second);
-         dump_header(s, cksum.header_name(), cksum.to_armor());
+         if (multipart_parts_count && multipart_parts_count > 0) {
+           dump_header(s, cksum.header_name(),
+                       fmt::format("{}-{}", cksum.to_armor(), *multipart_parts_count));
+         } else {
+           dump_header(s, cksum.header_name(), cksum.to_armor());
+         }
        }  catch (buffer::error& err) {
          ldpp_dout(this, 0) << "ERROR: failed to decode rgw::cksum::Cksum"
                             << dendl;
@@ -2733,7 +2740,7 @@ void RGWPutObj_ObjStore_S3::send_response()
       dump_content_length(s, 0);
       dump_header_if_nonempty(s, "x-amz-version-id", version_id);
       dump_header_if_nonempty(s, "x-amz-expiration", expires);
-      if (cksum) {
+      if (cksum && cksum->aws()) {
        dump_header(s, cksum->header_name(), cksum->to_armor());
       }
       for (auto &it : crypt_http_responses)
@@ -2962,6 +2969,7 @@ int RGWPostObj_ObjStore_S3::get_params(optional_yield y)
     std::string v { rgw_trim_whitespace(std::string_view(d.c_str(), d.length())) };
     rgw_set_amz_meta_header(s->info.crypt_attribute_map, p.first, v, OVERWRITE);
   }
+
   int r = get_encryption_defaults(s);
   if (r < 0) {
     ldpp_dout(this, 5) << __func__ << "(): get_encryption_defaults() returned ret=" << r << dendl;
@@ -4003,6 +4011,11 @@ int RGWInitMultipart_ObjStore_S3::get_params(optional_yield y)
     return -ERR_INVALID_REQUEST;
   }
 
+  auto algo_hdr = rgw::putobj::cksum_algorithm_hdr(*(s->info.env));
+  if (algo_hdr.second) {
+    cksum_algo = rgw::cksum::parse_cksum_type(algo_hdr.second);
+  }
+
   return 0;
 }
 
@@ -4020,6 +4033,9 @@ void RGWInitMultipart_ObjStore_S3::send_response()
     dump_time_header(s, "x-amz-abort-date", abort_date);
     dump_header_if_nonempty(s, "x-amz-abort-rule-id", rule_id);
   }
+  if (cksum_algo != rgw::cksum::Type::none) {
+    dump_header(s, "x-amz-checksum-algorithm", safe_upcase_str(to_string(cksum_algo)));
+  }
   end_header(s, this, to_mime_type(s->format));
   if (op_ret == 0) {
     dump_start(s);
@@ -4082,6 +4098,9 @@ void RGWCompleteMultipart_ObjStore_S3::send_response()
     s->formatter->dump_string("Bucket", s->bucket_name);
     s->formatter->dump_string("Key", s->object->get_name());
     s->formatter->dump_string("ETag", etag);
+    if (armored_cksum) {
+      s->formatter->dump_string(cksum->element_name(), *armored_cksum);
+    }
     s->formatter->close_section();
     rgw_flush_formatter_and_reset(s, s->formatter);
   }
@@ -4133,9 +4152,15 @@ void RGWListMultipart_ObjStore_S3::send_response()
     ACLOwner& owner = policy.get_owner();
     dump_owner(s, owner.id, owner.display_name);
 
+    /* TODO: missing initiator:
+       Container element that identifies who initiated the multipart upload. If the initiator is an AWS account, this element provides the same information as the Owner element. If the initiator is an IAM User, this element provides the user ARN and display name, see https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListParts.html */
+
+    if (cksum && cksum->aws()) {
+      s->formatter->dump_string("ChecksumAlgorithm", safe_upcase_str(cksum->type_string()));
+    }
+
     for (; iter != upload->get_parts().end(); ++iter) {
       rgw::sal::MultipartPart* part = iter->second.get();
-
       s->formatter->open_object_section("Part");
 
       dump_time(s, "LastModified", part->get_mtime());
@@ -4143,6 +4168,11 @@ void RGWListMultipart_ObjStore_S3::send_response()
       s->formatter->dump_unsigned("PartNumber", part->get_num());
       s->formatter->dump_format("ETag", "\"%s\"", part->get_etag().c_str());
       s->formatter->dump_unsigned("Size", part->get_size());
+      auto& part_cksum = part->get_cksum();
+      if (part_cksum && part_cksum->aws()) {
+       s->formatter->dump_string(part_cksum->element_name(),
+                                 fmt::format("{}", part_cksum->to_armor()));
+      }
       s->formatter->close_section();
     }
     s->formatter->close_section();
index 85c57ff700249b02603333d468f394d1644f8fa3..1701f1b3dc982268df7a482dcef0afdae0cdf526 100644 (file)
 
 #pragma once
 
+#include <optional>
 #include <boost/intrusive_ptr.hpp>
 #include <boost/smart_ptr/intrusive_ref_counter.hpp>
 
 #include "common/tracer.h"
+#include "rgw_cksum.h"
 #include "rgw_sal_fwd.h"
 #include "rgw_lua.h"
 #include "rgw_notify_event_type.h"
@@ -179,6 +181,7 @@ class ObjectProcessor : public DataProcessor {
   virtual int complete(size_t accounted_size, const std::string& etag,
                        ceph::real_time *mtime, ceph::real_time set_mtime,
                        std::map<std::string, bufferlist>& attrs,
+                      const std::optional<rgw::cksum::Cksum>& cksum,
                        ceph::real_time delete_at,
                        const char *if_match, const char *if_nomatch,
                        const std::string *user_data,
@@ -1343,6 +1346,8 @@ public:
   virtual const std::string& get_etag() = 0;
   /** Get the modification time of this part */
   virtual ceph::real_time& get_mtime() = 0;
+  /** Get computed (or default/empty) checksum */
+  virtual const std::optional<rgw::cksum::Cksum>& get_cksum() = 0;
 };
 
 /**
@@ -1359,6 +1364,7 @@ public:
   //object lock
   std::optional<RGWObjectRetention> obj_retention = std::nullopt;
   std::optional<RGWObjectLegalHold> obj_legal_hold = std::nullopt;
+  rgw::cksum::Type cksum_type = rgw::cksum::Type::none;
 
   MultipartUpload() = default;
   virtual ~MultipartUpload() = default;
@@ -1616,6 +1622,7 @@ public:
   virtual int complete(size_t accounted_size, const std::string& etag,
                        ceph::real_time *mtime, ceph::real_time set_mtime,
                        std::map<std::string, bufferlist>& attrs,
+                      const std::optional<rgw::cksum::Cksum>& cksum,
                        ceph::real_time delete_at,
                        const char *if_match, const char *if_nomatch,
                        const std::string *user_data,
index 4b27bd35f99ab24e03d02f61abc1728fd6b845dd..cb62ebecceedeb81a78363bfb1a92c1ade5b7320 100644 (file)
@@ -1210,9 +1210,11 @@ namespace rgw::sal {
     return 0;
   }
 
-  int DBMultipartWriter::complete(size_t accounted_size, const std::string& etag,
+  int DBMultipartWriter::complete(
+                      size_t accounted_size, const std::string& etag,
                        ceph::real_time *mtime, ceph::real_time set_mtime,
                        std::map<std::string, bufferlist>& attrs,
+                      const std::optional<rgw::cksum::Cksum>& cksum,
                        ceph::real_time delete_at,
                        const char *if_match, const char *if_nomatch,
                        const std::string *user_data,
@@ -1234,6 +1236,7 @@ namespace rgw::sal {
     RGWUploadPartInfo info;
     info.num = part_num;
     info.etag = etag;
+    info.cksum = cksum;
     info.size = total_data_size;
     info.accounted_size = accounted_size;
     info.modified = real_clock::now();
@@ -1368,6 +1371,7 @@ namespace rgw::sal {
   int DBAtomicWriter::complete(size_t accounted_size, const std::string& etag,
                          ceph::real_time *mtime, ceph::real_time set_mtime,
                          std::map<std::string, bufferlist>& attrs,
+                        const std::optional<rgw::cksum::Cksum>& cksum,
                          ceph::real_time delete_at,
                          const char *if_match, const char *if_nomatch,
                          const std::string *user_data,
index defa5c2c50d82fef3eb1732883af4ca5a7beeea7..2dfc340315de78c3d9ab70d9e04fe9e61629b2b4 100644 (file)
@@ -363,7 +363,9 @@ protected:
     virtual uint64_t get_size() { return info.accounted_size; }
     virtual const std::string& get_etag() { return info.etag; }
     virtual ceph::real_time& get_mtime() { return info.modified; }
-
+    virtual const std::optional<rgw::cksum::Cksum>& get_cksum() {
+      return info.cksum;
+    }
   };
 
   class DBMPObj {
@@ -633,6 +635,7 @@ protected:
     virtual int complete(size_t accounted_size, const std::string& etag,
                          ceph::real_time *mtime, ceph::real_time set_mtime,
                          std::map<std::string, bufferlist>& attrs,
+                        const std::optional<rgw::cksum::Cksum>& cksum,
                          ceph::real_time delete_at,
                          const char *if_match, const char *if_nomatch,
                          const std::string *user_data,
@@ -682,6 +685,7 @@ public:
     virtual int complete(size_t accounted_size, const std::string& etag,
                        ceph::real_time *mtime, ceph::real_time set_mtime,
                        std::map<std::string, bufferlist>& attrs,
+                      const std::optional<rgw::cksum::Cksum>& cksum,
                        ceph::real_time delete_at,
                        const char *if_match, const char *if_nomatch,
                        const std::string *user_data,
index 391d75662499245ba4659bfd35b618e602110fc2..8408620ba5c95c49a16cdcbeba13f9867dfa61f5 100644 (file)
@@ -1442,6 +1442,7 @@ int FilterWriter::process(bufferlist&& data, uint64_t offset)
 int FilterWriter::complete(size_t accounted_size, const std::string& etag,
                        ceph::real_time *mtime, ceph::real_time set_mtime,
                        std::map<std::string, bufferlist>& attrs,
+                      const std::optional<rgw::cksum::Cksum>& cksum,
                        ceph::real_time delete_at,
                        const char *if_match, const char *if_nomatch,
                        const std::string *user_data,
@@ -1449,7 +1450,7 @@ int FilterWriter::complete(size_t accounted_size, const std::string& etag,
                        const req_context& rctx,
                        uint32_t flags)
 {
-  return next->complete(accounted_size, etag, mtime, set_mtime, attrs,
+  return next->complete(accounted_size, etag, mtime, set_mtime, attrs, cksum,
                        delete_at, if_match, if_nomatch, user_data, zones_trace,
                        canceled, rctx, flags);
 }
index 6ff3004189540f89f9e520b76c4a66c23ae45871..664b37fa6a9dd7fee6d570c1ed9808eb9941ad16 100644 (file)
@@ -878,6 +878,9 @@ public:
   virtual uint64_t get_size() override { return next->get_size(); }
   virtual const std::string& get_etag() override { return next->get_etag(); }
   virtual ceph::real_time& get_mtime() override { return next->get_mtime(); }
+  virtual const std::optional<rgw::cksum::Cksum>& get_cksum() {
+    return next->get_cksum();
+  }
 };
 
 class FilterMultipartUpload : public MultipartUpload {
@@ -1048,6 +1051,7 @@ public:
   virtual int complete(size_t accounted_size, const std::string& etag,
                        ceph::real_time *mtime, ceph::real_time set_mtime,
                        std::map<std::string, bufferlist>& attrs,
+                      const std::optional<rgw::cksum::Cksum>& cksum,
                        ceph::real_time delete_at,
                        const char *if_match, const char *if_nomatch,
                        const std::string *user_data,
index 603197d41f9d877ea2b748078768516f698033d9..d410ea27506354b9e869e6971874ee865798ce1e 100644 (file)
@@ -24,6 +24,7 @@
 #include "common/ceph_argparse.h"
 #include "common/debug.h"
 #include "rgw/rgw_cksum.h"
+#include "rgw/rgw_cksum_pipe.h"
 #include <openssl/sha.h>
 #include "rgw/rgw_hex.h"
 
@@ -50,6 +51,17 @@ namespace {
   std::string dolor =
     R"(Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.)";
 
+TEST(RGWCksum, Ctor)
+{
+  cksum::Cksum ck1;
+  cksum::Cksum ck2(cksum::Type::none);
+
+  auto ck3 = rgw::putobj::GetHeaderCksumResult(ck1, "");
+
+  ASSERT_EQ(ck1.to_armor(), ck2.to_armor());
+  ASSERT_EQ(ck2.to_armor(), ck3.first.to_armor());
+}
+
 TEST(RGWCksum, Output)
 {
   auto o_mode = std::ios::out|std::ios::trunc;