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