]> git.proxmox.com Git - ceph.git/blame - ceph/src/rgw/rgw_rest_swift.cc
bump version to 18.2.2-pve1
[ceph.git] / ceph / src / rgw / rgw_rest_swift.cc
CommitLineData
7c673cae 1// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
9f95a23c 2// vim: ts=8 sw=2 smarttab ft=cpp
7c673cae
FG
3
4#include <boost/algorithm/string/predicate.hpp>
3efd9988 5#include <boost/format.hpp>
7c673cae
FG
6#include <boost/optional.hpp>
7#include <boost/utility/in_place_factory.hpp>
8
11fdf7f2 9#include "include/ceph_assert.h"
7c673cae
FG
10#include "ceph_ver.h"
11
12#include "common/Formatter.h"
13#include "common/utf8.h"
14#include "common/ceph_json.h"
15
16#include "rgw_rest_swift.h"
17#include "rgw_acl_swift.h"
18#include "rgw_cors_swift.h"
19#include "rgw_formats.h"
20#include "rgw_client_io.h"
9f95a23c 21#include "rgw_compression.h"
7c673cae
FG
22
23#include "rgw_auth.h"
1e59de90 24#include "rgw_auth_registry.h"
7c673cae
FG
25#include "rgw_swift_auth.h"
26
27#include "rgw_request.h"
28#include "rgw_process.h"
29
11fdf7f2 30#include "rgw_zone.h"
20effc67 31#include "rgw_sal.h"
11fdf7f2
TL
32
33#include "services/svc_zone.h"
34
7c673cae 35#include <array>
f67539c2 36#include <string_view>
7c673cae
FG
37#include <sstream>
38#include <memory>
39
7c673cae
FG
40#define dout_context g_ceph_context
41#define dout_subsys ceph_subsys_rgw
42
20effc67
TL
43using namespace std;
44
f67539c2 45int RGWListBuckets_ObjStore_SWIFT::get_params(optional_yield y)
7c673cae
FG
46{
47 prefix = s->info.args.get("prefix");
48 marker = s->info.args.get("marker");
49 end_marker = s->info.args.get("end_marker");
3efd9988 50 wants_reversed = s->info.args.exists("reverse");
7c673cae 51
3efd9988
FG
52 if (wants_reversed) {
53 std::swap(marker, end_marker);
54 }
55
56 std::string limit_str = s->info.args.get("limit");
7c673cae 57 if (!limit_str.empty()) {
3efd9988 58 std::string err;
7c673cae
FG
59 long l = strict_strtol(limit_str.c_str(), 10, &err);
60 if (!err.empty()) {
61 return -EINVAL;
62 }
63
64 if (l > (long)limit_max || l < 0) {
65 return -ERR_PRECONDITION_FAILED;
66 }
67
68 limit = (uint64_t)l;
69 }
70
224ce89b 71 if (s->cct->_conf->rgw_swift_need_stats) {
7c673cae
FG
72 bool stats, exists;
73 int r = s->info.args.get_bool("stats", &stats, &exists);
74
75 if (r < 0) {
76 return r;
77 }
78
79 if (exists) {
80 need_stats = stats;
81 }
224ce89b
WB
82 } else {
83 need_stats = false;
7c673cae
FG
84 }
85
86 return 0;
87}
88
1e59de90 89static void dump_account_metadata(req_state * const s,
3efd9988 90 const RGWUsageStats& global_stats,
11fdf7f2 91 const std::map<std::string, RGWUsageStats> &policies_stats,
7c673cae
FG
92 /* const */map<string, bufferlist>& attrs,
93 const RGWQuotaInfo& quota,
94 const RGWAccessControlPolicy_SWIFTAcct &policy)
95{
96 /* Adding X-Timestamp to keep align with Swift API */
97 dump_header(s, "X-Timestamp", ceph_clock_now());
98
3efd9988
FG
99 dump_header(s, "X-Account-Container-Count", global_stats.buckets_count);
100 dump_header(s, "X-Account-Object-Count", global_stats.objects_count);
101 dump_header(s, "X-Account-Bytes-Used", global_stats.bytes_used);
102 dump_header(s, "X-Account-Bytes-Used-Actual", global_stats.bytes_used_rounded);
103
104 for (const auto& kv : policies_stats) {
105 const auto& policy_name = camelcase_dash_http_attr(kv.first);
106 const auto& policy_stats = kv.second;
107
108 dump_header_infixed(s, "X-Account-Storage-Policy-", policy_name,
109 "-Container-Count", policy_stats.buckets_count);
110 dump_header_infixed(s, "X-Account-Storage-Policy-", policy_name,
111 "-Object-Count", policy_stats.objects_count);
112 dump_header_infixed(s, "X-Account-Storage-Policy-", policy_name,
113 "-Bytes-Used", policy_stats.bytes_used);
114 dump_header_infixed(s, "X-Account-Storage-Policy-", policy_name,
115 "-Bytes-Used-Actual", policy_stats.bytes_used_rounded);
116 }
7c673cae
FG
117
118 /* Dump TempURL-related stuff */
119 if (s->perm_mask == RGW_PERM_FULL_CONTROL) {
9f95a23c
TL
120 auto iter = s->user->get_info().temp_url_keys.find(0);
121 if (iter != std::end(s->user->get_info().temp_url_keys) && ! iter->second.empty()) {
7c673cae
FG
122 dump_header(s, "X-Account-Meta-Temp-Url-Key", iter->second);
123 }
124
9f95a23c
TL
125 iter = s->user->get_info().temp_url_keys.find(1);
126 if (iter != std::end(s->user->get_info().temp_url_keys) && ! iter->second.empty()) {
7c673cae
FG
127 dump_header(s, "X-Account-Meta-Temp-Url-Key-2", iter->second);
128 }
129 }
130
131 /* Dump quota headers. */
132 if (quota.enabled) {
133 if (quota.max_size >= 0) {
134 dump_header(s, "X-Account-Meta-Quota-Bytes", quota.max_size);
135 }
136
137 /* Limit on the number of objects in a given account is a RadosGW's
138 * extension. Swift's account quota WSGI filter doesn't support it. */
139 if (quota.max_objects >= 0) {
140 dump_header(s, "X-Account-Meta-Quota-Count", quota.max_objects);
141 }
142 }
143
144 /* Dump user-defined metadata items and generic attrs. */
145 const size_t PREFIX_LEN = sizeof(RGW_ATTR_META_PREFIX) - 1;
146 map<string, bufferlist>::iterator iter;
147 for (iter = attrs.lower_bound(RGW_ATTR_PREFIX); iter != attrs.end(); ++iter) {
148 const char *name = iter->first.c_str();
149 map<string, string>::const_iterator geniter = rgw_to_http_attrs.find(name);
150
151 if (geniter != rgw_to_http_attrs.end()) {
152 dump_header(s, geniter->second, iter->second);
153 } else if (strncmp(name, RGW_ATTR_META_PREFIX, PREFIX_LEN) == 0) {
154 dump_header_prefixed(s, "X-Account-Meta-",
155 camelcase_dash_http_attr(name + PREFIX_LEN),
156 iter->second);
157 }
158 }
159
160 /* Dump account ACLs */
224ce89b
WB
161 auto account_acls = policy.to_str();
162 if (account_acls) {
163 dump_header(s, "X-Account-Access-Control", std::move(*account_acls));
7c673cae
FG
164 }
165}
166
167void RGWListBuckets_ObjStore_SWIFT::send_response_begin(bool has_buckets)
168{
169 if (op_ret) {
170 set_req_state_err(s, op_ret);
1e59de90 171 } else if (!has_buckets && s->format == RGWFormat::PLAIN) {
7c673cae
FG
172 op_ret = STATUS_NO_CONTENT;
173 set_req_state_err(s, op_ret);
174 }
175
176 if (! s->cct->_conf->rgw_swift_enforce_content_length) {
177 /* Adding account stats in the header to keep align with Swift API */
178 dump_account_metadata(s,
3efd9988
FG
179 global_stats,
180 policies_stats,
20effc67 181 s->user->get_attrs(),
1e59de90 182 s->user->get_info().quota.user_quota,
7c673cae
FG
183 static_cast<RGWAccessControlPolicy_SWIFTAcct&>(*s->user_acl));
184 dump_errno(s);
11fdf7f2 185 dump_header(s, "Accept-Ranges", "bytes");
7c673cae
FG
186 end_header(s, NULL, NULL, NO_CONTENT_LENGTH, true);
187 }
188
189 if (! op_ret) {
190 dump_start(s);
191 s->formatter->open_array_section_with_attrs("account",
9f95a23c 192 FormatterAttrs("name", s->user->get_display_name().c_str(), NULL));
7c673cae
FG
193
194 sent_data = true;
195 }
196}
197
20effc67 198void RGWListBuckets_ObjStore_SWIFT::handle_listing_chunk(rgw::sal::BucketList&& buckets)
3efd9988
FG
199{
200 if (wants_reversed) {
201 /* Just store in the reversal buffer. Its content will be handled later,
202 * in send_response_end(). */
203 reverse_buffer.emplace(std::begin(reverse_buffer), std::move(buckets));
204 } else {
205 return send_response_data(buckets);
206 }
207}
208
20effc67 209void RGWListBuckets_ObjStore_SWIFT::send_response_data(rgw::sal::BucketList& buckets)
7c673cae
FG
210{
211 if (! sent_data) {
212 return;
213 }
214
215 /* Take care of the prefix parameter of Swift API. There is no business
216 * in applying the filter earlier as we really need to go through all
217 * entries regardless of it (the headers like X-Account-Container-Count
218 * aren't affected by specifying prefix). */
f67539c2 219 const auto& m = buckets.get_buckets();
7c673cae
FG
220 for (auto iter = m.lower_bound(prefix);
221 iter != m.end() && boost::algorithm::starts_with(iter->first, prefix);
222 ++iter) {
9f95a23c 223 dump_bucket_entry(*iter->second);
3efd9988
FG
224 }
225}
7c673cae 226
20effc67 227void RGWListBuckets_ObjStore_SWIFT::dump_bucket_entry(const rgw::sal::Bucket& bucket)
3efd9988
FG
228{
229 s->formatter->open_object_section("container");
20effc67 230 s->formatter->dump_string("name", bucket.get_name());
3efd9988
FG
231
232 if (need_stats) {
20effc67
TL
233 s->formatter->dump_int("count", bucket.get_count());
234 s->formatter->dump_int("bytes", bucket.get_size());
3efd9988
FG
235 }
236
237 s->formatter->close_section();
238
239 if (! s->cct->_conf->rgw_swift_enforce_content_length) {
240 rgw_flush_formatter(s, s->formatter);
241 }
242}
243
20effc67 244void RGWListBuckets_ObjStore_SWIFT::send_response_data_reversed(rgw::sal::BucketList& buckets)
3efd9988
FG
245{
246 if (! sent_data) {
247 return;
248 }
249
250 /* Take care of the prefix parameter of Swift API. There is no business
251 * in applying the filter earlier as we really need to go through all
252 * entries regardless of it (the headers like X-Account-Container-Count
253 * aren't affected by specifying prefix). */
f67539c2 254 auto& m = buckets.get_buckets();
3efd9988
FG
255
256 auto iter = m.rbegin();
257 for (/* initialized above */;
258 iter != m.rend() && !boost::algorithm::starts_with(iter->first, prefix);
259 ++iter) {
260 /* NOP */;
261 }
262
263 for (/* iter carried */;
264 iter != m.rend() && boost::algorithm::starts_with(iter->first, prefix);
265 ++iter) {
9f95a23c 266 dump_bucket_entry(*iter->second);
7c673cae
FG
267 }
268}
269
270void RGWListBuckets_ObjStore_SWIFT::send_response_end()
271{
3efd9988
FG
272 if (wants_reversed) {
273 for (auto& buckets : reverse_buffer) {
274 send_response_data_reversed(buckets);
275 }
276 }
277
7c673cae
FG
278 if (sent_data) {
279 s->formatter->close_section();
280 }
281
282 if (s->cct->_conf->rgw_swift_enforce_content_length) {
283 /* Adding account stats in the header to keep align with Swift API */
284 dump_account_metadata(s,
3efd9988
FG
285 global_stats,
286 policies_stats,
20effc67 287 s->user->get_attrs(),
1e59de90 288 s->user->get_info().quota.user_quota,
7c673cae
FG
289 static_cast<RGWAccessControlPolicy_SWIFTAcct&>(*s->user_acl));
290 dump_errno(s);
3efd9988 291 end_header(s, nullptr, nullptr, s->formatter->get_len(), true);
7c673cae
FG
292 }
293
294 if (sent_data || s->cct->_conf->rgw_swift_enforce_content_length) {
295 rgw_flush_formatter_and_reset(s, s->formatter);
296 }
297}
298
f67539c2 299int RGWListBucket_ObjStore_SWIFT::get_params(optional_yield y)
7c673cae
FG
300{
301 prefix = s->info.args.get("prefix");
302 marker = s->info.args.get("marker");
303 end_marker = s->info.args.get("end_marker");
304 max_keys = s->info.args.get("limit");
1adf2230
AA
305
306 // non-standard
307 s->info.args.get_bool("allow_unordered", &allow_unordered, false);
308
309 delimiter = s->info.args.get("delimiter");
310
7c673cae
FG
311 op_ret = parse_max_keys();
312 if (op_ret < 0) {
313 return op_ret;
314 }
f64942e4
AA
315 // S3 behavior is to silently cap the max-keys.
316 // Swift behavior is to abort.
7c673cae
FG
317 if (max > default_max)
318 return -ERR_PRECONDITION_FAILED;
319
7c673cae
FG
320 string path_args;
321 if (s->info.args.exists("path")) { // should handle empty path
322 path_args = s->info.args.get("path");
323 if (!delimiter.empty() || !prefix.empty()) {
324 return -EINVAL;
325 }
326 prefix = path_args;
327 delimiter="/";
328
329 path = prefix;
330 if (path.size() && path[path.size() - 1] != '/')
331 path.append("/");
332
333 int len = prefix.size();
334 int delim_size = delimiter.size();
335
336 if (len >= delim_size) {
337 if (prefix.substr(len - delim_size).compare(delimiter) != 0)
338 prefix.append(delimiter);
339 }
340 }
341
342 return 0;
343}
344
1e59de90 345static void dump_container_metadata(req_state *,
20effc67 346 const rgw::sal::Bucket*,
7c673cae
FG
347 const RGWQuotaInfo&,
348 const RGWBucketWebsiteConf&);
349
350void RGWListBucket_ObjStore_SWIFT::send_response()
351{
352 vector<rgw_bucket_dir_entry>::iterator iter = objs.begin();
353 map<string, bool>::iterator pref_iter = common_prefixes.begin();
354
355 dump_start(s);
1e59de90 356 dump_container_metadata(s, s->bucket.get(), quota.bucket_quota,
f67539c2 357 s->bucket->get_info().website_conf);
7c673cae 358
1adf2230
AA
359 s->formatter->open_array_section_with_attrs("container",
360 FormatterAttrs("name",
f67539c2 361 s->bucket->get_name().c_str(),
1adf2230 362 NULL));
7c673cae
FG
363
364 while (iter != objs.end() || pref_iter != common_prefixes.end()) {
365 bool do_pref = false;
366 bool do_objs = false;
367 rgw_obj_key key;
368 if (iter != objs.end()) {
369 key = iter->key;
370 }
371 if (pref_iter == common_prefixes.end())
372 do_objs = true;
373 else if (iter == objs.end())
374 do_pref = true;
375 else if (!key.empty() && key.name.compare(pref_iter->first) == 0) {
376 do_objs = true;
377 ++pref_iter;
378 } else if (!key.empty() && key.name.compare(pref_iter->first) <= 0)
379 do_objs = true;
380 else
381 do_pref = true;
382
1adf2230 383 if (do_objs && (allow_unordered || marker.empty() || marker < key)) {
7c673cae
FG
384 if (key.name.compare(path) == 0)
385 goto next;
386
387 s->formatter->open_object_section("object");
388 s->formatter->dump_string("name", key.name);
389 s->formatter->dump_string("hash", iter->meta.etag);
390 s->formatter->dump_int("bytes", iter->meta.accounted_size);
391 if (!iter->meta.user_data.empty())
392 s->formatter->dump_string("user_custom_data", iter->meta.user_data);
393 string single_content_type = iter->meta.content_type;
394 if (iter->meta.content_type.size()) {
395 // content type might hold multiple values, just dump the last one
396 ssize_t pos = iter->meta.content_type.rfind(',');
397 if (pos > 0) {
398 ++pos;
399 while (single_content_type[pos] == ' ')
400 ++pos;
401 single_content_type = single_content_type.substr(pos);
402 }
403 s->formatter->dump_string("content_type", single_content_type);
404 }
20effc67 405 dump_time(s, "last_modified", iter->meta.mtime);
7c673cae
FG
406 s->formatter->close_section();
407 }
408
409 if (do_pref && (marker.empty() || pref_iter->first.compare(marker.name) > 0)) {
410 const string& name = pref_iter->first;
411 if (name.compare(delimiter) == 0)
412 goto next;
413
414 s->formatter->open_object_section_with_attrs("subdir", FormatterAttrs("name", name.c_str(), NULL));
415
416 /* swift is a bit inconsistent here */
417 switch (s->format) {
1e59de90 418 case RGWFormat::XML:
7c673cae
FG
419 s->formatter->dump_string("name", name);
420 break;
421 default:
422 s->formatter->dump_string("subdir", name);
423 }
424 s->formatter->close_section();
425 }
426next:
427 if (do_objs)
428 ++iter;
429 else
430 ++pref_iter;
431 }
432
433 s->formatter->close_section();
434
435 int64_t content_len = 0;
436 if (! op_ret) {
437 content_len = s->formatter->get_len();
438 if (content_len == 0) {
439 op_ret = STATUS_NO_CONTENT;
440 }
441 } else if (op_ret > 0) {
442 op_ret = 0;
443 }
444
445 set_req_state_err(s, op_ret);
446 dump_errno(s);
447 end_header(s, this, NULL, content_len);
448 if (op_ret < 0) {
449 return;
450 }
451
452 rgw_flush_formatter_and_reset(s, s->formatter);
1adf2230 453} // RGWListBucket_ObjStore_SWIFT::send_response
7c673cae 454
1e59de90 455static void dump_container_metadata(req_state *s,
20effc67 456 const rgw::sal::Bucket* bucket,
7c673cae
FG
457 const RGWQuotaInfo& quota,
458 const RGWBucketWebsiteConf& ws_conf)
459{
460 /* Adding X-Timestamp to keep align with Swift API */
f67539c2 461 dump_header(s, "X-Timestamp", utime_t(s->bucket->get_info().creation_time));
7c673cae 462
9f95a23c
TL
463 dump_header(s, "X-Container-Object-Count", bucket->get_count());
464 dump_header(s, "X-Container-Bytes-Used", bucket->get_size());
465 dump_header(s, "X-Container-Bytes-Used-Actual", bucket->get_size_rounded());
7c673cae 466
20effc67 467 if (rgw::sal::Object::empty(s->object.get())) {
c07f9fc5
FG
468 auto swift_policy = \
469 static_cast<RGWAccessControlPolicy_SWIFT*>(s->bucket_acl.get());
7c673cae
FG
470 std::string read_acl, write_acl;
471 swift_policy->to_str(read_acl, write_acl);
472
473 if (read_acl.size()) {
474 dump_header(s, "X-Container-Read", read_acl);
475 }
476 if (write_acl.size()) {
477 dump_header(s, "X-Container-Write", write_acl);
478 }
f67539c2
TL
479 if (!s->bucket->get_placement_rule().name.empty()) {
480 dump_header(s, "X-Storage-Policy", s->bucket->get_placement_rule().name);
7c673cae 481 }
f67539c2 482 dump_header(s, "X-Storage-Class", s->bucket->get_placement_rule().get_storage_class());
7c673cae
FG
483
484 /* Dump user-defined metadata items and generic attrs. */
485 const size_t PREFIX_LEN = sizeof(RGW_ATTR_META_PREFIX) - 1;
486 map<string, bufferlist>::iterator iter;
487 for (iter = s->bucket_attrs.lower_bound(RGW_ATTR_PREFIX);
488 iter != s->bucket_attrs.end();
489 ++iter) {
490 const char *name = iter->first.c_str();
491 map<string, string>::const_iterator geniter = rgw_to_http_attrs.find(name);
492
493 if (geniter != rgw_to_http_attrs.end()) {
494 dump_header(s, geniter->second, iter->second);
495 } else if (strncmp(name, RGW_ATTR_META_PREFIX, PREFIX_LEN) == 0) {
496 dump_header_prefixed(s, "X-Container-Meta-",
497 camelcase_dash_http_attr(name + PREFIX_LEN),
498 iter->second);
499 }
500 }
501 }
502
503 /* Dump container versioning info. */
f67539c2 504 if (! s->bucket->get_info().swift_ver_location.empty()) {
7c673cae 505 dump_header(s, "X-Versions-Location",
f67539c2 506 url_encode(s->bucket->get_info().swift_ver_location));
7c673cae
FG
507 }
508
509 /* Dump quota headers. */
510 if (quota.enabled) {
511 if (quota.max_size >= 0) {
512 dump_header(s, "X-Container-Meta-Quota-Bytes", quota.max_size);
513 }
514
515 if (quota.max_objects >= 0) {
516 dump_header(s, "X-Container-Meta-Quota-Count", quota.max_objects);
517 }
518 }
519
520 /* Dump Static Website headers. */
521 if (! ws_conf.index_doc_suffix.empty()) {
522 dump_header(s, "X-Container-Meta-Web-Index", ws_conf.index_doc_suffix);
523 }
524
525 if (! ws_conf.error_doc.empty()) {
526 dump_header(s, "X-Container-Meta-Web-Error", ws_conf.error_doc);
527 }
528
529 if (! ws_conf.subdir_marker.empty()) {
530 dump_header(s, "X-Container-Meta-Web-Directory-Type",
531 ws_conf.subdir_marker);
532 }
533
534 if (! ws_conf.listing_css_doc.empty()) {
535 dump_header(s, "X-Container-Meta-Web-Listings-CSS",
536 ws_conf.listing_css_doc);
537 }
538
539 if (ws_conf.listing_enabled) {
540 dump_header(s, "X-Container-Meta-Web-Listings", "true");
541 }
11fdf7f2
TL
542
543 /* Dump bucket's modification time. Compliance with the Swift API really
544 * needs that. */
545 dump_last_modified(s, s->bucket_mtime);
7c673cae
FG
546}
547
f67539c2 548void RGWStatAccount_ObjStore_SWIFT::execute(optional_yield y)
7c673cae 549{
f67539c2 550 RGWStatAccount_ObjStore::execute(y);
20effc67
TL
551 op_ret = s->user->read_attrs(s, s->yield);
552 attrs = s->user->get_attrs();
7c673cae
FG
553}
554
555void RGWStatAccount_ObjStore_SWIFT::send_response()
556{
557 if (op_ret >= 0) {
558 op_ret = STATUS_NO_CONTENT;
559 dump_account_metadata(s,
3efd9988
FG
560 global_stats,
561 policies_stats,
7c673cae 562 attrs,
1e59de90 563 s->user->get_info().quota.user_quota,
7c673cae
FG
564 static_cast<RGWAccessControlPolicy_SWIFTAcct&>(*s->user_acl));
565 }
566
567 set_req_state_err(s, op_ret);
568 dump_errno(s);
569
570 end_header(s, NULL, NULL, 0, true);
571
572 dump_start(s);
573}
574
575void RGWStatBucket_ObjStore_SWIFT::send_response()
576{
577 if (op_ret >= 0) {
578 op_ret = STATUS_NO_CONTENT;
1e59de90 579 dump_container_metadata(s, bucket.get(), quota.bucket_quota,
f67539c2 580 s->bucket->get_info().website_conf);
7c673cae
FG
581 }
582
583 set_req_state_err(s, op_ret);
584 dump_errno(s);
585
586 end_header(s, this, NULL, 0, true);
587 dump_start(s);
588}
589
590static int get_swift_container_settings(req_state * const s,
1e59de90 591 rgw::sal::Driver* const driver,
7c673cae
FG
592 RGWAccessControlPolicy * const policy,
593 bool * const has_policy,
594 uint32_t * rw_mask,
595 RGWCORSConfiguration * const cors_config,
596 bool * const has_cors)
597{
28e407b8
AA
598 const char * const read_list = s->info.env->get("HTTP_X_CONTAINER_READ");
599 const char * const write_list = s->info.env->get("HTTP_X_CONTAINER_WRITE");
7c673cae
FG
600
601 *has_policy = false;
602
28e407b8 603 if (read_list || write_list) {
7c673cae 604 RGWAccessControlPolicy_SWIFT swift_policy(s->cct);
1e59de90 605 const auto r = swift_policy.create(s, driver,
9f95a23c
TL
606 s->user->get_id(),
607 s->user->get_display_name(),
7c673cae
FG
608 read_list,
609 write_list,
610 *rw_mask);
611 if (r < 0) {
612 return r;
613 }
614
615 *policy = swift_policy;
616 *has_policy = true;
617 }
618
619 *has_cors = false;
620
621 /*Check and update CORS configuration*/
622 const char *allow_origins = s->info.env->get("HTTP_X_CONTAINER_META_ACCESS_CONTROL_ALLOW_ORIGIN");
623 const char *allow_headers = s->info.env->get("HTTP_X_CONTAINER_META_ACCESS_CONTROL_ALLOW_HEADERS");
624 const char *expose_headers = s->info.env->get("HTTP_X_CONTAINER_META_ACCESS_CONTROL_EXPOSE_HEADERS");
625 const char *max_age = s->info.env->get("HTTP_X_CONTAINER_META_ACCESS_CONTROL_MAX_AGE");
626 if (allow_origins) {
627 RGWCORSConfiguration_SWIFT *swift_cors = new RGWCORSConfiguration_SWIFT;
628 int r = swift_cors->create_update(allow_origins, allow_headers, expose_headers, max_age);
629 if (r < 0) {
b3b6e05e 630 ldpp_dout(s, 0) << "Error creating/updating the cors configuration" << dendl;
7c673cae
FG
631 delete swift_cors;
632 return r;
633 }
634 *has_cors = true;
635 *cors_config = *swift_cors;
636 cors_config->dump();
637 delete swift_cors;
638 }
639
640 return 0;
641}
642
643#define ACCT_REMOVE_ATTR_PREFIX "HTTP_X_REMOVE_ACCOUNT_META_"
644#define ACCT_PUT_ATTR_PREFIX "HTTP_X_ACCOUNT_META_"
645#define CONT_REMOVE_ATTR_PREFIX "HTTP_X_REMOVE_CONTAINER_META_"
646#define CONT_PUT_ATTR_PREFIX "HTTP_X_CONTAINER_META_"
647
648static void get_rmattrs_from_headers(const req_state * const s,
649 const char * const put_prefix,
650 const char * const del_prefix,
651 set<string>& rmattr_names)
652{
7c673cae
FG
653 const size_t put_prefix_len = strlen(put_prefix);
654 const size_t del_prefix_len = strlen(del_prefix);
655
31f18b77 656 for (const auto& kv : s->info.env->get_map()) {
7c673cae 657 size_t prefix_len = 0;
31f18b77 658 const char * const p = kv.first.c_str();
7c673cae
FG
659
660 if (strncasecmp(p, del_prefix, del_prefix_len) == 0) {
661 /* Explicitly requested removal. */
662 prefix_len = del_prefix_len;
663 } else if ((strncasecmp(p, put_prefix, put_prefix_len) == 0)
31f18b77 664 && kv.second.empty()) {
7c673cae
FG
665 /* Removal requested by putting an empty value. */
666 prefix_len = put_prefix_len;
667 }
668
669 if (prefix_len > 0) {
670 string name(RGW_ATTR_META_PREFIX);
671 name.append(lowercase_dash_http_attr(p + prefix_len));
672 rmattr_names.insert(name);
673 }
674 }
675}
676
677static int get_swift_versioning_settings(
678 req_state * const s,
679 boost::optional<std::string>& swift_ver_location)
680{
681 /* Removing the Swift's versions location has lower priority than setting
682 * a new one. That's the reason why we're handling it first. */
683 const std::string vlocdel =
684 s->info.env->get("HTTP_X_REMOVE_VERSIONS_LOCATION", "");
685 if (vlocdel.size()) {
686 swift_ver_location = boost::in_place(std::string());
687 }
688
689 if (s->info.env->exists("HTTP_X_VERSIONS_LOCATION")) {
690 /* If the Swift's versioning is globally disabled but someone wants to
691 * enable it for a given container, new version of Swift will generate
692 * the precondition failed error. */
693 if (! s->cct->_conf->rgw_swift_versioning_enabled) {
694 return -ERR_PRECONDITION_FAILED;
695 }
696
697 swift_ver_location = s->info.env->get("HTTP_X_VERSIONS_LOCATION", "");
698 }
699
700 return 0;
701}
702
f67539c2 703int RGWCreateBucket_ObjStore_SWIFT::get_params(optional_yield y)
7c673cae
FG
704{
705 bool has_policy;
706 uint32_t policy_rw_mask = 0;
707
1e59de90 708 int r = get_swift_container_settings(s, driver, &policy, &has_policy,
7c673cae
FG
709 &policy_rw_mask, &cors_config, &has_cors);
710 if (r < 0) {
711 return r;
712 }
713
714 if (!has_policy) {
9f95a23c 715 policy.create_default(s->user->get_id(), s->user->get_display_name());
7c673cae
FG
716 }
717
1e59de90 718 location_constraint = driver->get_zone()->get_zonegroup().get_api_name();
7c673cae
FG
719 get_rmattrs_from_headers(s, CONT_PUT_ATTR_PREFIX,
720 CONT_REMOVE_ATTR_PREFIX, rmattr_names);
11fdf7f2 721 placement_rule.init(s->info.env->get("HTTP_X_STORAGE_POLICY", ""), s->info.storage_class);
7c673cae
FG
722
723 return get_swift_versioning_settings(s, swift_ver_location);
724}
725
3efd9988
FG
726static inline int handle_metadata_errors(req_state* const s, const int op_ret)
727{
728 if (op_ret == -EFBIG) {
729 /* Handle the custom error message of exceeding maximum custom attribute
730 * (stored as xattr) size. */
731 const auto error_message = boost::str(
732 boost::format("Metadata value longer than %lld")
11fdf7f2 733 % s->cct->_conf.get_val<Option::size_t>("rgw_max_attr_size"));
3efd9988
FG
734 set_req_state_err(s, EINVAL, error_message);
735 return -EINVAL;
736 } else if (op_ret == -E2BIG) {
737 const auto error_message = boost::str(
738 boost::format("Too many metadata items; max %lld")
11fdf7f2 739 % s->cct->_conf.get_val<uint64_t>("rgw_max_attrs_num_in_req"));
3efd9988
FG
740 set_req_state_err(s, EINVAL, error_message);
741 return -EINVAL;
742 }
743
744 return op_ret;
745}
746
7c673cae
FG
747void RGWCreateBucket_ObjStore_SWIFT::send_response()
748{
3efd9988
FG
749 const auto meta_ret = handle_metadata_errors(s, op_ret);
750 if (meta_ret != op_ret) {
751 op_ret = meta_ret;
752 } else {
753 if (!op_ret) {
754 op_ret = STATUS_CREATED;
755 } else if (op_ret == -ERR_BUCKET_EXISTS) {
756 op_ret = STATUS_ACCEPTED;
757 }
758 set_req_state_err(s, op_ret);
759 }
760
7c673cae
FG
761 dump_errno(s);
762 /* Propose ending HTTP header with 0 Content-Length header. */
763 end_header(s, NULL, NULL, 0);
764 rgw_flush_formatter_and_reset(s, s->formatter);
765}
766
767void RGWDeleteBucket_ObjStore_SWIFT::send_response()
768{
769 int r = op_ret;
770 if (!r)
771 r = STATUS_NO_CONTENT;
772
773 set_req_state_err(s, r);
774 dump_errno(s);
775 end_header(s, this, NULL, 0);
776 rgw_flush_formatter_and_reset(s, s->formatter);
777}
778
779static int get_delete_at_param(req_state *s, boost::optional<real_time> &delete_at)
780{
781 /* Handle Swift object expiration. */
782 real_time delat_proposal;
783 string x_delete = s->info.env->get("HTTP_X_DELETE_AFTER", "");
784
785 if (x_delete.empty()) {
786 x_delete = s->info.env->get("HTTP_X_DELETE_AT", "");
787 } else {
788 /* X-Delete-After HTTP is present. It means we need add its value
789 * to the current time. */
790 delat_proposal = real_clock::now();
791 }
792
793 if (x_delete.empty()) {
794 delete_at = boost::none;
795 if (s->info.env->exists("HTTP_X_REMOVE_DELETE_AT")) {
796 delete_at = boost::in_place(real_time());
797 }
798 return 0;
799 }
800 string err;
801 long ts = strict_strtoll(x_delete.c_str(), 10, &err);
802
803 if (!err.empty()) {
804 return -EINVAL;
805 }
806
807 delat_proposal += make_timespan(ts);
808 if (delat_proposal < real_clock::now()) {
809 return -EINVAL;
810 }
811
812 delete_at = delat_proposal;
813
814 return 0;
815}
816
f67539c2 817int RGWPutObj_ObjStore_SWIFT::verify_permission(optional_yield y)
7c673cae 818{
f67539c2 819 op_ret = RGWPutObj_ObjStore::verify_permission(y);
7c673cae
FG
820
821 /* We have to differentiate error codes depending on whether user is
822 * anonymous (401 Unauthorized) or he doesn't have necessary permissions
823 * (403 Forbidden). */
824 if (s->auth.identity->is_anonymous() && op_ret == -EACCES) {
825 return -EPERM;
826 } else {
827 return op_ret;
828 }
829}
830
11fdf7f2
TL
831int RGWPutObj_ObjStore_SWIFT::update_slo_segment_size(rgw_slo_entry& entry) {
832
833 int r = 0;
834 const string& path = entry.path;
835
836 /* If the path starts with slashes, strip them all. */
837 const size_t pos_init = path.find_first_not_of('/');
838
839 if (pos_init == string::npos) {
840 return -EINVAL;
841 }
842
843 const size_t pos_sep = path.find('/', pos_init);
844 if (pos_sep == string::npos) {
845 return -EINVAL;
846 }
847
848 string bucket_name = path.substr(pos_init, pos_sep - pos_init);
849 string obj_name = path.substr(pos_sep + 1);
850
20effc67 851 std::unique_ptr<rgw::sal::Bucket> bucket;
11fdf7f2 852
f67539c2 853 if (bucket_name.compare(s->bucket->get_name()) != 0) {
1e59de90 854 r = driver->get_bucket(s, s->user.get(), s->user->get_id().tenant, bucket_name, &bucket, s->yield);
11fdf7f2
TL
855 if (r < 0) {
856 ldpp_dout(this, 0) << "could not get bucket info for bucket="
857 << bucket_name << dendl;
858 return r;
859 }
11fdf7f2 860 } else {
20effc67 861 bucket = s->bucket->clone();
11fdf7f2
TL
862 }
863
864 /* fetch the stored size of the seg (or error if not valid) */
20effc67 865 std::unique_ptr<rgw::sal::Object> slo_seg = bucket->get_object(rgw_obj_key(obj_name));
11fdf7f2
TL
866
867 /* no prefetch */
1e59de90 868 slo_seg->set_atomic();
11fdf7f2
TL
869
870 bool compressed;
871 RGWCompressionInfo cs_info;
11fdf7f2
TL
872 uint64_t size_bytes{0};
873
1e59de90 874 r = slo_seg->get_obj_attrs(s->yield, s);
11fdf7f2
TL
875 if (r < 0) {
876 return r;
877 }
878
20effc67
TL
879 size_bytes = slo_seg->get_obj_size();
880
881 r = rgw_compression_info_from_attrset(slo_seg->get_attrs(), compressed, cs_info);
11fdf7f2
TL
882 if (r < 0) {
883 return -EIO;
884 }
885
886 if (compressed) {
887 size_bytes = cs_info.orig_size;
888 }
889
890 /* "When the PUT operation sees the multipart-manifest=put query
891 * parameter, it reads the request body and verifies that each
892 * segment object exists and that the sizes and ETags match. If
893 * there is a mismatch, the PUT operation fails."
894 */
895 if (entry.size_bytes &&
896 (entry.size_bytes != size_bytes)) {
897 return -EINVAL;
898 }
899
900 entry.size_bytes = size_bytes;
901
902 return 0;
903} /* RGWPutObj_ObjStore_SWIFT::update_slo_segment_sizes */
904
f67539c2 905int RGWPutObj_ObjStore_SWIFT::get_params(optional_yield y)
7c673cae
FG
906{
907 if (s->has_bad_meta) {
908 return -EINVAL;
909 }
910
911 if (!s->length) {
912 const char *encoding = s->info.env->get("HTTP_TRANSFER_ENCODING");
913 if (!encoding || strcmp(encoding, "chunked") != 0) {
9f95a23c 914 ldpp_dout(this, 20) << "neither length nor chunked encoding" << dendl;
7c673cae
FG
915 return -ERR_LENGTH_REQUIRED;
916 }
917
918 chunked_upload = true;
919 }
920
921 supplied_etag = s->info.env->get("HTTP_ETAG");
922
923 if (!s->generic_attrs.count(RGW_ATTR_CONTENT_TYPE)) {
9f95a23c 924 ldpp_dout(this, 5) << "content type wasn't provided, trying to guess" << dendl;
f67539c2 925 const char *suffix = strrchr(s->object->get_name().c_str(), '.');
7c673cae
FG
926 if (suffix) {
927 suffix++;
928 if (*suffix) {
929 string suffix_str(suffix);
930 const char *mime = rgw_find_mime_by_ext(suffix_str);
931 if (mime) {
932 s->generic_attrs[RGW_ATTR_CONTENT_TYPE] = mime;
933 }
934 }
935 }
936 }
937
9f95a23c 938 policy.create_default(s->user->get_id(), s->user->get_display_name());
7c673cae
FG
939
940 int r = get_delete_at_param(s, delete_at);
941 if (r < 0) {
9f95a23c 942 ldpp_dout(this, 5) << "ERROR: failed to get Delete-At param" << dendl;
7c673cae
FG
943 return r;
944 }
945
946 if (!s->cct->_conf->rgw_swift_custom_header.empty()) {
947 string custom_header = s->cct->_conf->rgw_swift_custom_header;
aee94f69
TL
948 auto data = s->info.env->get_optional(custom_header);
949 if (data) {
950 user_data = *data;
7c673cae
FG
951 }
952 }
953
954 dlo_manifest = s->info.env->get("HTTP_X_OBJECT_MANIFEST");
955 bool exists;
956 string multipart_manifest = s->info.args.get("multipart-manifest", &exists);
957 if (exists) {
958 if (multipart_manifest != "put") {
9f95a23c 959 ldpp_dout(this, 5) << "invalid multipart-manifest http param: " << multipart_manifest << dendl;
7c673cae
FG
960 return -EINVAL;
961 }
962
963#define MAX_SLO_ENTRY_SIZE (1024 + 128) // 1024 - max obj name, 128 - enough extra for other info
964 uint64_t max_len = s->cct->_conf->rgw_max_slo_entries * MAX_SLO_ENTRY_SIZE;
965
966 slo_info = new RGWSLOInfo;
967
11fdf7f2
TL
968 int r = 0;
969 std::tie(r, slo_info->raw_data) = rgw_rest_get_json_input_keep_data(s->cct, s, slo_info->entries, max_len);
7c673cae 970 if (r < 0) {
9f95a23c 971 ldpp_dout(this, 5) << "failed to read input for slo r=" << r << dendl;
7c673cae
FG
972 return r;
973 }
974
975 if ((int64_t)slo_info->entries.size() > s->cct->_conf->rgw_max_slo_entries) {
9f95a23c 976 ldpp_dout(this, 5) << "too many entries in slo request: " << slo_info->entries.size() << dendl;
7c673cae
FG
977 return -EINVAL;
978 }
979
980 MD5 etag_sum;
20effc67
TL
981 // Allow use of MD5 digest in FIPS mode for non-cryptographic purposes
982 etag_sum.SetFlags(EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);
7c673cae 983 uint64_t total_size = 0;
11fdf7f2
TL
984 for (auto& entry : slo_info->entries) {
985 etag_sum.Update((const unsigned char *)entry.etag.c_str(),
7c673cae 986 entry.etag.length());
11fdf7f2
TL
987
988 /* if size_bytes == 0, it should be replaced with the
989 * real segment size (which could be 0); this follows from the
990 * fact that Swift requires all segments to exist, but permits
991 * the size_bytes element to be omitted from the SLO manifest, see
992 * https://docs.openstack.org/swift/latest/api/large_objects.html
993 */
994 r = update_slo_segment_size(entry);
995 if (r < 0) {
996 return r;
997 }
998
7c673cae
FG
999 total_size += entry.size_bytes;
1000
9f95a23c 1001 ldpp_dout(this, 20) << "slo_part: " << entry.path
7c673cae
FG
1002 << " size=" << entry.size_bytes
1003 << " etag=" << entry.etag
1004 << dendl;
1005 }
1006 complete_etag(etag_sum, &lo_etag);
1007 slo_info->total_size = total_size;
1008
11fdf7f2 1009 ofs = slo_info->raw_data.length();
7c673cae
FG
1010 }
1011
f67539c2 1012 return RGWPutObj_ObjStore::get_params(y);
7c673cae
FG
1013}
1014
1015void RGWPutObj_ObjStore_SWIFT::send_response()
1016{
3efd9988
FG
1017 const auto meta_ret = handle_metadata_errors(s, op_ret);
1018 if (meta_ret) {
1019 op_ret = meta_ret;
1020 } else {
1021 if (!op_ret) {
1022 op_ret = STATUS_CREATED;
1023 }
1024 set_req_state_err(s, op_ret);
7c673cae
FG
1025 }
1026
1027 if (! lo_etag.empty()) {
1028 /* Static Large Object of Swift API has two etags represented by
1029 * following members:
1030 * - etag - for the manifest itself (it will be stored in xattrs),
1031 * - lo_etag - for the content composited from SLO's segments.
1032 * The value is calculated basing on segments' etags.
1033 * In response for PUT request we have to expose the second one.
1034 * The first one may be obtained by GET with "multipart-manifest=get"
1035 * in query string on a given SLO. */
1036 dump_etag(s, lo_etag, true /* quoted */);
1037 } else {
1038 dump_etag(s, etag);
1039 }
1040
1041 dump_last_modified(s, mtime);
1042 set_req_state_err(s, op_ret);
1043 dump_errno(s);
1044 end_header(s, this);
1045 rgw_flush_formatter_and_reset(s, s->formatter);
1046}
1047
1048static int get_swift_account_settings(req_state * const s,
1e59de90 1049 rgw::sal::Driver* const driver,
20effc67 1050 RGWAccessControlPolicy_SWIFTAcct* const policy,
7c673cae
FG
1051 bool * const has_policy)
1052{
1053 *has_policy = false;
1054
1055 const char * const acl_attr = s->info.env->get("HTTP_X_ACCOUNT_ACCESS_CONTROL");
1056 if (acl_attr) {
1057 RGWAccessControlPolicy_SWIFTAcct swift_acct_policy(s->cct);
1e59de90 1058 const bool r = swift_acct_policy.create(s, driver,
9f95a23c
TL
1059 s->user->get_id(),
1060 s->user->get_display_name(),
7c673cae
FG
1061 string(acl_attr));
1062 if (r != true) {
1063 return -EINVAL;
1064 }
1065
1066 *policy = swift_acct_policy;
1067 *has_policy = true;
1068 }
1069
1070 return 0;
1071}
1072
f67539c2 1073int RGWPutMetadataAccount_ObjStore_SWIFT::get_params(optional_yield y)
7c673cae
FG
1074{
1075 if (s->has_bad_meta) {
1076 return -EINVAL;
1077 }
1078
1079 int ret = get_swift_account_settings(s,
1e59de90 1080 driver,
7c673cae
FG
1081 // FIXME: we need to carry unique_ptr in generic class
1082 // and allocate appropriate ACL class in the ctor
1083 static_cast<RGWAccessControlPolicy_SWIFTAcct *>(&policy),
1084 &has_policy);
1085 if (ret < 0) {
1086 return ret;
1087 }
1088
1089 get_rmattrs_from_headers(s, ACCT_PUT_ATTR_PREFIX, ACCT_REMOVE_ATTR_PREFIX,
1090 rmattr_names);
1091 return 0;
1092}
1093
1094void RGWPutMetadataAccount_ObjStore_SWIFT::send_response()
1095{
3efd9988
FG
1096 const auto meta_ret = handle_metadata_errors(s, op_ret);
1097 if (meta_ret != op_ret) {
1098 op_ret = meta_ret;
1099 } else {
1100 if (!op_ret) {
1101 op_ret = STATUS_NO_CONTENT;
1102 }
1103 set_req_state_err(s, op_ret);
7c673cae 1104 }
3efd9988 1105
7c673cae
FG
1106 dump_errno(s);
1107 end_header(s, this);
1108 rgw_flush_formatter_and_reset(s, s->formatter);
1109}
1110
f67539c2 1111int RGWPutMetadataBucket_ObjStore_SWIFT::get_params(optional_yield y)
7c673cae
FG
1112{
1113 if (s->has_bad_meta) {
1114 return -EINVAL;
1115 }
1116
1e59de90 1117 int r = get_swift_container_settings(s, driver, &policy, &has_policy,
7c673cae
FG
1118 &policy_rw_mask, &cors_config, &has_cors);
1119 if (r < 0) {
1120 return r;
1121 }
1122
1123 get_rmattrs_from_headers(s, CONT_PUT_ATTR_PREFIX, CONT_REMOVE_ATTR_PREFIX,
1124 rmattr_names);
11fdf7f2 1125 placement_rule.init(s->info.env->get("HTTP_X_STORAGE_POLICY", ""), s->info.storage_class);
7c673cae
FG
1126
1127 return get_swift_versioning_settings(s, swift_ver_location);
1128}
1129
1130void RGWPutMetadataBucket_ObjStore_SWIFT::send_response()
1131{
3efd9988
FG
1132 const auto meta_ret = handle_metadata_errors(s, op_ret);
1133 if (meta_ret != op_ret) {
1134 op_ret = meta_ret;
1135 } else {
1136 if (!op_ret && (op_ret != -EINVAL)) {
1137 op_ret = STATUS_NO_CONTENT;
1138 }
1139 set_req_state_err(s, op_ret);
7c673cae 1140 }
3efd9988 1141
7c673cae
FG
1142 dump_errno(s);
1143 end_header(s, this);
1144 rgw_flush_formatter_and_reset(s, s->formatter);
1145}
1146
f67539c2 1147int RGWPutMetadataObject_ObjStore_SWIFT::get_params(optional_yield y)
7c673cae
FG
1148{
1149 if (s->has_bad_meta) {
1150 return -EINVAL;
1151 }
1152
1153 /* Handle Swift object expiration. */
1154 int r = get_delete_at_param(s, delete_at);
1155 if (r < 0) {
9f95a23c 1156 ldpp_dout(this, 5) << "ERROR: failed to get Delete-At param" << dendl;
7c673cae
FG
1157 return r;
1158 }
1159
7c673cae
FG
1160 dlo_manifest = s->info.env->get("HTTP_X_OBJECT_MANIFEST");
1161
1162 return 0;
1163}
1164
1165void RGWPutMetadataObject_ObjStore_SWIFT::send_response()
1166{
3efd9988
FG
1167 const auto meta_ret = handle_metadata_errors(s, op_ret);
1168 if (meta_ret != op_ret) {
1169 op_ret = meta_ret;
1170 } else {
1171 if (!op_ret) {
1172 op_ret = STATUS_ACCEPTED;
1173 }
1174 set_req_state_err(s, op_ret);
7c673cae 1175 }
3efd9988 1176
31f18b77 1177 if (!s->is_err()) {
7c673cae
FG
1178 dump_content_length(s, 0);
1179 }
3efd9988 1180
7c673cae
FG
1181 dump_errno(s);
1182 end_header(s, this);
1183 rgw_flush_formatter_and_reset(s, s->formatter);
1184}
1185
1186static void bulkdelete_respond(const unsigned num_deleted,
1187 const unsigned int num_unfound,
1188 const std::list<RGWBulkDelete::fail_desc_t>& failures,
1189 const int prot_flags, /* in */
1190 ceph::Formatter& formatter) /* out */
1191{
1192 formatter.open_object_section("delete");
1193
1194 string resp_status;
1195 string resp_body;
1196
1197 if (!failures.empty()) {
1198 int reason = ERR_INVALID_REQUEST;
f67539c2 1199 for (const auto& fail_desc : failures) {
7c673cae
FG
1200 if (-ENOENT != fail_desc.err && -EACCES != fail_desc.err) {
1201 reason = fail_desc.err;
1202 }
1203 }
7c673cae
FG
1204 rgw_err err;
1205 set_req_state_err(err, reason, prot_flags);
1206 dump_errno(err, resp_status);
1207 } else if (0 == num_deleted && 0 == num_unfound) {
1208 /* 400 Bad Request */
1209 dump_errno(400, resp_status);
1210 resp_body = "Invalid bulk delete.";
1211 } else {
1212 /* 200 OK */
1213 dump_errno(200, resp_status);
1214 }
1215
1216 encode_json("Number Deleted", num_deleted, &formatter);
1217 encode_json("Number Not Found", num_unfound, &formatter);
1218 encode_json("Response Body", resp_body, &formatter);
1219 encode_json("Response Status", resp_status, &formatter);
1220
1221 formatter.open_array_section("Errors");
f67539c2 1222 for (const auto& fail_desc : failures) {
7c673cae
FG
1223 formatter.open_array_section("object");
1224
1225 stringstream ss_name;
1226 ss_name << fail_desc.path;
1227 encode_json("Name", ss_name.str(), &formatter);
1228
1229 rgw_err err;
1230 set_req_state_err(err, fail_desc.err, prot_flags);
1231 string status;
1232 dump_errno(err, status);
1233 encode_json("Status", status, &formatter);
1234 formatter.close_section();
1235 }
1236 formatter.close_section();
1237
1238 formatter.close_section();
1239}
1240
f67539c2 1241int RGWDeleteObj_ObjStore_SWIFT::verify_permission(optional_yield y)
7c673cae 1242{
f67539c2 1243 op_ret = RGWDeleteObj_ObjStore::verify_permission(y);
7c673cae
FG
1244
1245 /* We have to differentiate error codes depending on whether user is
1246 * anonymous (401 Unauthorized) or he doesn't have necessary permissions
1247 * (403 Forbidden). */
1248 if (s->auth.identity->is_anonymous() && op_ret == -EACCES) {
1249 return -EPERM;
1250 } else {
1251 return op_ret;
1252 }
1253}
1254
f67539c2 1255int RGWDeleteObj_ObjStore_SWIFT::get_params(optional_yield y)
7c673cae
FG
1256{
1257 const string& mm = s->info.args.get("multipart-manifest");
1258 multipart_delete = (mm.compare("delete") == 0);
1259
f67539c2 1260 return RGWDeleteObj_ObjStore::get_params(y);
7c673cae
FG
1261}
1262
1263void RGWDeleteObj_ObjStore_SWIFT::send_response()
1264{
1265 int r = op_ret;
1266
1267 if (multipart_delete) {
1268 r = 0;
1269 } else if(!r) {
1270 r = STATUS_NO_CONTENT;
1271 }
1272
1273 set_req_state_err(s, r);
1274 dump_errno(s);
1275
1276 if (multipart_delete) {
1277 end_header(s, this /* RGWOp */, nullptr /* contype */,
1278 CHUNKED_TRANSFER_ENCODING);
1279
1280 if (deleter) {
1281 bulkdelete_respond(deleter->get_num_deleted(),
1282 deleter->get_num_unfound(),
1283 deleter->get_failures(),
1284 s->prot_flags,
1285 *s->formatter);
1286 } else if (-ENOENT == op_ret) {
1287 bulkdelete_respond(0, 1, {}, s->prot_flags, *s->formatter);
1288 } else {
1289 RGWBulkDelete::acct_path_t path;
1290 path.bucket_name = s->bucket_name;
f67539c2 1291 path.obj_key = s->object->get_key();
7c673cae
FG
1292
1293 RGWBulkDelete::fail_desc_t fail_desc;
1294 fail_desc.err = op_ret;
1295 fail_desc.path = path;
1296
1297 bulkdelete_respond(0, 0, { fail_desc }, s->prot_flags, *s->formatter);
1298 }
1299 } else {
1300 end_header(s, this);
1301 }
1302
1303 rgw_flush_formatter_and_reset(s, s->formatter);
1304
1305}
1306
1307static void get_contype_from_attrs(map<string, bufferlist>& attrs,
1308 string& content_type)
1309{
1310 map<string, bufferlist>::iterator iter = attrs.find(RGW_ATTR_CONTENT_TYPE);
1311 if (iter != attrs.end()) {
11fdf7f2 1312 content_type = rgw_bl_str(iter->second);
7c673cae
FG
1313 }
1314}
1315
1e59de90 1316static void dump_object_metadata(const DoutPrefixProvider* dpp, req_state * const s,
11fdf7f2 1317 const map<string, bufferlist>& attrs)
7c673cae
FG
1318{
1319 map<string, string> response_attrs;
1320
1321 for (auto kv : attrs) {
1322 const char * name = kv.first.c_str();
1323 const auto aiter = rgw_to_http_attrs.find(name);
1324
1325 if (aiter != std::end(rgw_to_http_attrs)) {
11fdf7f2 1326 response_attrs[aiter->second] = rgw_bl_str(kv.second);
31f18b77 1327 } else if (strcmp(name, RGW_ATTR_SLO_UINDICATOR) == 0) {
11fdf7f2 1328 // this attr has an extra length prefix from encode() in prior versions
31f18b77 1329 dump_header(s, "X-Object-Meta-Static-Large-Object", "True");
7c673cae
FG
1330 } else if (strncmp(name, RGW_ATTR_META_PREFIX,
1331 sizeof(RGW_ATTR_META_PREFIX)-1) == 0) {
1332 name += sizeof(RGW_ATTR_META_PREFIX) - 1;
1333 dump_header_prefixed(s, "X-Object-Meta-",
1334 camelcase_dash_http_attr(name), kv.second);
1335 }
1336 }
1337
1338 /* Handle override and fallback for Content-Disposition HTTP header.
1339 * At the moment this will be used only by TempURL of the Swift API. */
1340 const auto cditer = rgw_to_http_attrs.find(RGW_ATTR_CONTENT_DISP);
1341 if (cditer != std::end(rgw_to_http_attrs)) {
1342 const auto& name = cditer->second;
1343
1344 if (!s->content_disp.override.empty()) {
1345 response_attrs[name] = s->content_disp.override;
1346 } else if (!s->content_disp.fallback.empty()
1347 && response_attrs.find(name) == std::end(response_attrs)) {
1348 response_attrs[name] = s->content_disp.fallback;
1349 }
1350 }
1351
f67539c2 1352 for (const auto& kv : response_attrs) {
7c673cae
FG
1353 dump_header(s, kv.first, kv.second);
1354 }
1355
1356 const auto iter = attrs.find(RGW_ATTR_DELETE_AT);
1357 if (iter != std::end(attrs)) {
1358 utime_t delete_at;
1359 try {
11fdf7f2 1360 decode(delete_at, iter->second);
7c673cae
FG
1361 if (!delete_at.is_zero()) {
1362 dump_header(s, "X-Delete-At", delete_at.sec());
1363 }
1364 } catch (buffer::error& err) {
9f95a23c 1365 ldpp_dout(dpp, 0) << "ERROR: cannot decode object's " RGW_ATTR_DELETE_AT
7c673cae
FG
1366 " attr, ignoring"
1367 << dendl;
1368 }
1369 }
1370}
1371
1372int RGWCopyObj_ObjStore_SWIFT::init_dest_policy()
1373{
9f95a23c 1374 dest_policy.create_default(s->user->get_id(), s->user->get_display_name());
7c673cae
FG
1375
1376 return 0;
1377}
1378
f67539c2 1379int RGWCopyObj_ObjStore_SWIFT::get_params(optional_yield y)
7c673cae
FG
1380{
1381 if_mod = s->info.env->get("HTTP_IF_MODIFIED_SINCE");
1382 if_unmod = s->info.env->get("HTTP_IF_UNMODIFIED_SINCE");
1383 if_match = s->info.env->get("HTTP_COPY_IF_MATCH");
1384 if_nomatch = s->info.env->get("HTTP_COPY_IF_NONE_MATCH");
1385
7c673cae
FG
1386 const char * const fresh_meta = s->info.env->get("HTTP_X_FRESH_METADATA");
1387 if (fresh_meta && strcasecmp(fresh_meta, "TRUE") == 0) {
f67539c2 1388 attrs_mod = rgw::sal::ATTRSMOD_REPLACE;
7c673cae 1389 } else {
f67539c2 1390 attrs_mod = rgw::sal::ATTRSMOD_MERGE;
7c673cae
FG
1391 }
1392
1393 int r = get_delete_at_param(s, delete_at);
1394 if (r < 0) {
9f95a23c 1395 ldpp_dout(this, 5) << "ERROR: failed to get Delete-At param" << dendl;
7c673cae
FG
1396 return r;
1397 }
1398
1399 return 0;
1400}
1401
1402void RGWCopyObj_ObjStore_SWIFT::send_partial_response(off_t ofs)
1403{
1404 if (! sent_header) {
1405 if (! op_ret)
1406 op_ret = STATUS_CREATED;
1407 set_req_state_err(s, op_ret);
1408 dump_errno(s);
1409 end_header(s, this);
1410
1411 /* Send progress information. Note that this diverge from the original swift
1412 * spec. We do this in order to keep connection alive.
1413 */
1414 if (op_ret == 0) {
1415 s->formatter->open_array_section("progress");
1416 }
1417 sent_header = true;
1418 } else {
1419 s->formatter->dump_int("ofs", (uint64_t)ofs);
1420 }
1421 rgw_flush_formatter(s, s->formatter);
1422}
1423
1424void RGWCopyObj_ObjStore_SWIFT::dump_copy_info()
1425{
1426 /* Dump X-Copied-From. */
f67539c2 1427 dump_header(s, "X-Copied-From", url_encode(src_bucket->get_name()) +
20effc67 1428 "/" + url_encode(s->src_object->get_name()));
7c673cae
FG
1429
1430 /* Dump X-Copied-From-Account. */
1431 /* XXX tenant */
9f95a23c 1432 dump_header(s, "X-Copied-From-Account", url_encode(s->user->get_id().id));
7c673cae
FG
1433
1434 /* Dump X-Copied-From-Last-Modified. */
1435 dump_time_header(s, "X-Copied-From-Last-Modified", src_mtime);
1436}
1437
1438void RGWCopyObj_ObjStore_SWIFT::send_response()
1439{
1440 if (! sent_header) {
1441 string content_type;
1442 if (! op_ret)
1443 op_ret = STATUS_CREATED;
1444 set_req_state_err(s, op_ret);
1445 dump_errno(s);
1446 dump_etag(s, etag);
1447 dump_last_modified(s, mtime);
1448 dump_copy_info();
1449 get_contype_from_attrs(attrs, content_type);
9f95a23c 1450 dump_object_metadata(this, s, attrs);
7c673cae
FG
1451 end_header(s, this, !content_type.empty() ? content_type.c_str()
1452 : "binary/octet-stream");
1453 } else {
1454 s->formatter->close_section();
1455 rgw_flush_formatter(s, s->formatter);
1456 }
1457}
1458
f67539c2 1459int RGWGetObj_ObjStore_SWIFT::verify_permission(optional_yield y)
7c673cae 1460{
f67539c2 1461 op_ret = RGWGetObj_ObjStore::verify_permission(y);
7c673cae
FG
1462
1463 /* We have to differentiate error codes depending on whether user is
1464 * anonymous (401 Unauthorized) or he doesn't have necessary permissions
1465 * (403 Forbidden). */
1466 if (s->auth.identity->is_anonymous() && op_ret == -EACCES) {
1467 return -EPERM;
1468 } else {
1469 return op_ret;
1470 }
1471}
1472
f67539c2 1473int RGWGetObj_ObjStore_SWIFT::get_params(optional_yield y)
7c673cae
FG
1474{
1475 const string& mm = s->info.args.get("multipart-manifest");
1476 skip_manifest = (mm.compare("get") == 0);
1477
f67539c2 1478 return RGWGetObj_ObjStore::get_params(y);
7c673cae
FG
1479}
1480
f67539c2 1481int RGWGetObj_ObjStore_SWIFT::send_response_data_error(optional_yield y)
7c673cae
FG
1482{
1483 std::string error_content;
f67539c2 1484 op_ret = error_handler(op_ret, &error_content, y);
7c673cae
FG
1485 if (! op_ret) {
1486 /* The error handler has taken care of the error. */
1487 return 0;
1488 }
1489
1490 bufferlist error_bl;
1491 error_bl.append(error_content);
1492 return send_response_data(error_bl, 0, error_bl.length());
1493}
1494
1495int RGWGetObj_ObjStore_SWIFT::send_response_data(bufferlist& bl,
1496 const off_t bl_ofs,
1497 const off_t bl_len)
1498{
1499 string content_type;
1500
1501 if (sent_header) {
1502 goto send_data;
1503 }
1504
1505 if (custom_http_ret) {
1506 set_req_state_err(s, 0);
1507 dump_errno(s, custom_http_ret);
1508 } else {
1509 set_req_state_err(s, (partial_content && !op_ret) ? STATUS_PARTIAL_CONTENT
1510 : op_ret);
1511 dump_errno(s);
1512
31f18b77 1513 if (s->is_err()) {
7c673cae
FG
1514 end_header(s, NULL);
1515 return 0;
1516 }
1517 }
1518
1519 if (range_str) {
1520 dump_range(s, ofs, end, s->obj_size);
1521 }
1522
31f18b77 1523 if (s->is_err()) {
7c673cae
FG
1524 end_header(s, NULL);
1525 return 0;
1526 }
1527
1528 dump_content_length(s, total_len);
1529 dump_last_modified(s, lastmod);
1530 dump_header(s, "X-Timestamp", utime_t(lastmod));
1531 if (is_slo) {
1532 dump_header(s, "X-Static-Large-Object", "True");
1533 }
1534
1535 if (! op_ret) {
1536 if (! lo_etag.empty()) {
1537 dump_etag(s, lo_etag, true /* quoted */);
1538 } else {
1539 auto iter = attrs.find(RGW_ATTR_ETAG);
1540 if (iter != attrs.end()) {
11fdf7f2 1541 dump_etag(s, iter->second.to_str());
7c673cae
FG
1542 }
1543 }
1544
1545 get_contype_from_attrs(attrs, content_type);
9f95a23c 1546 dump_object_metadata(this, s, attrs);
7c673cae
FG
1547 }
1548
1549 end_header(s, this, !content_type.empty() ? content_type.c_str()
1550 : "binary/octet-stream");
1551
1552 sent_header = true;
1553
1554send_data:
1555 if (get_data && !op_ret) {
1556 const auto r = dump_body(s, bl.c_str() + bl_ofs, bl_len);
1557 if (r < 0) {
1558 return r;
1559 }
1560 }
1561 rgw_flush_formatter_and_reset(s, s->formatter);
1562
1563 return 0;
1564}
1565
1566void RGWOptionsCORS_ObjStore_SWIFT::send_response()
1567{
1568 string hdrs, exp_hdrs;
1569 uint32_t max_age = CORS_MAX_AGE_INVALID;
1570 /*EACCES means, there is no CORS registered yet for the bucket
1571 *ENOENT means, there is no match of the Origin in the list of CORSRule
1572 */
1573 if (op_ret == -ENOENT)
1574 op_ret = -EACCES;
1575 if (op_ret < 0) {
1576 set_req_state_err(s, op_ret);
1577 dump_errno(s);
1578 end_header(s, NULL);
1579 return;
1580 }
1581 get_response_params(hdrs, exp_hdrs, &max_age);
1582 dump_errno(s);
1583 dump_access_control(s, origin, req_meth, hdrs.c_str(), exp_hdrs.c_str(),
1584 max_age);
1585 end_header(s, NULL);
1586}
1587
1588int RGWBulkDelete_ObjStore_SWIFT::get_data(
1589 list<RGWBulkDelete::acct_path_t>& items, bool * const is_truncated)
1590{
1591 constexpr size_t MAX_LINE_SIZE = 2048;
1592
1593 RGWClientIOStreamBuf ciosb(static_cast<RGWRestfulIO&>(*(s->cio)),
1594 size_t(s->cct->_conf->rgw_max_chunk_size));
1595 istream cioin(&ciosb);
1596
1597 char buf[MAX_LINE_SIZE];
1598 while (cioin.getline(buf, sizeof(buf))) {
1599 string path_str(buf);
1600
9f95a23c 1601 ldpp_dout(this, 20) << "extracted Bulk Delete entry: " << path_str << dendl;
7c673cae
FG
1602
1603 RGWBulkDelete::acct_path_t path;
1604
1605 /* We need to skip all slashes at the beginning in order to preserve
1606 * compliance with Swift. */
1607 const size_t start_pos = path_str.find_first_not_of('/');
1608
1609 if (string::npos != start_pos) {
1610 /* Seperator is the first slash after the leading ones. */
1611 const size_t sep_pos = path_str.find('/', start_pos);
1612
1613 if (string::npos != sep_pos) {
31f18b77
FG
1614 path.bucket_name = url_decode(path_str.substr(start_pos,
1615 sep_pos - start_pos));
1616 path.obj_key = url_decode(path_str.substr(sep_pos + 1));
7c673cae
FG
1617 } else {
1618 /* It's guaranteed here that bucket name is at least one character
1619 * long and is different than slash. */
31f18b77 1620 path.bucket_name = url_decode(path_str.substr(start_pos));
7c673cae
FG
1621 }
1622
1623 items.push_back(path);
1624 }
1625
1626 if (items.size() == MAX_CHUNK_ENTRIES) {
1627 *is_truncated = true;
1628 return 0;
1629 }
1630 }
1631
1632 *is_truncated = false;
1633 return 0;
1634}
1635
1636void RGWBulkDelete_ObjStore_SWIFT::send_response()
1637{
1638 set_req_state_err(s, op_ret);
1639 dump_errno(s);
1640 end_header(s, this /* RGWOp */, nullptr /* contype */,
1641 CHUNKED_TRANSFER_ENCODING);
1642
1643 bulkdelete_respond(deleter->get_num_deleted(),
1644 deleter->get_num_unfound(),
1645 deleter->get_failures(),
1646 s->prot_flags,
1647 *s->formatter);
1648 rgw_flush_formatter_and_reset(s, s->formatter);
1649}
1650
1651
1652std::unique_ptr<RGWBulkUploadOp::StreamGetter>
1653RGWBulkUploadOp_ObjStore_SWIFT::create_stream()
1654{
1655 class SwiftStreamGetter : public StreamGetter {
9f95a23c 1656 const DoutPrefixProvider* dpp;
7c673cae
FG
1657 const size_t conlen;
1658 size_t curpos;
1659 req_state* const s;
1660
1661 public:
9f95a23c
TL
1662 SwiftStreamGetter(const DoutPrefixProvider* dpp, req_state* const s, const size_t conlen)
1663 : dpp(dpp),
1664 conlen(conlen),
7c673cae
FG
1665 curpos(0),
1666 s(s) {
1667 }
1668
1669 ssize_t get_at_most(size_t want, ceph::bufferlist& dst) override {
1670 /* maximum requested by a caller */
1671 /* data provided by client */
1672 /* RadosGW's limit. */
1673 const size_t max_chunk_size = \
1674 static_cast<size_t>(s->cct->_conf->rgw_max_chunk_size);
1675 const size_t max_to_read = std::min({ want, conlen - curpos, max_chunk_size });
1676
9f95a23c 1677 ldpp_dout(dpp, 20) << "bulk_upload: get_at_most max_to_read="
7c673cae
FG
1678 << max_to_read
1679 << ", dst.c_str()=" << reinterpret_cast<intptr_t>(dst.c_str()) << dendl;
1680
1681 bufferptr bp(max_to_read);
1682 const auto read_len = recv_body(s, bp.c_str(), max_to_read);
1683 dst.append(bp, 0, read_len);
1684 //const auto read_len = recv_body(s, dst.c_str(), max_to_read);
1685 if (read_len < 0) {
1686 return read_len;
1687 }
1688
1689 curpos += read_len;
1690 return curpos > s->cct->_conf->rgw_max_put_size ? -ERR_TOO_LARGE
1691 : read_len;
1692 }
1693
1694 ssize_t get_exactly(size_t want, ceph::bufferlist& dst) override {
9f95a23c 1695 ldpp_dout(dpp, 20) << "bulk_upload: get_exactly want=" << want << dendl;
7c673cae
FG
1696
1697 /* FIXME: do this in a loop. */
1698 const auto ret = get_at_most(want, dst);
9f95a23c 1699 ldpp_dout(dpp, 20) << "bulk_upload: get_exactly ret=" << ret << dendl;
7c673cae
FG
1700 if (ret < 0) {
1701 return ret;
1702 } else if (static_cast<size_t>(ret) != want) {
1703 return -EINVAL;
1704 } else {
1705 return want;
1706 }
1707 }
1708 };
1709
1710 if (! s->length) {
1711 op_ret = -EINVAL;
1712 return nullptr;
1713 } else {
9f95a23c 1714 ldpp_dout(this, 20) << "bulk upload: create_stream for length="
7c673cae
FG
1715 << s->length << dendl;
1716
1717 const size_t conlen = atoll(s->length);
9f95a23c 1718 return std::unique_ptr<SwiftStreamGetter>(new SwiftStreamGetter(this, s, conlen));
7c673cae
FG
1719 }
1720}
1721
1722void RGWBulkUploadOp_ObjStore_SWIFT::send_response()
1723{
1724 set_req_state_err(s, op_ret);
1725 dump_errno(s);
1726 end_header(s, this /* RGWOp */, nullptr /* contype */,
1727 CHUNKED_TRANSFER_ENCODING);
1728 rgw_flush_formatter_and_reset(s, s->formatter);
1729
1730 s->formatter->open_object_section("delete");
1731
1732 std::string resp_status;
1733 std::string resp_body;
1734
1735 if (! failures.empty()) {
1736 rgw_err err;
1737
1738 const auto last_err = { failures.back().err };
1739 if (boost::algorithm::contains(last_err, terminal_errors)) {
1740 /* The terminal errors are affecting the status of the whole upload. */
1741 set_req_state_err(err, failures.back().err, s->prot_flags);
1742 } else {
1743 set_req_state_err(err, ERR_INVALID_REQUEST, s->prot_flags);
1744 }
1745
1746 dump_errno(err, resp_status);
1747 } else if (0 == num_created && failures.empty()) {
1748 /* Nothing created, nothing failed. This means the archive contained no
1749 * entity we could understand (regular file or directory). We need to
1750 * send 400 Bad Request to an HTTP client in the internal status field. */
1751 dump_errno(400, resp_status);
1752 resp_body = "Invalid Tar File: No Valid Files";
1753 } else {
1754 /* 200 OK */
1755 dump_errno(201, resp_status);
1756 }
1757
1758 encode_json("Number Files Created", num_created, s->formatter);
1759 encode_json("Response Body", resp_body, s->formatter);
1760 encode_json("Response Status", resp_status, s->formatter);
1761
1762 s->formatter->open_array_section("Errors");
1763 for (const auto& fail_desc : failures) {
1764 s->formatter->open_array_section("object");
1765
1766 encode_json("Name", fail_desc.path, s->formatter);
1767
1768 rgw_err err;
1769 set_req_state_err(err, fail_desc.err, s->prot_flags);
1770 std::string status;
1771 dump_errno(err, status);
1772 encode_json("Status", status, s->formatter);
1773
1774 s->formatter->close_section();
1775 }
1776 s->formatter->close_section();
1777
1778 s->formatter->close_section();
1779 rgw_flush_formatter_and_reset(s, s->formatter);
1780}
1781
1782
1783void RGWGetCrossDomainPolicy_ObjStore_SWIFT::send_response()
1784{
1785 set_req_state_err(s, op_ret);
1786 dump_errno(s);
1787 end_header(s, this, "application/xml");
1788
1789 std::stringstream ss;
1790
1791 ss << R"(<?xml version="1.0"?>)" << "\n"
1792 << R"(<!DOCTYPE cross-domain-policy SYSTEM )"
1793 << R"("http://www.adobe.com/xml/dtds/cross-domain-policy.dtd" >)" << "\n"
1794 << R"(<cross-domain-policy>)" << "\n"
11fdf7f2 1795 << g_conf()->rgw_cross_domain_policy << "\n"
7c673cae
FG
1796 << R"(</cross-domain-policy>)";
1797
1798 dump_body(s, ss.str());
1799}
1800
1801void RGWGetHealthCheck_ObjStore_SWIFT::send_response()
1802{
1803 set_req_state_err(s, op_ret);
1804 dump_errno(s);
1805 end_header(s, this, "application/xml");
1806
1807 if (op_ret) {
1808 static constexpr char DISABLED[] = "DISABLED BY FILE";
1809 dump_body(s, DISABLED, strlen(DISABLED));
1810 }
1811}
1812
1813const vector<pair<string, RGWInfo_ObjStore_SWIFT::info>> RGWInfo_ObjStore_SWIFT::swift_info =
1814{
1815 {"bulk_delete", {false, nullptr}},
1816 {"container_quotas", {false, nullptr}},
1817 {"swift", {false, RGWInfo_ObjStore_SWIFT::list_swift_data}},
1818 {"tempurl", { false, RGWInfo_ObjStore_SWIFT::list_tempurl_data}},
1819 {"slo", {false, RGWInfo_ObjStore_SWIFT::list_slo_data}},
1820 {"account_quotas", {false, nullptr}},
1821 {"staticweb", {false, nullptr}},
224ce89b 1822 {"tempauth", {false, RGWInfo_ObjStore_SWIFT::list_tempauth_data}},
7c673cae
FG
1823};
1824
f67539c2 1825void RGWInfo_ObjStore_SWIFT::execute(optional_yield y)
7c673cae
FG
1826{
1827 bool is_admin_info_enabled = false;
1828
1829 const string& swiftinfo_sig = s->info.args.get("swiftinfo_sig");
1830 const string& swiftinfo_expires = s->info.args.get("swiftinfo_expires");
1831
1832 if (!swiftinfo_sig.empty() &&
1833 !swiftinfo_expires.empty() &&
9f95a23c 1834 !is_expired(swiftinfo_expires, this)) {
7c673cae
FG
1835 is_admin_info_enabled = true;
1836 }
1837
1838 s->formatter->open_object_section("info");
1839
1840 for (const auto& pair : swift_info) {
1841 if(!is_admin_info_enabled && pair.second.is_admin_info)
1842 continue;
1843
1844 if (!pair.second.list_data) {
1845 s->formatter->open_object_section((pair.first).c_str());
1846 s->formatter->close_section();
1847 }
1848 else {
1e59de90 1849 pair.second.list_data(*(s->formatter), s->cct->_conf, driver);
7c673cae
FG
1850 }
1851 }
1852
1853 s->formatter->close_section();
1854}
1855
1856void RGWInfo_ObjStore_SWIFT::send_response()
1857{
1858 if (op_ret < 0) {
1859 op_ret = STATUS_NO_CONTENT;
1860 }
1861 set_req_state_err(s, op_ret);
1862 dump_errno(s);
1863 end_header(s, this);
1864 rgw_flush_formatter_and_reset(s, s->formatter);
1865}
1866
1867void RGWInfo_ObjStore_SWIFT::list_swift_data(Formatter& formatter,
11fdf7f2 1868 const ConfigProxy& config,
1e59de90 1869 rgw::sal::Driver* driver)
7c673cae
FG
1870{
1871 formatter.open_object_section("swift");
11fdf7f2 1872 formatter.dump_int("max_file_size", config->rgw_max_put_size);
7c673cae
FG
1873 formatter.dump_int("container_listing_limit", RGW_LIST_BUCKETS_LIMIT_MAX);
1874
1875 string ceph_version(CEPH_GIT_NICE_VER);
1876 formatter.dump_string("version", ceph_version);
3efd9988
FG
1877
1878 const size_t max_attr_name_len = \
11fdf7f2 1879 g_conf().get_val<Option::size_t>("rgw_max_attr_name_len");
3efd9988
FG
1880 if (max_attr_name_len) {
1881 const size_t meta_name_limit = \
1882 max_attr_name_len - strlen(RGW_ATTR_PREFIX RGW_AMZ_META_PREFIX);
1883 formatter.dump_int("max_meta_name_length", meta_name_limit);
1884 }
1885
11fdf7f2 1886 const size_t meta_value_limit = g_conf().get_val<Option::size_t>("rgw_max_attr_size");
3efd9988
FG
1887 if (meta_value_limit) {
1888 formatter.dump_int("max_meta_value_length", meta_value_limit);
1889 }
1890
1891 const size_t meta_num_limit = \
11fdf7f2 1892 g_conf().get_val<uint64_t>("rgw_max_attrs_num_in_req");
3efd9988
FG
1893 if (meta_num_limit) {
1894 formatter.dump_int("max_meta_count", meta_num_limit);
1895 }
7c673cae
FG
1896
1897 formatter.open_array_section("policies");
1e59de90 1898 const rgw::sal::ZoneGroup& zonegroup = driver->get_zone()->get_zonegroup();
7c673cae 1899
1e59de90 1900 std::set<std::string> targets;
aee94f69
TL
1901 zonegroup.get_placement_target_names(targets);
1902 for (const auto& placement_targets : targets) {
1903 formatter.open_object_section("policy");
1904 if (placement_targets.compare(zonegroup.get_default_placement_name()) == 0)
1905 formatter.dump_bool("default", true);
1906 formatter.dump_string("name", placement_targets.c_str());
1907 formatter.close_section();
7c673cae
FG
1908 }
1909 formatter.close_section();
1910
1911 formatter.dump_int("max_object_name_size", RGWHandler_REST::MAX_OBJ_NAME_LEN);
1912 formatter.dump_bool("strict_cors_mode", true);
1913 formatter.dump_int("max_container_name_length", RGWHandler_REST::MAX_BUCKET_NAME_LEN);
1914 formatter.close_section();
1915}
1916
224ce89b 1917void RGWInfo_ObjStore_SWIFT::list_tempauth_data(Formatter& formatter,
11fdf7f2 1918 const ConfigProxy& config,
1e59de90 1919 rgw::sal::Driver* driver)
224ce89b
WB
1920{
1921 formatter.open_object_section("tempauth");
1922 formatter.dump_bool("account_acls", true);
1923 formatter.close_section();
1924}
7c673cae 1925void RGWInfo_ObjStore_SWIFT::list_tempurl_data(Formatter& formatter,
11fdf7f2 1926 const ConfigProxy& config,
1e59de90 1927 rgw::sal::Driver* driver)
7c673cae
FG
1928{
1929 formatter.open_object_section("tempurl");
1930 formatter.open_array_section("methods");
1931 formatter.dump_string("methodname", "GET");
1932 formatter.dump_string("methodname", "HEAD");
1933 formatter.dump_string("methodname", "PUT");
1934 formatter.dump_string("methodname", "POST");
1935 formatter.dump_string("methodname", "DELETE");
1936 formatter.close_section();
1937 formatter.close_section();
1938}
1939
1940void RGWInfo_ObjStore_SWIFT::list_slo_data(Formatter& formatter,
11fdf7f2 1941 const ConfigProxy& config,
1e59de90 1942 rgw::sal::Driver* driver)
7c673cae
FG
1943{
1944 formatter.open_object_section("slo");
11fdf7f2 1945 formatter.dump_int("max_manifest_segments", config->rgw_max_slo_entries);
7c673cae
FG
1946 formatter.close_section();
1947}
1948
9f95a23c 1949bool RGWInfo_ObjStore_SWIFT::is_expired(const std::string& expires, const DoutPrefixProvider *dpp)
7c673cae
FG
1950{
1951 string err;
1952 const utime_t now = ceph_clock_now();
1953 const uint64_t expiration = (uint64_t)strict_strtoll(expires.c_str(),
1954 10, &err);
1955 if (!err.empty()) {
9f95a23c 1956 ldpp_dout(dpp, 5) << "failed to parse siginfo_expires: " << err << dendl;
7c673cae
FG
1957 return true;
1958 }
1959
1960 if (expiration <= (uint64_t)now.sec()) {
9f95a23c 1961 ldpp_dout(dpp, 5) << "siginfo expired: " << expiration << " <= " << now.sec() << dendl;
7c673cae
FG
1962 return true;
1963 }
1964
1965 return false;
1966}
1967
1968
1e59de90 1969void RGWFormPost::init(rgw::sal::Driver* const driver,
7c673cae
FG
1970 req_state* const s,
1971 RGWHandler* const dialect_handler)
1972{
1e59de90
TL
1973 if (!rgw::sal::Object::empty(s->object)) {
1974 prefix = std::move(s->object->get_name());
1975 s->object->set_key(rgw_obj_key());
1976 }
7c673cae 1977
1e59de90 1978 return RGWPostObj_ObjStore::init(driver, s, dialect_handler);
7c673cae
FG
1979}
1980
1981std::size_t RGWFormPost::get_max_file_size() /*const*/
1982{
1983 std::string max_str = get_part_str(ctrl_parts, "max_file_size", "0");
1984
1985 std::string err;
1986 const std::size_t max_file_size =
1987 static_cast<uint64_t>(strict_strtoll(max_str.c_str(), 10, &err));
1988
1989 if (! err.empty()) {
9f95a23c 1990 ldpp_dout(this, 5) << "failed to parse FormPost's max_file_size: " << err
7c673cae
FG
1991 << dendl;
1992 return 0;
1993 }
1994
1995 return max_file_size;
1996}
1997
1998bool RGWFormPost::is_non_expired()
1999{
2000 std::string expires = get_part_str(ctrl_parts, "expires", "0");
2001
2002 std::string err;
2003 const uint64_t expires_timestamp =
2004 static_cast<uint64_t>(strict_strtoll(expires.c_str(), 10, &err));
2005
2006 if (! err.empty()) {
9f95a23c 2007 ldpp_dout(this, 5) << "failed to parse FormPost's expires: " << err << dendl;
7c673cae
FG
2008 return false;
2009 }
2010
2011 const utime_t now = ceph_clock_now();
1e59de90 2012 if (std::cmp_less_equal(expires_timestamp, now.sec())) {
9f95a23c 2013 ldpp_dout(this, 5) << "FormPost form expired: "
7c673cae
FG
2014 << expires_timestamp << " <= " << now.sec() << dendl;
2015 return false;
2016 }
2017
2018 return true;
2019}
2020
2021bool RGWFormPost::is_integral()
2022{
2023 const std::string form_signature = get_part_str(ctrl_parts, "signature");
2024
31f18b77 2025 try {
9f95a23c 2026 get_owner_info(s, s->user->get_info());
31f18b77
FG
2027 s->auth.identity = rgw::auth::transform_old_authinfo(s);
2028 } catch (...) {
9f95a23c 2029 ldpp_dout(this, 5) << "cannot get user_info of account's owner" << dendl;
31f18b77
FG
2030 return false;
2031 }
2032
9f95a23c 2033 for (const auto& kv : s->user->get_info().temp_url_keys) {
7c673cae
FG
2034 const int temp_url_key_num = kv.first;
2035 const string& temp_url_key = kv.second;
2036
2037 if (temp_url_key.empty()) {
2038 continue;
2039 }
2040
2041 SignatureHelper sig_helper;
2042 sig_helper.calc(temp_url_key,
2043 s->info.request_uri,
2044 get_part_str(ctrl_parts, "redirect"),
2045 get_part_str(ctrl_parts, "max_file_size", "0"),
2046 get_part_str(ctrl_parts, "max_file_count", "0"),
2047 get_part_str(ctrl_parts, "expires", "0"));
2048
2049 const auto local_sig = sig_helper.get_signature();
2050
9f95a23c 2051 ldpp_dout(this, 20) << "FormPost signature [" << temp_url_key_num << "]"
7c673cae
FG
2052 << " (calculated): " << local_sig << dendl;
2053
2054 if (sig_helper.is_equal_to(form_signature)) {
2055 return true;
2056 } else {
9f95a23c 2057 ldpp_dout(this, 5) << "FormPost's signature mismatch: "
7c673cae
FG
2058 << local_sig << " != " << form_signature << dendl;
2059 }
2060 }
2061
2062 return false;
2063}
2064
31f18b77
FG
2065void RGWFormPost::get_owner_info(const req_state* const s,
2066 RGWUserInfo& owner_info) const
2067{
2068 /* We cannot use req_state::bucket_name because it isn't available
2069 * now. It will be initialized in RGWHandler_REST_SWIFT::postauth_init(). */
2070 const string& bucket_name = s->init_state.url_bucket;
2071
20effc67 2072 std::unique_ptr<rgw::sal::User> user;
9f95a23c 2073
31f18b77
FG
2074 /* TempURL in Formpost only requires that bucket name is specified. */
2075 if (bucket_name.empty()) {
2076 throw -EPERM;
2077 }
2078
31f18b77
FG
2079 if (!s->account_name.empty()) {
2080 RGWUserInfo uinfo;
2081 bool found = false;
2082
2083 const rgw_user uid(s->account_name);
2084 if (uid.tenant.empty()) {
2085 const rgw_user tenanted_uid(uid.id, uid.id);
1e59de90 2086 user = driver->get_user(tenanted_uid);
31f18b77 2087
20effc67 2088 if (user->load_user(s, s->yield) >= 0) {
31f18b77 2089 /* Succeeded. */
31f18b77
FG
2090 found = true;
2091 }
2092 }
2093
20effc67 2094 if (!found) {
1e59de90 2095 user = driver->get_user(uid);
20effc67
TL
2096 if (user->load_user(s, s->yield) < 0) {
2097 throw -EPERM;
2098 }
31f18b77
FG
2099 }
2100 }
2101
2102 /* Need to get user info of bucket owner. */
20effc67 2103 std::unique_ptr<rgw::sal::Bucket> bucket;
1e59de90 2104 int ret = driver->get_bucket(s, user.get(), user->get_tenant(), bucket_name, &bucket, s->yield);
31f18b77
FG
2105 if (ret < 0) {
2106 throw ret;
2107 }
2108
20effc67 2109 ldpp_dout(this, 20) << "temp url user (bucket owner): " << bucket->get_info().owner
31f18b77
FG
2110 << dendl;
2111
1e59de90 2112 user = driver->get_user(bucket->get_info().owner);
20effc67 2113 if (user->load_user(s, s->yield) < 0) {
31f18b77
FG
2114 throw -EPERM;
2115 }
20effc67
TL
2116
2117 owner_info = user->get_info();
31f18b77
FG
2118}
2119
f67539c2 2120int RGWFormPost::get_params(optional_yield y)
7c673cae
FG
2121{
2122 /* The parentt class extracts boundary info from the Content-Type. */
f67539c2 2123 int ret = RGWPostObj_ObjStore::get_params(y);
7c673cae
FG
2124 if (ret < 0) {
2125 return ret;
2126 }
2127
9f95a23c 2128 policy.create_default(s->user->get_id(), s->user->get_display_name());
7c673cae
FG
2129
2130 /* Let's start parsing the HTTP body by parsing each form part step-
2131 * by-step till encountering the first part with file data. */
2132 do {
2133 struct post_form_part part;
2134 ret = read_form_part_header(&part, stream_done);
2135 if (ret < 0) {
2136 return ret;
2137 }
2138
11fdf7f2 2139 if (s->cct->_conf->subsys.should_gather<ceph_subsys_rgw, 20>()) {
9f95a23c 2140 ldpp_dout(this, 20) << "read part header -- part.name="
7c673cae
FG
2141 << part.name << dendl;
2142
2143 for (const auto& pair : part.fields) {
9f95a23c
TL
2144 ldpp_dout(this, 20) << "field.name=" << pair.first << dendl;
2145 ldpp_dout(this, 20) << "field.val=" << pair.second.val << dendl;
2146 ldpp_dout(this, 20) << "field.params:" << dendl;
7c673cae
FG
2147
2148 for (const auto& param_pair : pair.second.params) {
9f95a23c 2149 ldpp_dout(this, 20) << " " << param_pair.first
7c673cae
FG
2150 << " -> " << param_pair.second << dendl;
2151 }
2152 }
2153 }
2154
2155 if (stream_done) {
2156 /* Unexpected here. */
2157 err_msg = "Malformed request";
2158 return -EINVAL;
2159 }
2160
2161 const auto field_iter = part.fields.find("Content-Disposition");
2162 if (std::end(part.fields) != field_iter &&
2163 std::end(field_iter->second.params) != field_iter->second.params.find("filename")) {
2164 /* First data part ahead. */
2165 current_data_part = std::move(part);
2166
2167 /* Stop the iteration. We can assume that all control parts have been
2168 * already parsed. The rest of HTTP body should contain data parts
2169 * only. They will be picked up by ::get_data(). */
2170 break;
2171 } else {
1e59de90 2172 /* Control part ahead. Receive, parse and driver for later usage. */
7c673cae
FG
2173 bool boundary;
2174 ret = read_data(part.data, s->cct->_conf->rgw_max_chunk_size,
2175 boundary, stream_done);
2176 if (ret < 0) {
2177 return ret;
2178 } else if (! boundary) {
2179 err_msg = "Couldn't find boundary";
2180 return -EINVAL;
2181 }
2182
2183 ctrl_parts[part.name] = std::move(part);
2184 }
2185 } while (! stream_done);
2186
2187 min_len = 0;
2188 max_len = get_max_file_size();
2189
2190 if (! current_data_part) {
2191 err_msg = "FormPost: no files to process";
2192 return -EINVAL;
2193 }
2194
2195 if (! is_non_expired()) {
2196 err_msg = "FormPost: Form Expired";
2197 return -EPERM;
2198 }
2199
2200 if (! is_integral()) {
2201 err_msg = "FormPost: Invalid Signature";
2202 return -EPERM;
2203 }
2204
2205 return 0;
2206}
2207
2208std::string RGWFormPost::get_current_filename() const
2209{
2210 try {
2211 const auto& field = current_data_part->fields.at("Content-Disposition");
2212 const auto iter = field.params.find("filename");
2213
2214 if (std::end(field.params) != iter) {
2215 return prefix + iter->second;
2216 }
2217 } catch (std::out_of_range&) {
2218 /* NOP */;
2219 }
2220
2221 return prefix;
2222}
2223
2224std::string RGWFormPost::get_current_content_type() const
2225{
2226 try {
2227 const auto& field = current_data_part->fields.at("Content-Type");
2228 return field.val;
2229 } catch (std::out_of_range&) {
2230 /* NOP */;
2231 }
2232
2233 return std::string();
2234}
2235
2236bool RGWFormPost::is_next_file_to_upload()
2237{
2238 if (! stream_done) {
2239 /* We have at least one additional part in the body. */
2240 struct post_form_part part;
2241 int r = read_form_part_header(&part, stream_done);
2242 if (r < 0) {
2243 return false;
2244 }
2245
2246 const auto field_iter = part.fields.find("Content-Disposition");
2247 if (std::end(part.fields) != field_iter) {
2248 const auto& params = field_iter->second.params;
31f18b77 2249 const auto& filename_iter = params.find("filename");
7c673cae 2250
31f18b77 2251 if (std::end(params) != filename_iter && ! filename_iter->second.empty()) {
7c673cae
FG
2252 current_data_part = std::move(part);
2253 return true;
2254 }
2255 }
2256 }
2257
2258 return false;
2259}
2260
2261int RGWFormPost::get_data(ceph::bufferlist& bl, bool& again)
2262{
2263 bool boundary;
2264
2265 int r = read_data(bl, s->cct->_conf->rgw_max_chunk_size,
2266 boundary, stream_done);
2267 if (r < 0) {
2268 return r;
2269 }
2270
f67539c2 2271 /* Tell RGWPostObj::execute(optional_yield y) that it has some data to put. */
7c673cae
FG
2272 again = !boundary;
2273
2274 return bl.length();
2275}
2276
2277void RGWFormPost::send_response()
2278{
2279 std::string redirect = get_part_str(ctrl_parts, "redirect");
2280 if (! redirect.empty()) {
2281 op_ret = STATUS_REDIRECT;
2282 }
2283
2284 set_req_state_err(s, op_ret);
31f18b77 2285 s->err.err_code = err_msg;
7c673cae
FG
2286 dump_errno(s);
2287 if (! redirect.empty()) {
2288 dump_redirect(s, redirect);
2289 }
2290 end_header(s, this);
2291}
2292
2293bool RGWFormPost::is_formpost_req(req_state* const s)
2294{
2295 std::string content_type;
2296 std::map<std::string, std::string> params;
2297
2298 parse_boundary_params(s->info.env->get("CONTENT_TYPE", ""),
2299 content_type, params);
2300
2301 return boost::algorithm::iequals(content_type, "multipart/form-data") &&
2302 params.count("boundary") > 0;
2303}
2304
2305
2306RGWOp *RGWHandler_REST_Service_SWIFT::op_get()
2307{
2308 return new RGWListBuckets_ObjStore_SWIFT;
2309}
2310
2311RGWOp *RGWHandler_REST_Service_SWIFT::op_head()
2312{
2313 return new RGWStatAccount_ObjStore_SWIFT;
2314}
2315
2316RGWOp *RGWHandler_REST_Service_SWIFT::op_put()
2317{
2318 if (s->info.args.exists("extract-archive")) {
2319 return new RGWBulkUploadOp_ObjStore_SWIFT;
2320 }
2321 return nullptr;
2322}
2323
2324RGWOp *RGWHandler_REST_Service_SWIFT::op_post()
2325{
2326 if (s->info.args.exists("bulk-delete")) {
2327 return new RGWBulkDelete_ObjStore_SWIFT;
2328 }
2329 return new RGWPutMetadataAccount_ObjStore_SWIFT;
2330}
2331
2332RGWOp *RGWHandler_REST_Service_SWIFT::op_delete()
2333{
2334 if (s->info.args.exists("bulk-delete")) {
2335 return new RGWBulkDelete_ObjStore_SWIFT;
2336 }
2337 return NULL;
2338}
2339
2340int RGWSwiftWebsiteHandler::serve_errordoc(const int http_ret,
f67539c2
TL
2341 const std::string error_doc,
2342 optional_yield y)
7c673cae
FG
2343{
2344 /* Try to throw it all away. */
2345 s->formatter->reset();
2346
2347 class RGWGetErrorPage : public RGWGetObj_ObjStore_SWIFT {
2348 public:
1e59de90 2349 RGWGetErrorPage(rgw::sal::Driver* const driver,
7c673cae
FG
2350 RGWHandler_REST* const handler,
2351 req_state* const s,
2352 const int http_ret) {
2353 /* Calling a virtual from the base class is safe as the subobject should
2354 * be properly initialized and we haven't overridden the init method. */
1e59de90 2355 init(driver, s, handler);
7c673cae
FG
2356 set_get_data(true);
2357 set_custom_http_response(http_ret);
2358 }
2359
2360 int error_handler(const int err_no,
f67539c2 2361 std::string* const error_content, optional_yield y) override {
7c673cae
FG
2362 /* Enforce that any error generated while getting the error page will
2363 * not be send to a client. This allows us to recover from the double
2364 * fault situation by sending the original message. */
2365 return 0;
2366 }
1e59de90 2367 } get_errpage_op(driver, handler, s, http_ret);
7c673cae 2368
20effc67
TL
2369 /* This is okay. It's an error, so nothing will run after this, and it can be
2370 * called by abort_early(), which can be called before s->object or s->bucket
2371 * are set up. */
2372 if (!rgw::sal::Bucket::empty(s->bucket.get())) {
f67539c2
TL
2373 s->object = s->bucket->get_object(rgw_obj_key(std::to_string(http_ret) + error_doc));
2374 } else {
1e59de90 2375 s->object = driver->get_object(rgw_obj_key(std::to_string(http_ret) + error_doc));
f67539c2 2376 }
7c673cae
FG
2377
2378 RGWOp* newop = &get_errpage_op;
2379 RGWRequest req(0);
1e59de90 2380 return rgw_process_authenticated(handler, newop, &req, s, y, driver, true);
7c673cae
FG
2381}
2382
2383int RGWSwiftWebsiteHandler::error_handler(const int err_no,
f67539c2
TL
2384 std::string* const error_content,
2385 optional_yield y)
7c673cae 2386{
f67539c2
TL
2387 if (!s->bucket.get()) {
2388 /* No bucket, default no-op handler */
2389 return err_no;
2390 }
2391
2392 const auto& ws_conf = s->bucket->get_info().website_conf;
7c673cae
FG
2393
2394 if (can_be_website_req() && ! ws_conf.error_doc.empty()) {
31f18b77 2395 set_req_state_err(s, err_no);
f67539c2 2396 return serve_errordoc(s->err.http_ret, ws_conf.error_doc, y);
7c673cae
FG
2397 }
2398
2399 /* Let's go to the default, no-op handler. */
2400 return err_no;
2401}
2402
2403bool RGWSwiftWebsiteHandler::is_web_mode() const
2404{
f67539c2 2405 const std::string_view webmode = s->info.env->get("HTTP_X_WEB_MODE", "");
7c673cae
FG
2406 return boost::algorithm::iequals(webmode, "true");
2407}
2408
2409bool RGWSwiftWebsiteHandler::can_be_website_req() const
2410{
2411 /* Static website works only with the GET or HEAD method. Nothing more. */
f67539c2 2412 static const std::set<std::string_view> ws_methods = { "GET", "HEAD" };
7c673cae
FG
2413 if (ws_methods.count(s->info.method) == 0) {
2414 return false;
2415 }
2416
2417 /* We also need to handle early failures from the auth system. In such cases
2418 * req_state::auth.identity may be empty. Let's treat that the same way as
2419 * the anonymous access. */
2420 if (! s->auth.identity) {
2421 return true;
2422 }
2423
2424 /* Swift serves websites only for anonymous requests unless client explicitly
2425 * requested this behaviour by supplying X-Web-Mode HTTP header set to true. */
2426 if (s->auth.identity->is_anonymous() || is_web_mode()) {
2427 return true;
2428 }
2429
2430 return false;
2431}
2432
2433RGWOp* RGWSwiftWebsiteHandler::get_ws_redirect_op()
2434{
2435 class RGWMovedPermanently: public RGWOp {
2436 const std::string location;
2437 public:
11fdf7f2 2438 explicit RGWMovedPermanently(const std::string& location)
7c673cae
FG
2439 : location(location) {
2440 }
2441
f67539c2 2442 int verify_permission(optional_yield) override {
7c673cae
FG
2443 return 0;
2444 }
2445
f67539c2 2446 void execute(optional_yield) override {
7c673cae
FG
2447 op_ret = -ERR_PERMANENT_REDIRECT;
2448 return;
2449 }
2450
2451 void send_response() override {
2452 set_req_state_err(s, op_ret);
2453 dump_errno(s);
2454 dump_content_length(s, 0);
2455 dump_redirect(s, location);
2456 end_header(s, this);
2457 }
2458
11fdf7f2 2459 const char* name() const override {
7c673cae
FG
2460 return "RGWMovedPermanently";
2461 }
2462 };
2463
2464 return new RGWMovedPermanently(s->info.request_uri + '/');
2465}
2466
2467RGWOp* RGWSwiftWebsiteHandler::get_ws_index_op()
2468{
2469 /* Retarget to get obj on requested index file. */
f67539c2
TL
2470 if (! s->object->empty()) {
2471 s->object->set_name(s->object->get_name() +
2472 s->bucket->get_info().website_conf.get_index_doc());
7c673cae 2473 } else {
f67539c2 2474 s->object->set_name(s->bucket->get_info().website_conf.get_index_doc());
7c673cae
FG
2475 }
2476
2477 auto getop = new RGWGetObj_ObjStore_SWIFT;
2478 getop->set_get_data(boost::algorithm::equals("GET", s->info.method));
2479
2480 return getop;
2481}
2482
2483RGWOp* RGWSwiftWebsiteHandler::get_ws_listing_op()
2484{
2485 class RGWWebsiteListing : public RGWListBucket_ObjStore_SWIFT {
2486 const std::string prefix_override;
2487
f67539c2 2488 int get_params(optional_yield) override {
7c673cae
FG
2489 prefix = prefix_override;
2490 max = default_max;
2491 delimiter = "/";
2492 return 0;
2493 }
2494
2495 void send_response() override {
2496 /* Generate the header now. */
2497 set_req_state_err(s, op_ret);
2498 dump_errno(s);
1e59de90 2499 dump_container_metadata(s, s->bucket.get(), quota.bucket_quota,
f67539c2 2500 s->bucket->get_info().website_conf);
7c673cae
FG
2501 end_header(s, this, "text/html");
2502 if (op_ret < 0) {
2503 return;
2504 }
2505
2506 /* Now it's the time to start generating HTML bucket listing.
2507 * All the crazy stuff with crafting tags will be delegated to
2508 * RGWSwiftWebsiteListingFormatter. */
2509 std::stringstream ss;
2510 RGWSwiftWebsiteListingFormatter htmler(ss, prefix);
2511
f67539c2 2512 const auto& ws_conf = s->bucket->get_info().website_conf;
7c673cae
FG
2513 htmler.generate_header(s->decoded_uri,
2514 ws_conf.listing_css_doc);
2515
2516 for (const auto& pair : common_prefixes) {
2517 std::string subdir_name = pair.first;
2518 if (! subdir_name.empty()) {
2519 /* To be compliant with Swift we need to remove the trailing
2520 * slash. */
2521 subdir_name.pop_back();
2522 }
2523
2524 htmler.dump_subdir(subdir_name);
2525 }
2526
2527 for (const rgw_bucket_dir_entry& obj : objs) {
2528 if (! common_prefixes.count(obj.key.name + '/')) {
2529 htmler.dump_object(obj);
2530 }
2531 }
2532
2533 htmler.generate_footer();
2534 dump_body(s, ss.str());
2535 }
2536 public:
2537 /* Taking prefix_override by value to leverage std::string r-value ref
2538 * ctor and thus avoid extra memory copying/increasing ref counter. */
11fdf7f2 2539 explicit RGWWebsiteListing(std::string prefix_override)
7c673cae
FG
2540 : prefix_override(std::move(prefix_override)) {
2541 }
2542 };
2543
f67539c2
TL
2544 std::string prefix = std::move(s->object->get_name());
2545 s->object->set_key(rgw_obj_key());
7c673cae
FG
2546
2547 return new RGWWebsiteListing(std::move(prefix));
2548}
2549
2550bool RGWSwiftWebsiteHandler::is_web_dir() const
2551{
f67539c2 2552 std::string subdir_name = url_decode(s->object->get_name());
7c673cae
FG
2553
2554 /* Remove character from the subdir name if it is "/". */
2555 if (subdir_name.empty()) {
2556 return false;
2557 } else if (subdir_name.back() == '/') {
2558 subdir_name.pop_back();
18d92ca7
TL
2559 if (subdir_name.empty()) {
2560 return false;
2561 }
7c673cae
FG
2562 }
2563
20effc67 2564 std::unique_ptr<rgw::sal::Object> obj = s->bucket->get_object(rgw_obj_key(std::move(subdir_name)));
7c673cae
FG
2565
2566 /* First, get attrset of the object we'll try to retrieve. */
1e59de90
TL
2567 obj->set_atomic();
2568 obj->set_prefetch_data();
7c673cae
FG
2569
2570 RGWObjState* state = nullptr;
1e59de90 2571 if (obj->get_obj_state(s, &state, s->yield, false)) {
7c673cae
FG
2572 return false;
2573 }
2574
2575 /* A nonexistent object cannot be a considered as a marker representing
2576 * the emulation of catalog in FS hierarchy. */
2577 if (! state->exists) {
2578 return false;
2579 }
2580
2581 /* Decode the content type. */
2582 std::string content_type;
2583 get_contype_from_attrs(state->attrset, content_type);
2584
f67539c2 2585 const auto& ws_conf = s->bucket->get_info().website_conf;
7c673cae
FG
2586 const std::string subdir_marker = ws_conf.subdir_marker.empty()
2587 ? "application/directory"
2588 : ws_conf.subdir_marker;
2589 return subdir_marker == content_type && state->size <= 1;
2590}
2591
9f95a23c 2592bool RGWSwiftWebsiteHandler::is_index_present(const std::string& index) const
7c673cae 2593{
20effc67 2594 std::unique_ptr<rgw::sal::Object> obj = s->bucket->get_object(rgw_obj_key(index));
7c673cae 2595
1e59de90
TL
2596 obj->set_atomic();
2597 obj->set_prefetch_data();
7c673cae
FG
2598
2599 RGWObjState* state = nullptr;
1e59de90 2600 if (obj->get_obj_state(s, &state, s->yield, false)) {
7c673cae
FG
2601 return false;
2602 }
2603
2604 /* A nonexistent object cannot be a considered as a viable index. We will
2605 * try to list the bucket or - if this is impossible - return an error. */
2606 return state->exists;
2607}
2608
2609int RGWSwiftWebsiteHandler::retarget_bucket(RGWOp* op, RGWOp** new_op)
2610{
9f95a23c 2611 ldpp_dout(s, 10) << "Starting retarget" << dendl;
7c673cae
FG
2612 RGWOp* op_override = nullptr;
2613
2614 /* In Swift static web content is served if the request is anonymous or
2615 * has X-Web-Mode HTTP header specified to true. */
2616 if (can_be_website_req()) {
f67539c2
TL
2617 const auto& ws_conf = s->bucket->get_info().website_conf;
2618 const auto& index = s->bucket->get_info().website_conf.get_index_doc();
7c673cae
FG
2619
2620 if (s->decoded_uri.back() != '/') {
2621 op_override = get_ws_redirect_op();
2622 } else if (! index.empty() && is_index_present(index)) {
2623 op_override = get_ws_index_op();
2624 } else if (ws_conf.listing_enabled) {
2625 op_override = get_ws_listing_op();
2626 }
2627 }
2628
2629 if (op_override) {
2630 handler->put_op(op);
1e59de90 2631 op_override->init(driver, s, handler);
7c673cae
FG
2632
2633 *new_op = op_override;
2634 } else {
2635 *new_op = op;
2636 }
2637
2638 /* Return 404 Not Found is the request has web mode enforced but we static web
2639 * wasn't able to serve it accordingly. */
2640 return ! op_override && is_web_mode() ? -ENOENT : 0;
2641}
2642
2643int RGWSwiftWebsiteHandler::retarget_object(RGWOp* op, RGWOp** new_op)
2644{
9f95a23c 2645 ldpp_dout(s, 10) << "Starting object retarget" << dendl;
7c673cae
FG
2646 RGWOp* op_override = nullptr;
2647
2648 /* In Swift static web content is served if the request is anonymous or
2649 * has X-Web-Mode HTTP header specified to true. */
2650 if (can_be_website_req() && is_web_dir()) {
f67539c2
TL
2651 const auto& ws_conf = s->bucket->get_info().website_conf;
2652 const auto& index = s->bucket->get_info().website_conf.get_index_doc();
7c673cae
FG
2653
2654 if (s->decoded_uri.back() != '/') {
2655 op_override = get_ws_redirect_op();
2656 } else if (! index.empty() && is_index_present(index)) {
2657 op_override = get_ws_index_op();
2658 } else if (ws_conf.listing_enabled) {
2659 op_override = get_ws_listing_op();
2660 }
2661 } else {
2662 /* A regular request or the specified object isn't a subdirectory marker.
2663 * We don't need any re-targeting. Error handling (like sending a custom
2664 * error page) will be performed by error_handler of the actual RGWOp. */
2665 return 0;
2666 }
2667
2668 if (op_override) {
2669 handler->put_op(op);
1e59de90 2670 op_override->init(driver, s, handler);
7c673cae
FG
2671
2672 *new_op = op_override;
2673 } else {
2674 *new_op = op;
2675 }
2676
2677 /* Return 404 Not Found if we aren't able to re-target for subdir marker. */
2678 return ! op_override ? -ENOENT : 0;
2679}
2680
2681
2682RGWOp *RGWHandler_REST_Bucket_SWIFT::get_obj_op(bool get_data)
2683{
2684 if (is_acl_op()) {
2685 return new RGWGetACLs_ObjStore_SWIFT;
2686 }
2687
2688 if (get_data)
2689 return new RGWListBucket_ObjStore_SWIFT;
2690 else
2691 return new RGWStatBucket_ObjStore_SWIFT;
2692}
2693
2694RGWOp *RGWHandler_REST_Bucket_SWIFT::op_get()
2695{
2696 return get_obj_op(true);
2697}
2698
2699RGWOp *RGWHandler_REST_Bucket_SWIFT::op_head()
2700{
2701 return get_obj_op(false);
2702}
2703
2704RGWOp *RGWHandler_REST_Bucket_SWIFT::op_put()
2705{
2706 if (is_acl_op()) {
2707 return new RGWPutACLs_ObjStore_SWIFT;
2708 }
2709 if(s->info.args.exists("extract-archive")) {
2710 return new RGWBulkUploadOp_ObjStore_SWIFT;
2711 }
2712 return new RGWCreateBucket_ObjStore_SWIFT;
2713}
2714
2715RGWOp *RGWHandler_REST_Bucket_SWIFT::op_delete()
2716{
2717 return new RGWDeleteBucket_ObjStore_SWIFT;
2718}
2719
2720RGWOp *RGWHandler_REST_Bucket_SWIFT::op_post()
2721{
2722 if (RGWFormPost::is_formpost_req(s)) {
2723 return new RGWFormPost;
2724 } else {
2725 return new RGWPutMetadataBucket_ObjStore_SWIFT;
2726 }
2727}
2728
2729RGWOp *RGWHandler_REST_Bucket_SWIFT::op_options()
2730{
2731 return new RGWOptionsCORS_ObjStore_SWIFT;
2732}
2733
2734
2735RGWOp *RGWHandler_REST_Obj_SWIFT::get_obj_op(bool get_data)
2736{
2737 if (is_acl_op()) {
2738 return new RGWGetACLs_ObjStore_SWIFT;
2739 }
2740
2741 RGWGetObj_ObjStore_SWIFT *get_obj_op = new RGWGetObj_ObjStore_SWIFT;
2742 get_obj_op->set_get_data(get_data);
2743 return get_obj_op;
2744}
2745
2746RGWOp *RGWHandler_REST_Obj_SWIFT::op_get()
2747{
2748 return get_obj_op(true);
2749}
2750
2751RGWOp *RGWHandler_REST_Obj_SWIFT::op_head()
2752{
2753 return get_obj_op(false);
2754}
2755
2756RGWOp *RGWHandler_REST_Obj_SWIFT::op_put()
2757{
2758 if (is_acl_op()) {
2759 return new RGWPutACLs_ObjStore_SWIFT;
2760 }
2761 if(s->info.args.exists("extract-archive")) {
2762 return new RGWBulkUploadOp_ObjStore_SWIFT;
2763 }
2764 if (s->init_state.src_bucket.empty())
2765 return new RGWPutObj_ObjStore_SWIFT;
2766 else
2767 return new RGWCopyObj_ObjStore_SWIFT;
2768}
2769
2770RGWOp *RGWHandler_REST_Obj_SWIFT::op_delete()
2771{
2772 return new RGWDeleteObj_ObjStore_SWIFT;
2773}
2774
2775RGWOp *RGWHandler_REST_Obj_SWIFT::op_post()
2776{
2777 if (RGWFormPost::is_formpost_req(s)) {
2778 return new RGWFormPost;
2779 } else {
2780 return new RGWPutMetadataObject_ObjStore_SWIFT;
2781 }
2782}
2783
2784RGWOp *RGWHandler_REST_Obj_SWIFT::op_copy()
2785{
2786 return new RGWCopyObj_ObjStore_SWIFT;
2787}
2788
2789RGWOp *RGWHandler_REST_Obj_SWIFT::op_options()
2790{
2791 return new RGWOptionsCORS_ObjStore_SWIFT;
2792}
2793
2794
f67539c2 2795int RGWHandler_REST_SWIFT::authorize(const DoutPrefixProvider *dpp, optional_yield y)
7c673cae 2796{
f67539c2 2797 return rgw::auth::Strategy::apply(dpp, auth_strategy, s, y);
7c673cae
FG
2798}
2799
f67539c2 2800int RGWHandler_REST_SWIFT::postauth_init(optional_yield y)
7c673cae
FG
2801{
2802 struct req_init_state* t = &s->init_state;
2803
2804 /* XXX Stub this until Swift Auth sets account into URL. */
2a845540
TL
2805 if (g_conf()->rgw_swift_account_in_url
2806 && s->user->get_id().id == RGW_USER_ANON_ID) {
2807 s->bucket_tenant = s->account_name;
2808 } else {
2809 s->bucket_tenant = s->user->get_tenant();
2810 }
7c673cae
FG
2811 s->bucket_name = t->url_bucket;
2812
f67539c2
TL
2813 if (!s->object) {
2814 /* Need an object, even an empty one */
1e59de90 2815 s->object = driver->get_object(rgw_obj_key());
f67539c2
TL
2816 }
2817
b3b6e05e 2818 ldpp_dout(s, 10) << "s->object=" <<
f67539c2 2819 (!s->object->empty() ? s->object->get_key() : rgw_obj_key("<NULL>"))
7c673cae
FG
2820 << " s->bucket="
2821 << rgw_make_bucket_entry_name(s->bucket_tenant, s->bucket_name)
2822 << dendl;
2823
2824 int ret;
d2e6a577 2825 ret = rgw_validate_tenant_name(s->bucket_tenant);
7c673cae
FG
2826 if (ret)
2827 return ret;
2828 ret = validate_bucket_name(s->bucket_name);
2829 if (ret)
2830 return ret;
f67539c2 2831 ret = validate_object_name(s->object->get_name());
7c673cae
FG
2832 if (ret)
2833 return ret;
2834
2835 if (!t->src_bucket.empty()) {
2836 /*
2837 * We don't allow cross-tenant copy at present. It requires account
2838 * names in the URL for Swift.
2839 */
9f95a23c 2840 s->src_tenant_name = s->user->get_tenant();
7c673cae
FG
2841 s->src_bucket_name = t->src_bucket;
2842
2843 ret = validate_bucket_name(s->src_bucket_name);
2844 if (ret < 0) {
2845 return ret;
2846 }
f67539c2 2847 ret = validate_object_name(s->src_object->get_name());
7c673cae
FG
2848 if (ret < 0) {
2849 return ret;
2850 }
2851 }
2852
2853 return 0;
2854}
2855
2856int RGWHandler_REST_SWIFT::validate_bucket_name(const string& bucket)
2857{
3efd9988 2858 const size_t len = bucket.size();
7c673cae 2859
3efd9988
FG
2860 if (len > MAX_BUCKET_NAME_LEN) {
2861 /* Bucket Name too long. Generate custom error message and bind it
2862 * to an R-value reference. */
2863 const auto msg = boost::str(
2864 boost::format("Container name length of %lld longer than %lld")
2865 % len % int(MAX_BUCKET_NAME_LEN));
2866 set_req_state_err(s, ERR_INVALID_BUCKET_NAME, msg);
2867 return -ERR_INVALID_BUCKET_NAME;
2868 }
2869
7c673cae
FG
2870
2871 if (len == 0)
2872 return 0;
2873
2874 if (bucket[0] == '.')
2875 return -ERR_INVALID_BUCKET_NAME;
2876
2877 if (check_utf8(bucket.c_str(), len))
2878 return -ERR_INVALID_UTF8;
2879
2880 const char *s = bucket.c_str();
2881
3efd9988 2882 for (size_t i = 0; i < len; ++i, ++s) {
7c673cae
FG
2883 if (*(unsigned char *)s == 0xff)
2884 return -ERR_INVALID_BUCKET_NAME;
11fdf7f2
TL
2885 if (*(unsigned char *)s == '/')
2886 return -ERR_INVALID_BUCKET_NAME;
7c673cae
FG
2887 }
2888
2889 return 0;
2890}
2891
2892static void next_tok(string& str, string& tok, char delim)
2893{
2894 if (str.size() == 0) {
2895 tok = "";
2896 return;
2897 }
2898 tok = str;
2899 int pos = str.find(delim);
2900 if (pos > 0) {
2901 tok = str.substr(0, pos);
2902 str = str.substr(pos + 1);
2903 } else {
2904 str = "";
2905 }
2906}
2907
1e59de90
TL
2908int RGWHandler_REST_SWIFT::init_from_header(rgw::sal::Driver* driver,
2909 req_state* const s,
7c673cae
FG
2910 const std::string& frontend_prefix)
2911{
2912 string req;
2913 string first;
2914
2915 s->prot_flags |= RGW_REST_SWIFT;
2916
2917 char reqbuf[frontend_prefix.length() + s->decoded_uri.length() + 1];
2918 sprintf(reqbuf, "%s%s", frontend_prefix.c_str(), s->decoded_uri.c_str());
2919 const char *req_name = reqbuf;
2920
2921 const char *p;
2922
2923 if (*req_name == '?') {
2924 p = req_name;
2925 } else {
2926 p = s->info.request_params.c_str();
2927 }
2928
2929 s->info.args.set(p);
b3b6e05e 2930 s->info.args.parse(s);
7c673cae
FG
2931
2932 /* Skip the leading slash of URL hierarchy. */
2933 if (req_name[0] != '/') {
2934 return 0;
2935 } else {
2936 req_name++;
2937 }
2938
2939 if ('\0' == req_name[0]) {
11fdf7f2 2940 return g_conf()->rgw_swift_url_prefix == "/" ? -ERR_BAD_URL : 0;
7c673cae
FG
2941 }
2942
2943 req = req_name;
2944
2945 size_t pos = req.find('/');
11fdf7f2
TL
2946 if (std::string::npos != pos && g_conf()->rgw_swift_url_prefix != "/") {
2947 bool cut_url = g_conf()->rgw_swift_url_prefix.length();
7c673cae
FG
2948 first = req.substr(0, pos);
2949
11fdf7f2 2950 if (first.compare(g_conf()->rgw_swift_url_prefix) == 0) {
7c673cae
FG
2951 if (cut_url) {
2952 /* Rewind to the "v1/..." part. */
2953 next_tok(req, first, '/');
2954 }
2955 }
11fdf7f2 2956 } else if (req.compare(g_conf()->rgw_swift_url_prefix) == 0) {
7c673cae
FG
2957 s->formatter = new RGWFormatter_Plain;
2958 return -ERR_BAD_URL;
2959 } else {
2960 first = req;
2961 }
2962
2963 std::string tenant_path;
11fdf7f2 2964 if (! g_conf()->rgw_swift_tenant_name.empty()) {
7c673cae 2965 tenant_path = "/AUTH_";
11fdf7f2 2966 tenant_path.append(g_conf()->rgw_swift_tenant_name);
7c673cae
FG
2967 }
2968
2969 /* verify that the request_uri conforms with what's expected */
11fdf7f2 2970 char buf[g_conf()->rgw_swift_url_prefix.length() + 16 + tenant_path.length()];
7c673cae 2971 int blen;
11fdf7f2 2972 if (g_conf()->rgw_swift_url_prefix == "/") {
7c673cae
FG
2973 blen = sprintf(buf, "/v1%s", tenant_path.c_str());
2974 } else {
2975 blen = sprintf(buf, "/%s/v1%s",
11fdf7f2 2976 g_conf()->rgw_swift_url_prefix.c_str(), tenant_path.c_str());
7c673cae
FG
2977 }
2978
2979 if (strncmp(reqbuf, buf, blen) != 0) {
2980 return -ENOENT;
2981 }
2982
1e59de90 2983 int ret = allocate_formatter(s, RGWFormat::PLAIN, true);
7c673cae
FG
2984 if (ret < 0)
2985 return ret;
2986
2987 string ver;
2988
2989 next_tok(req, ver, '/');
2990
11fdf7f2 2991 if (!tenant_path.empty() || g_conf()->rgw_swift_account_in_url) {
7c673cae
FG
2992 string account_name;
2993 next_tok(req, account_name, '/');
2994
2995 /* Erase all pre-defined prefixes like "AUTH_" or "KEY_". */
2996 const vector<string> skipped_prefixes = { "AUTH_", "KEY_" };
2997
f67539c2 2998 for (const auto& pfx : skipped_prefixes) {
7c673cae
FG
2999 const size_t comp_len = min(account_name.length(), pfx.length());
3000 if (account_name.compare(0, comp_len, pfx) == 0) {
3001 /* Prefix is present. Drop it. */
3002 account_name = account_name.substr(comp_len);
3003 break;
3004 }
3005 }
3006
3007 if (account_name.empty()) {
3008 return -ERR_PRECONDITION_FAILED;
3009 } else {
3010 s->account_name = account_name;
3011 }
3012 }
3013
3014 next_tok(req, first, '/');
3015
b3b6e05e 3016 ldpp_dout(s, 10) << "ver=" << ver << " first=" << first << " req=" << req << dendl;
7c673cae
FG
3017 if (first.size() == 0)
3018 return 0;
3019
3020 s->info.effective_uri = "/" + first;
3021
3022 // Save bucket to tide us over until token is parsed.
3023 s->init_state.url_bucket = first;
3024
3025 if (req.size()) {
1e59de90 3026 s->object = driver->get_object(
f67539c2
TL
3027 rgw_obj_key(req, s->info.env->get("HTTP_X_OBJECT_VERSION_ID", ""))); /* rgw swift extension */
3028 s->info.effective_uri.append("/" + s->object->get_name());
7c673cae
FG
3029 }
3030
3031 return 0;
3032}
3033
1e59de90 3034int RGWHandler_REST_SWIFT::init(rgw::sal::Driver* driver, req_state* s,
7c673cae
FG
3035 rgw::io::BasicClient *cio)
3036{
3037 struct req_init_state *t = &s->init_state;
3038
3039 s->dialect = "swift";
3040
494da23a 3041 std::string copy_source = s->info.env->get("HTTP_X_COPY_FROM", "");
3a9019d9 3042 if (! copy_source.empty()) {
f67539c2 3043 rgw_obj_key key;
b3b6e05e 3044 bool result = RGWCopyObj::parse_copy_location(copy_source, t->src_bucket, key, s);
7c673cae
FG
3045 if (!result)
3046 return -ERR_BAD_URL;
1e59de90 3047 s->src_object = driver->get_object(key);
f67539c2
TL
3048 if (!s->src_object)
3049 return -ERR_BAD_URL;
7c673cae
FG
3050 }
3051
3052 if (s->op == OP_COPY) {
494da23a 3053 std::string req_dest = s->info.env->get("HTTP_DESTINATION", "");
3a9019d9 3054 if (req_dest.empty())
7c673cae
FG
3055 return -ERR_BAD_URL;
3056
3a9019d9 3057 std::string dest_bucket_name;
7c673cae
FG
3058 rgw_obj_key dest_obj_key;
3059 bool result =
3060 RGWCopyObj::parse_copy_location(req_dest, dest_bucket_name,
b3b6e05e 3061 dest_obj_key, s);
7c673cae
FG
3062 if (!result)
3063 return -ERR_BAD_URL;
3064
f67539c2 3065 std::string dest_object_name = dest_obj_key.name;
7c673cae
FG
3066
3067 /* convert COPY operation into PUT */
3068 t->src_bucket = t->url_bucket;
f67539c2 3069 s->src_object = s->object->clone();
7c673cae 3070 t->url_bucket = dest_bucket_name;
f67539c2 3071 s->object->set_name(dest_object_name);
7c673cae
FG
3072 s->op = OP_PUT;
3073 }
3074
11fdf7f2
TL
3075 s->info.storage_class = s->info.env->get("HTTP_X_OBJECT_STORAGE_CLASS", "");
3076
1e59de90 3077 return RGWHandler_REST::init(driver, s, cio);
7c673cae
FG
3078}
3079
3080RGWHandler_REST*
1e59de90
TL
3081RGWRESTMgr_SWIFT::get_handler(rgw::sal::Driver* driver,
3082 req_state* const s,
7c673cae
FG
3083 const rgw::auth::StrategyRegistry& auth_registry,
3084 const std::string& frontend_prefix)
3085{
1e59de90 3086 int ret = RGWHandler_REST_SWIFT::init_from_header(driver, s, frontend_prefix);
7c673cae 3087 if (ret < 0) {
9f95a23c 3088 ldpp_dout(s, 10) << "init_from_header returned err=" << ret << dendl;
7c673cae
FG
3089 return nullptr;
3090 }
3091
3092 const auto& auth_strategy = auth_registry.get_swift();
3093
3094 if (s->init_state.url_bucket.empty()) {
3095 return new RGWHandler_REST_Service_SWIFT(auth_strategy);
3096 }
3097
20effc67 3098 if (rgw::sal::Object::empty(s->object.get())) {
7c673cae
FG
3099 return new RGWHandler_REST_Bucket_SWIFT(auth_strategy);
3100 }
3101
3102 return new RGWHandler_REST_Obj_SWIFT(auth_strategy);
3103}
3104
3105RGWHandler_REST* RGWRESTMgr_SWIFT_Info::get_handler(
1e59de90
TL
3106 rgw::sal::Driver* driver,
3107 req_state* const s,
7c673cae
FG
3108 const rgw::auth::StrategyRegistry& auth_registry,
3109 const std::string& frontend_prefix)
3110{
3111 s->prot_flags |= RGW_REST_SWIFT;
3112 const auto& auth_strategy = auth_registry.get_swift();
3113 return new RGWHandler_REST_SWIFT_Info(auth_strategy);
3114}