Skip to content

Commit ee03b50

Browse files
committed
rgw:cksum: implement crc64nvme and combined 32- and 64-bit CRCs
* internally compensate for at-rest byteswapped crc64 representation (e.g., before combine step) * generalize Cksum crc api for 32bit and 64bit crcs, other cleanup * prototype abstract cksum::Combiner workflow * add support for forward and backward handling of composite vs full object checksums by marking the composites and the update flag day * clean up checksum formatting and checksum type reporting * add unit tests for Combiner interface * validate and track requested checksum type (i.e., composite or full), plus unit test fixture for combinations of full matrix of cksum types * doh. GET/HEAD checksums are in headers * add crcany license to COPYING * return ChecksumType as header in GET/HEAD Found by Casey in review * avoid fmt of char* null when no checksum header supplied A cksum_hdr_t(nullptr, nullptr) results in this case, and is intended, but its components obviously can't be presented to std::format unless cast to a safe pointer type. * fail checksum mismatch with BadDigest When uploading an S3 object with an invalid checksum, the return code should be BadDigest to mirror more closely the AWS S3 implementation. See: https://docs.aws.amazon.com/AmazonS3/latest/userguide/checking-object-integrity.html Fixes: https://tracker.ceph.com/issues/70614 Reported by Alex Wojno <[email protected]> * fix comparison and display of composite checksums A string comparision bounds error and a bitmask comparison error are fixed. * fix build on centos9 On gcc(?13?) we can't declare rgw::cksum::FLAG_NONE and also rgw::cksum::Cksum::FlAG_NONE. SAD!! * include <variant> invariantly * fix checksum type return from complete-multipart This one is in the XML response * aieee, don't leak Combiners Use unique_ptr to polymorphic type...correctly. Signed-off-by: Matt Benjamin <[email protected]>
1 parent a1aed60 commit ee03b50

File tree

15 files changed

+1117
-82
lines changed

15 files changed

+1117
-82
lines changed

COPYING

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,3 +224,26 @@ License: GNU Affero General Public License, Version 3
224224
Files: src/common/*s390x*
225225
Copyright: 2024 IBM <[email protected]>
226226
License: Apache License, version 2.0
227+
228+
Files: src/rgw/madler/*
229+
License:
230+
Copyright (C) 2014-2025 Mark Adler
231+
232+
This software is provided 'as-is', without any express or implied warranty.
233+
In no event will the authors be held liable for any damages arising from the
234+
use of this software.
235+
236+
Permission is granted to anyone to use this software for any purpose,
237+
including commercial applications, and to alter it and redistribute it
238+
freely, subject to the following restrictions:
239+
240+
1. The origin of this software must not be misrepresented; you must not claim
241+
that you wrote the original software. If you use this software in a
242+
product, an acknowledgment in the product documentation would be
243+
appreciated but is not required.
244+
2. Altered source versions must be plainly marked as such, and must not be
245+
misrepresented as being the original software.
246+
3. This notice may not be removed or altered from any source distribution.
247+
248+
Mark Adler
249+

src/rgw/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ set(librgw_common_srcs
5757
services/svc_zone.cc
5858
services/svc_zone_utils.cc
5959
spdk/crc64.c
60+
madler/crc64nvme.c
61+
madler/crc32iso_hdlc.c
62+
madler/crc32iscsi.c
6063
rgw_account.cc
6164
rgw_acl.cc
6265
rgw_acl_s3.cc
@@ -71,6 +74,7 @@ set(librgw_common_srcs
7174
rgw_bucket.cc
7275
rgw_bucket_layout.cc
7376
rgw_cache.cc
77+
rgw_cksum.cc
7478
rgw_cksum_pipe.cc
7579
rgw_common.cc
7680
rgw_compression.cc

src/rgw/driver/rados/rgw_sal_rados.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3841,6 +3841,7 @@ int RadosMultipartUpload::init(const DoutPrefixProvider *dpp, optional_yield y,
38413841
multipart_upload_info upload_info;
38423842
upload_info.dest_placement = dest_placement;
38433843
upload_info.cksum_type = cksum_type;
3844+
upload_info.cksum_flags = cksum_flags;
38443845

38453846
if (obj_legal_hold) {
38463847
upload_info.obj_legal_hold_exist = true;
@@ -4256,6 +4257,7 @@ int RadosMultipartUpload::get_info(const DoutPrefixProvider *dpp, optional_yield
42564257
return -EIO;
42574258
}
42584259
cksum_type = upload_info.cksum_type;
4260+
cksum_flags = upload_info.cksum_flags;
42594261
placement = upload_info.dest_placement;
42604262
upload_information = upload_info;
42614263
*rule = &placement;

src/rgw/rgw_cksum.cc

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2+
// vim: ts=8 sw=2 smarttab ft=cpp
3+
4+
/*
5+
* Ceph - scalable distributed file system
6+
*
7+
* Copyright contributors to the Ceph project
8+
*
9+
* This is free software; you can redistribute it and/or
10+
* modify it under the terms of the GNU Lesser General Public
11+
* License version 2.1, as published by the Free Software
12+
* Foundation. See file COPYING.
13+
*
14+
*/
15+
16+
#include "rgw_cksum.h"
17+
#include "rgw_cksum_digest.h"
18+
#include <cstdint>
19+
#include <memory>
20+
#include "rgw_crc_digest.h"
21+
22+
extern "C" {
23+
#include "madler/crc64nvme.h"
24+
#include "madler/crc32iso_hdlc.h"
25+
#include "madler/crc32iscsi.h"
26+
#include "spdk/crc64.h"
27+
} // extern "C"
28+
29+
namespace rgw::cksum {
30+
31+
std::optional<Cksum>
32+
combine_crc_cksum(const Cksum& ck1, const Cksum& ck2, uintmax_t len1)
33+
{
34+
std::optional<rgw::cksum::Cksum> ck3;
35+
if ((ck1.type != ck2.type) ||
36+
!ck1.crc()) {
37+
goto out;
38+
}
39+
40+
switch(ck1.type) {
41+
case cksum::Type::crc64nvme:
42+
{
43+
/* due to AWS (and other) convention, the at-rest
44+
* digest is byteswapped (on LE?); restore the
45+
* defined byte order before combining */
46+
auto cck1 =
47+
rgw::digest::byteswap(std::get<uint64_t>(*ck1.get_crc()));
48+
auto cck2 =
49+
rgw::digest::byteswap(std::get<uint64_t>(*ck2.get_crc()));
50+
/* madler crcany */
51+
auto cck3 = crc64nvme_comb(cck1, cck2, len1);
52+
/* and byteswap */
53+
cck3 = rgw::digest::byteswap(cck3);
54+
/* convert to a Cksum, no ascii armor */
55+
ck3 = Cksum(ck1.type, (char*) &cck3, Cksum::CtorStyle::raw);
56+
}
57+
break;
58+
case cksum::Type::crc32:
59+
case cksum::Type::crc32c:
60+
{
61+
uint32_t cck3;
62+
auto cck1 =
63+
rgw::digest::byteswap(std::get<uint32_t>(*ck1.get_crc()));
64+
auto cck2 =
65+
rgw::digest::byteswap(std::get<uint32_t>(*ck2.get_crc()));
66+
/* madler crcany */
67+
switch (ck1.type) {
68+
case cksum::Type::crc32:
69+
cck3 = crc32iso_hdlc_comb(cck1, cck2, len1);
70+
break;
71+
case cksum::Type::crc32c:
72+
cck3 = crc32iscsi_comb(cck1, cck2, len1);
73+
break;
74+
default:
75+
break;
76+
}
77+
/* and byteswap */
78+
cck3 = rgw::digest::byteswap(cck3);
79+
/* convert to a Cksum, no ascii armor */
80+
ck3 = Cksum(ck1.type, (char*) &cck3, Cksum::CtorStyle::raw);
81+
}
82+
break;
83+
default:
84+
break;
85+
};
86+
87+
out:
88+
return ck3;
89+
}
90+
91+
/* the checksum of the final object is a checksum (of the same type,
92+
* presumably) of the concatenated checksum bytes of the parts, plus
93+
* "-<num-parts>. See
94+
* https://docs.aws.amazon.com/AmazonS3/latest/userguide/checking-object-integrity.html#large-object-checksums
95+
*/
96+
class DigestCombiner : public Combiner
97+
{
98+
DigestVariant dv;
99+
Digest* digest{nullptr};
100+
101+
public:
102+
DigestCombiner(cksum::Type t)
103+
: Combiner(t),
104+
dv(digest_factory(t)),
105+
digest(get_digest(dv))
106+
{}
107+
virtual void append(const Cksum& cksum, uint64_t ignored) {
108+
auto ckr = cksum.raw();
109+
digest->Update((unsigned char *)ckr.data(), ckr.length());
110+
}
111+
virtual Cksum final() {
112+
auto cksum = finalize_digest(digest, get_type());
113+
cksum.flags |= Cksum::COMPOSITE_MASK;
114+
return cksum;
115+
}
116+
}; /* Digest */
117+
118+
/* the checksum of the final object is an "ordinary" (full object) checksum,
119+
* synthesized from the CRCs of the component checksums
120+
*/
121+
class CRCCombiner : public Combiner
122+
{
123+
Cksum cksum;
124+
bool once{false};
125+
public:
126+
CRCCombiner(cksum::Type t)
127+
: Combiner(t)
128+
{}
129+
virtual void append(const Cksum& rhs, uint64_t part_size) {
130+
if (! once) {
131+
// save the first part checksum
132+
cksum = rhs;
133+
once = true;
134+
} else {
135+
// combine the next partial checksum into cksum
136+
cksum = *combine_crc_cksum(cksum, rhs, part_size);
137+
}
138+
}
139+
virtual Cksum final() {
140+
cksum.flags |= Cksum::FULL_OBJECT_MASK;
141+
return cksum;
142+
}
143+
}; /* CRCCombine */
144+
145+
std::unique_ptr<Combiner> CombinerFactory(cksum::Type t, uint16_t flags)
146+
{
147+
switch(t) {
148+
case cksum::Type::crc32:
149+
case cksum::Type::crc32c:
150+
case cksum::Type::crc64nvme:
151+
return std::unique_ptr<Combiner>(new CRCCombiner(t));
152+
default:
153+
return std::unique_ptr<Combiner>(new DigestCombiner(t));
154+
};
155+
}
156+
157+
} // namespace rgw::cksum

0 commit comments

Comments
 (0)