]> git.proxmox.com Git - ceph.git/blame - ceph/src/rgw/rgw_lc_s3.cc
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / rgw / rgw_lc_s3.cc
CommitLineData
11fdf7f2 1// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
9f95a23c 2// vim: ts=8 sw=2 smarttab ft=cpp
11fdf7f2 3
7c673cae
FG
4#include <string.h>
5
6#include <iostream>
7#include <map>
8
9#include "include/types.h"
10
11#include "rgw_user.h"
12#include "rgw_lc_s3.h"
13
14
15#define dout_subsys ceph_subsys_rgw
16
20effc67
TL
17using namespace std;
18
11fdf7f2
TL
19static bool check_date(const string& _date)
20{
21 boost::optional<ceph::real_time> date = ceph::from_iso_8601(_date);
22 if (boost::none == date) {
7c673cae 23 return false;
31f18b77 24 }
11fdf7f2
TL
25 struct timespec time = ceph::real_clock::to_timespec(*date);
26 if (time.tv_sec % (24*60*60) || time.tv_nsec) {
27 return false;
31f18b77 28 }
7c673cae
FG
29 return true;
30}
31
11fdf7f2
TL
32void LCExpiration_S3::dump_xml(Formatter *f) const {
33 if (dm_expiration) {
34 encode_xml("ExpiredObjectDeleteMarker", "true", f);
35 } else if (!days.empty()) {
36 encode_xml("Days", days, f);
37 } else {
38 encode_xml("Date", date, f);
7c673cae 39 }
7c673cae
FG
40}
41
11fdf7f2
TL
42void LCExpiration_S3::decode_xml(XMLObj *obj)
43{
44 bool has_days = RGWXMLDecoder::decode_xml("Days", days, obj);
45 bool has_date = RGWXMLDecoder::decode_xml("Date", date, obj);
46 string dm;
47 bool has_dm = RGWXMLDecoder::decode_xml("ExpiredObjectDeleteMarker", dm, obj);
48
49 int num = !!has_days + !!has_date + !!has_dm;
50
51 if (num != 1) {
52 throw RGWXMLDecoder::err("bad Expiration section");
53 }
54
55 if (has_date && !check_date(date)) {
56 //We need return xml error according to S3
57 throw RGWXMLDecoder::err("bad date in Date section");
58 }
59
60 if (has_dm) {
61 dm_expiration = (dm == "true");
7c673cae 62 }
7c673cae
FG
63}
64
11fdf7f2
TL
65void LCNoncurExpiration_S3::decode_xml(XMLObj *obj)
66{
67 RGWXMLDecoder::decode_xml("NoncurrentDays", days, obj, true);
68}
69
70void LCNoncurExpiration_S3::dump_xml(Formatter *f) const
71{
72 encode_xml("NoncurrentDays", days, f);
73}
74
75void LCMPExpiration_S3::decode_xml(XMLObj *obj)
76{
77 RGWXMLDecoder::decode_xml("DaysAfterInitiation", days, obj, true);
78}
79
80void LCMPExpiration_S3::dump_xml(Formatter *f) const
81{
82 encode_xml("DaysAfterInitiation", days, f);
83}
84
85void RGWLifecycleConfiguration_S3::decode_xml(XMLObj *obj)
86{
87 if (!cct) {
88 throw RGWXMLDecoder::err("ERROR: RGWLifecycleConfiguration_S3 can't be decoded without cct initialized");
89 }
90 vector<LCRule_S3> rules;
91
92 RGWXMLDecoder::decode_xml("Rule", rules, obj, true);
93
94 for (auto& rule : rules) {
95 if (rule.get_id().empty()) {
11fdf7f2
TL
96 // S3 generates a 48 bit random ID, maybe we could generate shorter IDs
97 static constexpr auto LC_ID_LENGTH = 48;
9f95a23c 98 string id = gen_rand_alphanumeric_lower(cct, LC_ID_LENGTH);
11fdf7f2
TL
99 rule.set_id(id);
100 }
101
7c673cae 102 add_rule(rule);
7c673cae 103 }
11fdf7f2 104
91327a77 105 if (cct->_conf->rgw_lc_max_rules < rule_map.size()) {
11fdf7f2
TL
106 stringstream ss;
107 ss << "Warn: The lifecycle config has too many rules, rule number is:"
108 << rule_map.size() << ", max number is:" << cct->_conf->rgw_lc_max_rules;
109 throw RGWXMLDecoder::err(ss.str());
91327a77 110 }
7c673cae
FG
111}
112
11fdf7f2
TL
113void LCFilter_S3::dump_xml(Formatter *f) const
114{
11fdf7f2
TL
115 bool multi = has_multi_condition();
116 if (multi) {
117 f->open_array_section("And");
118 }
1e59de90
TL
119 if (has_prefix()) {
120 encode_xml("Prefix", prefix, f);
121 }
11fdf7f2
TL
122 if (has_tags()) {
123 const auto& tagset_s3 = static_cast<const RGWObjTagSet_S3 &>(obj_tags);
124 tagset_s3.dump_xml(f);
125 }
1e59de90
TL
126 if (has_flags()) {
127 if (have_flag(LCFlagType::ArchiveZone)) {
128 encode_xml("ArchiveZone", "", f);
129 }
130 }
11fdf7f2 131 if (multi) {
1e59de90 132 f->close_section(); // And
11fdf7f2
TL
133 }
134}
7c673cae 135
11fdf7f2
TL
136void LCFilter_S3::decode_xml(XMLObj *obj)
137{
f67539c2
TL
138 /*
139 * The prior logic here looked for an And element, but did not
140 * structurally parse the Filter clause (and incorrectly rejected
141 * the base case where a Prefix and one Tag were supplied). It
142 * could not reject generally malformed Filter syntax.
143 *
144 * Empty filters are allowed:
145 * https://docs.aws.amazon.com/AmazonS3/latest/dev/intro-lifecycle-rules.html
146 */
1e59de90 147 XMLObj* o = obj->find_first("And");
11fdf7f2
TL
148 if (o == nullptr){
149 o = obj;
11fdf7f2 150 }
181888fb 151
11fdf7f2 152 RGWXMLDecoder::decode_xml("Prefix", prefix, o);
1e59de90
TL
153
154 /* parse optional ArchiveZone flag (extension) */
155 if (o->find_first("ArchiveZone")) {
156 flags |= make_flag(LCFlagType::ArchiveZone);
157 }
158
159 obj_tags.clear(); // why is this needed?
11fdf7f2 160 auto tags_iter = o->find("Tag");
1e59de90 161 while (auto tag_xml = tags_iter.get_next()){
11fdf7f2
TL
162 std::string _key,_val;
163 RGWXMLDecoder::decode_xml("Key", _key, tag_xml);
164 RGWXMLDecoder::decode_xml("Value", _val, tag_xml);
165 obj_tags.emplace_tag(std::move(_key), std::move(_val));
11fdf7f2
TL
166 }
167}
181888fb 168
11fdf7f2
TL
169void LCTransition_S3::decode_xml(XMLObj *obj)
170{
171 bool has_days = RGWXMLDecoder::decode_xml("Days", days, obj);
172 bool has_date = RGWXMLDecoder::decode_xml("Date", date, obj);
173 if ((has_days && has_date) || (!has_days && !has_date)) {
174 throw RGWXMLDecoder::err("bad Transition section");
175 }
181888fb 176
11fdf7f2
TL
177 if (has_date && !check_date(date)) {
178 //We need return xml error according to S3
179 throw RGWXMLDecoder::err("bad Date in Transition section");
180 }
181
182 if (!RGWXMLDecoder::decode_xml("StorageClass", storage_class, obj)) {
183 throw RGWXMLDecoder::err("missing StorageClass in Transition section");
184 }
185}
186
187void LCTransition_S3::dump_xml(Formatter *f) const {
188 if (!days.empty()) {
189 encode_xml("Days", days, f);
181888fb 190 } else {
11fdf7f2
TL
191 encode_xml("Date", date, f);
192 }
193 encode_xml("StorageClass", storage_class, f);
194}
195
196void LCNoncurTransition_S3::decode_xml(XMLObj *obj)
197{
198 if (!RGWXMLDecoder::decode_xml("NoncurrentDays", days, obj)) {
199 throw RGWXMLDecoder::err("missing NoncurrentDays in NoncurrentVersionTransition section");
200 }
201 if (!RGWXMLDecoder::decode_xml("StorageClass", storage_class, obj)) {
202 throw RGWXMLDecoder::err("missing StorageClass in NoncurrentVersionTransition section");
203 }
204}
205
206void LCNoncurTransition_S3::dump_xml(Formatter *f) const
207{
208 encode_xml("NoncurrentDays", days, f);
209 encode_xml("StorageClass", storage_class, f);
210}
211
212void LCRule_S3::decode_xml(XMLObj *obj)
213{
214 id.clear();
215 prefix.clear();
216 status.clear();
217 dm_expiration = false;
218
219 RGWXMLDecoder::decode_xml("ID", id, obj);
220
221 LCFilter_S3 filter_s3;
222 if (!RGWXMLDecoder::decode_xml("Filter", filter_s3, obj)) {
181888fb
FG
223 // Ideally the following code should be deprecated and we should return
224 // False here, The new S3 LC configuration xml spec. makes Filter mandatory
225 // and Prefix optional. However older clients including boto2 still generate
226 // xml according to the older spec, where Prefix existed outside of Filter
227 // and S3 itself seems to be sloppy on enforcing the mandatory Filter
228 // argument. A day will come when S3 enforces their own xml-spec, but it is
229 // not this day
230
11fdf7f2
TL
231 if (!RGWXMLDecoder::decode_xml("Prefix", prefix, obj)) {
232 throw RGWXMLDecoder::err("missing Prefix in Filter");
181888fb 233 }
11fdf7f2
TL
234 }
235 filter = (LCFilter)filter_s3;
181888fb 236
11fdf7f2
TL
237 if (!RGWXMLDecoder::decode_xml("Status", status, obj)) {
238 throw RGWXMLDecoder::err("missing Status in Filter");
239 }
240 if (status.compare("Enabled") != 0 && status.compare("Disabled") != 0) {
241 throw RGWXMLDecoder::err("bad Status in Filter");
181888fb 242 }
7c673cae 243
11fdf7f2
TL
244 LCExpiration_S3 s3_expiration;
245 LCNoncurExpiration_S3 s3_noncur_expiration;
246 LCMPExpiration_S3 s3_mp_expiration;
247 LCFilter_S3 s3_filter;
7c673cae 248
11fdf7f2
TL
249 bool has_expiration = RGWXMLDecoder::decode_xml("Expiration", s3_expiration, obj);
250 bool has_noncur_expiration = RGWXMLDecoder::decode_xml("NoncurrentVersionExpiration", s3_noncur_expiration, obj);
251 bool has_mp_expiration = RGWXMLDecoder::decode_xml("AbortIncompleteMultipartUpload", s3_mp_expiration, obj);
7c673cae 252
11fdf7f2
TL
253 vector<LCTransition_S3> transitions;
254 vector<LCNoncurTransition_S3> noncur_transitions;
255
256 bool has_transition = RGWXMLDecoder::decode_xml("Transition", transitions, obj);
257 bool has_noncur_transition = RGWXMLDecoder::decode_xml("NoncurrentVersionTransition", noncur_transitions, obj);
258
259 if (!has_expiration &&
260 !has_noncur_expiration &&
261 !has_mp_expiration &&
262 !has_transition &&
263 !has_noncur_transition) {
264 throw RGWXMLDecoder::err("bad Rule");
265 }
266
267 if (has_expiration) {
268 if (s3_expiration.has_days() ||
269 s3_expiration.has_date()) {
270 expiration = s3_expiration;
271 } else {
272 dm_expiration = s3_expiration.get_dm_expiration();
7c673cae 273 }
11fdf7f2
TL
274 }
275 if (has_noncur_expiration) {
276 noncur_expiration = s3_noncur_expiration;
277 }
278 if (has_mp_expiration) {
279 mp_expiration = s3_mp_expiration;
280 }
281 for (auto& t : transitions) {
282 if (!add_transition(t)) {
283 throw RGWXMLDecoder::err("Failed to add transition");
7c673cae 284 }
11fdf7f2
TL
285 }
286 for (auto& t : noncur_transitions) {
287 if (!add_noncur_transition(t)) {
288 throw RGWXMLDecoder::err("Failed to add non-current version transition");
7c673cae
FG
289 }
290 }
7c673cae
FG
291}
292
11fdf7f2
TL
293void LCRule_S3::dump_xml(Formatter *f) const {
294 encode_xml("ID", id, f);
295 // In case of an empty filter and an empty Prefix, we defer to Prefix.
181888fb 296 if (!filter.empty()) {
11fdf7f2
TL
297 const LCFilter_S3& lc_filter = static_cast<const LCFilter_S3&>(filter);
298 encode_xml("Filter", lc_filter, f);
181888fb 299 } else {
11fdf7f2 300 encode_xml("Prefix", prefix, f);
181888fb 301 }
11fdf7f2 302 encode_xml("Status", status, f);
31f18b77 303 if (!expiration.empty() || dm_expiration) {
224ce89b 304 LCExpiration_S3 expir(expiration.get_days_str(), expiration.get_date(), dm_expiration);
11fdf7f2 305 encode_xml("Expiration", expir, f);
7c673cae
FG
306 }
307 if (!noncur_expiration.empty()) {
11fdf7f2
TL
308 const LCNoncurExpiration_S3& noncur_expir = static_cast<const LCNoncurExpiration_S3&>(noncur_expiration);
309 encode_xml("NoncurrentVersionExpiration", noncur_expir, f);
7c673cae
FG
310 }
311 if (!mp_expiration.empty()) {
11fdf7f2
TL
312 const LCMPExpiration_S3& mp_expir = static_cast<const LCMPExpiration_S3&>(mp_expiration);
313 encode_xml("AbortIncompleteMultipartUpload", mp_expir, f);
314 }
315 if (!transitions.empty()) {
316 for (auto &elem : transitions) {
317 const LCTransition_S3& tran = static_cast<const LCTransition_S3&>(elem.second);
318 encode_xml("Transition", tran, f);
319 }
320 }
321 if (!noncur_transitions.empty()) {
322 for (auto &elem : noncur_transitions) {
323 const LCNoncurTransition_S3& noncur_tran = static_cast<const LCNoncurTransition_S3&>(elem.second);
324 encode_xml("NoncurrentVersionTransition", noncur_tran, f);
325 }
7c673cae 326 }
7c673cae
FG
327}
328
f67539c2 329int RGWLifecycleConfiguration_S3::rebuild(RGWLifecycleConfiguration& dest)
7c673cae
FG
330{
331 int ret = 0;
332 multimap<string, LCRule>::iterator iter;
333 for (iter = rule_map.begin(); iter != rule_map.end(); ++iter) {
334 LCRule& src_rule = iter->second;
11fdf7f2 335 ret = dest.check_and_add_rule(src_rule);
7c673cae
FG
336 if (ret < 0)
337 return ret;
338 }
224ce89b 339 if (!dest.valid()) {
7c673cae
FG
340 ret = -ERR_INVALID_REQUEST;
341 }
342 return ret;
343}
344
11fdf7f2 345
7c673cae
FG
346void RGWLifecycleConfiguration_S3::dump_xml(Formatter *f) const
347{
11fdf7f2
TL
348 for (auto iter = rule_map.begin(); iter != rule_map.end(); ++iter) {
349 const LCRule_S3& rule = static_cast<const LCRule_S3&>(iter->second);
350 encode_xml("Rule", rule, f);
351 }
7c673cae
FG
352}
353