]>
Commit | Line | Data |
---|---|---|
adb31ebb TL |
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 | #include "rgw_etag_verifier.h" | |
20effc67 | 5 | #include "rgw_obj_manifest.h" |
adb31ebb TL |
6 | |
7 | #define dout_subsys ceph_subsys_rgw | |
8 | ||
9 | namespace rgw::putobj { | |
10 | ||
b3b6e05e | 11 | int create_etag_verifier(const DoutPrefixProvider *dpp, |
20effc67 | 12 | CephContext* cct, rgw::sal::DataProcessor* filter, |
adb31ebb TL |
13 | const bufferlist& manifest_bl, |
14 | const std::optional<RGWCompressionInfo>& compression, | |
15 | etag_verifier_ptr& verifier) | |
16 | { | |
17 | RGWObjManifest manifest; | |
18 | ||
19 | try { | |
20 | auto miter = manifest_bl.cbegin(); | |
21 | decode(manifest, miter); | |
22 | } catch (buffer::error& err) { | |
b3b6e05e | 23 | ldpp_dout(dpp, 0) << "ERROR: couldn't decode manifest" << dendl; |
adb31ebb TL |
24 | return -EIO; |
25 | } | |
26 | ||
27 | RGWObjManifestRule rule; | |
28 | bool found = manifest.get_rule(0, &rule); | |
29 | if (!found) { | |
b3b6e05e | 30 | ldpp_dout(dpp, -1) << "ERROR: manifest->get_rule() could not find rule" << dendl; |
adb31ebb TL |
31 | return -EIO; |
32 | } | |
33 | ||
f67539c2 | 34 | if (rule.start_part_num == 0) { |
adb31ebb TL |
35 | /* Atomic object */ |
36 | verifier.emplace<ETagVerifier_Atomic>(cct, filter); | |
37 | return 0; | |
38 | } | |
39 | ||
40 | uint64_t cur_part_ofs = UINT64_MAX; | |
41 | std::vector<uint64_t> part_ofs; | |
42 | ||
43 | /* | |
44 | * We must store the offset of each part to calculate the ETAGs for each | |
45 | * MPU part. These part ETags then become the input for the MPU object | |
46 | * Etag. | |
47 | */ | |
b3b6e05e | 48 | for (auto mi = manifest.obj_begin(dpp); mi != manifest.obj_end(dpp); ++mi) { |
adb31ebb TL |
49 | if (cur_part_ofs == mi.get_part_ofs()) |
50 | continue; | |
51 | cur_part_ofs = mi.get_part_ofs(); | |
b3b6e05e | 52 | ldpp_dout(dpp, 20) << "MPU Part offset:" << cur_part_ofs << dendl; |
adb31ebb TL |
53 | part_ofs.push_back(cur_part_ofs); |
54 | } | |
55 | ||
56 | if (compression) { | |
57 | // if the source object was compressed, the manifest is storing | |
58 | // compressed part offsets. transform the compressed offsets back to | |
59 | // their original offsets by finding the first block of each part | |
60 | const auto& blocks = compression->blocks; | |
61 | auto block = blocks.begin(); | |
62 | for (auto& ofs : part_ofs) { | |
63 | // find the compression_block with new_ofs == ofs | |
64 | constexpr auto less = [] (const compression_block& block, uint64_t ofs) { | |
65 | return block.new_ofs < ofs; | |
66 | }; | |
67 | block = std::lower_bound(block, blocks.end(), ofs, less); | |
68 | if (block == blocks.end() || block->new_ofs != ofs) { | |
b3b6e05e | 69 | ldpp_dout(dpp, 4) << "no match for compressed offset " << ofs |
adb31ebb TL |
70 | << ", disabling etag verification" << dendl; |
71 | return -EIO; | |
72 | } | |
73 | ofs = block->old_ofs; | |
b3b6e05e | 74 | ldpp_dout(dpp, 20) << "MPU Part uncompressed offset:" << ofs << dendl; |
adb31ebb TL |
75 | } |
76 | } | |
77 | ||
78 | verifier.emplace<ETagVerifier_MPU>(cct, std::move(part_ofs), filter); | |
79 | return 0; | |
80 | } | |
81 | ||
82 | int ETagVerifier_Atomic::process(bufferlist&& in, uint64_t logical_offset) | |
83 | { | |
84 | bufferlist out; | |
85 | if (in.length() > 0) | |
86 | hash.Update((const unsigned char *)in.c_str(), in.length()); | |
87 | ||
88 | return Pipe::process(std::move(in), logical_offset); | |
89 | } | |
90 | ||
91 | void ETagVerifier_Atomic::calculate_etag() | |
92 | { | |
93 | unsigned char m[CEPH_CRYPTO_MD5_DIGESTSIZE]; | |
94 | char calc_md5[CEPH_CRYPTO_MD5_DIGESTSIZE * 2 + 1]; | |
95 | ||
96 | /* Return early if ETag has already been calculated */ | |
97 | if (!calculated_etag.empty()) | |
98 | return; | |
99 | ||
100 | hash.Final(m); | |
101 | buf_to_hex(m, CEPH_CRYPTO_MD5_DIGESTSIZE, calc_md5); | |
102 | calculated_etag = calc_md5; | |
103 | ldout(cct, 20) << "Single part object: " << " etag:" << calculated_etag | |
104 | << dendl; | |
105 | } | |
106 | ||
107 | void ETagVerifier_MPU::process_end_of_MPU_part() | |
108 | { | |
109 | unsigned char m[CEPH_CRYPTO_MD5_DIGESTSIZE]; | |
110 | char calc_md5_part[CEPH_CRYPTO_MD5_DIGESTSIZE * 2 + 1]; | |
111 | std::string calculated_etag_part; | |
112 | ||
113 | hash.Final(m); | |
114 | mpu_etag_hash.Update((const unsigned char *)m, sizeof(m)); | |
115 | hash.Restart(); | |
116 | ||
117 | if (cct->_conf->subsys.should_gather(dout_subsys, 20)) { | |
118 | buf_to_hex(m, CEPH_CRYPTO_MD5_DIGESTSIZE, calc_md5_part); | |
119 | calculated_etag_part = calc_md5_part; | |
120 | ldout(cct, 20) << "Part etag: " << calculated_etag_part << dendl; | |
121 | } | |
122 | ||
123 | cur_part_index++; | |
124 | next_part_index++; | |
125 | } | |
126 | ||
127 | int ETagVerifier_MPU::process(bufferlist&& in, uint64_t logical_offset) | |
128 | { | |
129 | uint64_t bl_end = in.length() + logical_offset; | |
130 | ||
131 | /* Handle the last MPU part */ | |
f67539c2 | 132 | if (size_t(next_part_index) == part_ofs.size()) { |
adb31ebb TL |
133 | hash.Update((const unsigned char *)in.c_str(), in.length()); |
134 | goto done; | |
135 | } | |
136 | ||
137 | /* Incoming bufferlist spans two MPU parts. Calculate separate ETags */ | |
138 | if (bl_end > part_ofs[next_part_index]) { | |
139 | ||
140 | uint64_t part_one_len = part_ofs[next_part_index] - logical_offset; | |
141 | hash.Update((const unsigned char *)in.c_str(), part_one_len); | |
142 | process_end_of_MPU_part(); | |
143 | ||
144 | hash.Update((const unsigned char *)in.c_str() + part_one_len, | |
145 | bl_end - part_ofs[cur_part_index]); | |
146 | /* | |
147 | * If we've moved to the last part of the MPU, avoid usage of | |
148 | * parts_ofs[next_part_index] as it will lead to our-of-range access. | |
149 | */ | |
f67539c2 | 150 | if (size_t(next_part_index) == part_ofs.size()) |
adb31ebb TL |
151 | goto done; |
152 | } else { | |
153 | hash.Update((const unsigned char *)in.c_str(), in.length()); | |
154 | } | |
155 | ||
156 | /* Update the MPU Etag if the current part has ended */ | |
157 | if (logical_offset + in.length() + 1 == part_ofs[next_part_index]) | |
158 | process_end_of_MPU_part(); | |
159 | ||
160 | done: | |
161 | return Pipe::process(std::move(in), logical_offset); | |
162 | } | |
163 | ||
164 | void ETagVerifier_MPU::calculate_etag() | |
165 | { | |
f67539c2 TL |
166 | const uint32_t parts = part_ofs.size(); |
167 | constexpr auto digits10 = std::numeric_limits<uint32_t>::digits10; | |
168 | constexpr auto extra = 2 + digits10; // add "-%u\0" at the end | |
169 | ||
adb31ebb | 170 | unsigned char m[CEPH_CRYPTO_MD5_DIGESTSIZE], mpu_m[CEPH_CRYPTO_MD5_DIGESTSIZE]; |
f67539c2 | 171 | char final_etag_str[CEPH_CRYPTO_MD5_DIGESTSIZE * 2 + extra]; |
adb31ebb TL |
172 | |
173 | /* Return early if ETag has already been calculated */ | |
174 | if (!calculated_etag.empty()) | |
175 | return; | |
176 | ||
177 | hash.Final(m); | |
178 | mpu_etag_hash.Update((const unsigned char *)m, sizeof(m)); | |
179 | ||
180 | /* Refer RGWCompleteMultipart::execute() for ETag calculation for MPU object */ | |
181 | mpu_etag_hash.Final(mpu_m); | |
182 | buf_to_hex(mpu_m, CEPH_CRYPTO_MD5_DIGESTSIZE, final_etag_str); | |
183 | snprintf(&final_etag_str[CEPH_CRYPTO_MD5_DIGESTSIZE * 2], | |
184 | sizeof(final_etag_str) - CEPH_CRYPTO_MD5_DIGESTSIZE * 2, | |
f67539c2 | 185 | "-%u", parts); |
adb31ebb TL |
186 | |
187 | calculated_etag = final_etag_str; | |
188 | ldout(cct, 20) << "MPU calculated ETag:" << calculated_etag << dendl; | |
189 | } | |
190 | ||
191 | } // namespace rgw::putobj |