}
+constexpr std::initializer_list<int> RGWBulkUploadOp::terminal_errors;
+
int RGWBulkUploadOp::verify_permission()
{
if (s->auth.identity->is_anonymous()) {
op_ret = handle_file(header->get_filename(),
header->get_filesize(),
body);
+ if (! op_ret) {
+ /* Only regular files counts. */
+ num_created++;
+ } else {
+ failures.emplace_back(op_ret, header->get_filename().to_string());
+ }
break;
}
case rgw::tar::FileType::DIRECTORY: {
ldout(s->cct, 2) << "bulk upload: handling regular directory" << dendl;
op_ret = handle_dir(header->get_filename());
+ if (op_ret < 0 && op_ret != -ERR_BUCKET_EXISTS) {
+ failures.emplace_back(op_ret, header->get_filename().to_string());
+ }
break;
}
default: {
break;
}
}
+
+ /* In case of any problems with sub-request authorization Swift simply
+ * terminates whole upload immediately. */
+ if (boost::algorithm::contains(std::initializer_list<int>{ op_ret },
+ terminal_errors)) {
+ ldout(s->cct, 2) << "bulk upload: terminating due to ret=" << op_ret
+ << dendl;
+ break;
+ }
} else {
ldout(s->cct, 2) << "bulk upload: an empty block" << dendl;
op_ret = 0;
#include <string>
#include <set>
#include <map>
+#include <vector>
#include <boost/optional.hpp>
#include <boost/utility/in_place_factory.hpp>
boost::optional<RGWObjectCtx> dir_ctx;
protected:
+ class fail_desc_t {
+ public:
+ fail_desc_t(const int err, std::string path)
+ : err(err),
+ path(std::move(path)) {
+ }
+
+ const int err;
+ const std::string path;
+ };
+
+ static constexpr std::initializer_list<int> terminal_errors = {
+ -EACCES, -EPERM
+ };
+
+ /* FIXME: boost::container::small_vector<fail_desc_t, 4> failures; */
+ std::vector<fail_desc_t> failures;
+ size_t num_created;
+
class StreamGetter;
class DecoratedStreamGetter;
class AlignedStreamGetter;
int handle_dir(boost::string_ref path);
public:
+ RGWBulkUploadOp()
+ : num_created(0) {
+ }
+
void init(RGWRados* const store,
struct req_state* const s,
RGWHandler* const h) override {
const string& mm = s->info.args.get("multipart-manifest");
const bool multipart_delete = (mm.compare("delete") == 0);
-
+ const bool swift_bulkupload = s->prot_flags & RGW_REST_SWIFT &&
+ s->info.args.exists("extract-archive");
switch (s->format) {
case RGW_FORMAT_PLAIN:
{
- const bool use_kv_syntax = s->info.args.exists("bulk-delete") || multipart_delete;
+ const bool use_kv_syntax = s->info.args.exists("bulk-delete") ||
+ multipart_delete || swift_bulkupload;
s->formatter = new RGWFormatter_Plain(use_kv_syntax);
break;
}
case RGW_FORMAT_XML:
{
- const bool lowercase_underscore = s->info.args.exists("bulk-delete") || multipart_delete;
+ const bool lowercase_underscore = s->info.args.exists("bulk-delete") ||
+ multipart_delete || swift_bulkupload;
+
s->formatter = new XMLFormatter(false, lowercase_underscore);
break;
}
end_header(s, this /* RGWOp */, nullptr /* contype */,
CHUNKED_TRANSFER_ENCODING);
rgw_flush_formatter_and_reset(s, s->formatter);
+
+ s->formatter->open_object_section("delete");
+
+ std::string resp_status;
+ std::string resp_body;
+
+ if (! failures.empty()) {
+ rgw_err err;
+
+ const auto last_err = { failures.back().err };
+ if (boost::algorithm::contains(last_err, terminal_errors)) {
+ /* The terminal errors are affecting the status of the whole upload. */
+ set_req_state_err(err, failures.back().err, s->prot_flags);
+ } else {
+ set_req_state_err(err, ERR_INVALID_REQUEST, s->prot_flags);
+ }
+
+ dump_errno(err, resp_status);
+ } else if (0 == num_created && failures.empty()) {
+ /* Nothing created, nothing failed. This means the archive contained no
+ * entity we could understand (regular file or directory). We need to
+ * send 400 Bad Request to an HTTP client in the internal status field. */
+ dump_errno(400, resp_status);
+ resp_body = "Invalid Tar File: No Valid Files";
+ } else {
+ /* 200 OK */
+ dump_errno(201, resp_status);
+ }
+
+ encode_json("Number Files Created", num_created, s->formatter);
+ encode_json("Response Body", resp_body, s->formatter);
+ encode_json("Response Status", resp_status, s->formatter);
+
+ s->formatter->open_array_section("Errors");
+ for (const auto& fail_desc : failures) {
+ s->formatter->open_array_section("object");
+
+ encode_json("Name", fail_desc.path, s->formatter);
+
+ rgw_err err;
+ set_req_state_err(err, fail_desc.err, s->prot_flags);
+ std::string status;
+ dump_errno(err, status);
+ encode_json("Status", status, s->formatter);
+
+ s->formatter->close_section();
+ }
+ s->formatter->close_section();
+
+ s->formatter->close_section();
+ rgw_flush_formatter_and_reset(s, s->formatter);
}