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