diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-21 11:54:28 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-21 11:54:28 +0000 |
commit | e6918187568dbd01842d8d1d2c808ce16a894239 (patch) | |
tree | 64f88b554b444a49f656b6c656111a145cbbaa28 /src/rgw/driver/rados/rgw_sal_rados.cc | |
parent | Initial commit. (diff) | |
download | ceph-e6918187568dbd01842d8d1d2c808ce16a894239.tar.xz ceph-e6918187568dbd01842d8d1d2c808ce16a894239.zip |
Adding upstream version 18.2.2.upstream/18.2.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/rgw/driver/rados/rgw_sal_rados.cc')
-rw-r--r-- | src/rgw/driver/rados/rgw_sal_rados.cc | 3846 |
1 files changed, 3846 insertions, 0 deletions
diff --git a/src/rgw/driver/rados/rgw_sal_rados.cc b/src/rgw/driver/rados/rgw_sal_rados.cc new file mode 100644 index 000000000..9acdb79d3 --- /dev/null +++ b/src/rgw/driver/rados/rgw_sal_rados.cc @@ -0,0 +1,3846 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab ft=cpp + +/* + * Ceph - scalable distributed file system + * + * Copyright (C) 2020 Red Hat, Inc. + * + * This is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software + * Foundation. See file COPYING. + * + */ + +#include <errno.h> +#include <stdlib.h> +#include <system_error> +#include <filesystem> +#include <unistd.h> +#include <sstream> +#include <boost/algorithm/string.hpp> +#include <boost/process.hpp> + +#include "common/Clock.h" +#include "common/errno.h" + +#include "rgw_sal.h" +#include "rgw_sal_rados.h" +#include "rgw_bucket.h" +#include "rgw_multi.h" +#include "rgw_acl.h" +#include "rgw_acl_s3.h" +#include "rgw_aio.h" +#include "rgw_aio_throttle.h" +#include "rgw_tracer.h" + +#include "rgw_zone.h" +#include "rgw_rest_conn.h" +#include "rgw_service.h" +#include "rgw_lc.h" +#include "rgw_lc_tier.h" +#include "rgw_rest_admin.h" +#include "rgw_rest_bucket.h" +#include "rgw_rest_metadata.h" +#include "rgw_rest_log.h" +#include "rgw_rest_config.h" +#include "rgw_rest_ratelimit.h" +#include "rgw_rest_realm.h" +#include "rgw_rest_user.h" +#include "services/svc_sys_obj.h" +#include "services/svc_meta.h" +#include "services/svc_meta_be_sobj.h" +#include "services/svc_cls.h" +#include "services/svc_zone.h" +#include "services/svc_tier_rados.h" +#include "services/svc_quota.h" +#include "services/svc_config_key.h" +#include "services/svc_zone_utils.h" +#include "services/svc_role_rados.h" +#include "services/svc_user.h" +#include "cls/rgw/cls_rgw_client.h" + +#include "rgw_pubsub.h" + +#define dout_subsys ceph_subsys_rgw + +using namespace std; + +static string mp_ns = RGW_OBJ_NS_MULTIPART; + +namespace rgw::sal { + +// default number of entries to list with each bucket listing call +// (use marker to bridge between calls) +static constexpr size_t listing_max_entries = 1000; +static std::string pubsub_oid_prefix = "pubsub."; + +static int decode_policy(CephContext* cct, + bufferlist& bl, + RGWAccessControlPolicy* policy) +{ + auto iter = bl.cbegin(); + try { + policy->decode(iter); + } catch (buffer::error& err) { + ldout(cct, 0) << "ERROR: could not decode policy, caught buffer::error" << dendl; + return -EIO; + } + if (cct->_conf->subsys.should_gather<ceph_subsys_rgw, 15>()) { + ldout(cct, 15) << __func__ << " Read AccessControlPolicy"; + RGWAccessControlPolicy_S3* s3policy = static_cast<RGWAccessControlPolicy_S3 *>(policy); + s3policy->to_xml(*_dout); + *_dout << dendl; + } + return 0; +} + +static int rgw_op_get_bucket_policy_from_attr(const DoutPrefixProvider* dpp, + RadosStore* store, + User* user, + Attrs& bucket_attrs, + RGWAccessControlPolicy* policy, + optional_yield y) +{ + auto aiter = bucket_attrs.find(RGW_ATTR_ACL); + + if (aiter != bucket_attrs.end()) { + int ret = decode_policy(store->ctx(), aiter->second, policy); + if (ret < 0) + return ret; + } else { + ldout(store->ctx(), 0) << "WARNING: couldn't find acl header for bucket, generating default" << dendl; + /* object exists, but policy is broken */ + int r = user->load_user(dpp, y); + if (r < 0) + return r; + + policy->create_default(user->get_id(), user->get_display_name()); + } + return 0; +} + +static int drain_aio(std::list<librados::AioCompletion*>& handles) +{ + int ret = 0; + while (!handles.empty()) { + librados::AioCompletion* handle = handles.front(); + handles.pop_front(); + handle->wait_for_complete(); + int r = handle->get_return_value(); + handle->release(); + if (r < 0) { + ret = r; + } + } + return ret; +} + +int RadosCompletions::drain() +{ + return drain_aio(handles); +} + +int RadosUser::list_buckets(const DoutPrefixProvider* dpp, const std::string& marker, + const std::string& end_marker, uint64_t max, bool need_stats, + BucketList &buckets, optional_yield y) +{ + RGWUserBuckets ulist; + bool is_truncated = false; + int ret; + + buckets.clear(); + ret = store->ctl()->user->list_buckets(dpp, info.user_id, marker, end_marker, max, + need_stats, &ulist, &is_truncated, y); + if (ret < 0) + return ret; + + buckets.set_truncated(is_truncated); + for (const auto& ent : ulist.get_buckets()) { + buckets.add(std::unique_ptr<Bucket>(new RadosBucket(this->store, ent.second, this))); + } + + return 0; +} + +int RadosUser::create_bucket(const DoutPrefixProvider* dpp, + const rgw_bucket& b, + const std::string& zonegroup_id, + rgw_placement_rule& placement_rule, + std::string& swift_ver_location, + const RGWQuotaInfo * pquota_info, + const RGWAccessControlPolicy& policy, + Attrs& attrs, + RGWBucketInfo& info, + obj_version& ep_objv, + bool exclusive, + bool obj_lock_enabled, + bool* existed, + req_info& req_info, + std::unique_ptr<Bucket>* bucket_out, + optional_yield y) +{ + int ret; + bufferlist in_data; + RGWBucketInfo master_info; + rgw_bucket* pmaster_bucket; + uint32_t* pmaster_num_shards; + real_time creation_time; + std::unique_ptr<Bucket> bucket; + obj_version objv,* pobjv = NULL; + + /* If it exists, look it up; otherwise create it */ + ret = store->get_bucket(dpp, this, b, &bucket, y); + if (ret < 0 && ret != -ENOENT) + return ret; + + if (ret != -ENOENT) { + RGWAccessControlPolicy old_policy(store->ctx()); + *existed = true; + if (swift_ver_location.empty()) { + swift_ver_location = bucket->get_info().swift_ver_location; + } + placement_rule.inherit_from(bucket->get_info().placement_rule); + + // don't allow changes to the acl policy + int r = rgw_op_get_bucket_policy_from_attr(dpp, store, this, bucket->get_attrs(), + &old_policy, y); + if (r >= 0 && old_policy != policy) { + bucket_out->swap(bucket); + return -EEXIST; + } + } else { + bucket = std::unique_ptr<Bucket>(new RadosBucket(store, b, this)); + *existed = false; + bucket->set_attrs(attrs); + } + + if (!store->svc()->zone->is_meta_master()) { + JSONParser jp; + ret = store->forward_request_to_master(dpp, this, NULL, in_data, &jp, req_info, y); + if (ret < 0) { + return ret; + } + + JSONDecoder::decode_json("entry_point_object_ver", ep_objv, &jp); + JSONDecoder::decode_json("object_ver", objv, &jp); + JSONDecoder::decode_json("bucket_info", master_info, &jp); + ldpp_dout(dpp, 20) << "parsed: objv.tag=" << objv.tag << " objv.ver=" << objv.ver << dendl; + std::time_t ctime = ceph::real_clock::to_time_t(master_info.creation_time); + ldpp_dout(dpp, 20) << "got creation time: << " << std::put_time(std::localtime(&ctime), "%F %T") << dendl; + pmaster_bucket= &master_info.bucket; + creation_time = master_info.creation_time; + pmaster_num_shards = &master_info.layout.current_index.layout.normal.num_shards; + pobjv = &objv; + if (master_info.obj_lock_enabled()) { + info.flags = BUCKET_VERSIONED | BUCKET_OBJ_LOCK_ENABLED; + } + } else { + pmaster_bucket = NULL; + pmaster_num_shards = NULL; + if (obj_lock_enabled) + info.flags = BUCKET_VERSIONED | BUCKET_OBJ_LOCK_ENABLED; + } + + std::string zid = zonegroup_id; + if (zid.empty()) { + zid = store->svc()->zone->get_zonegroup().get_id(); + } + + if (*existed) { + rgw_placement_rule selected_placement_rule; + ret = store->svc()->zone->select_bucket_placement(dpp, this->get_info(), + zid, placement_rule, + &selected_placement_rule, nullptr, y); + if (selected_placement_rule != info.placement_rule) { + ret = -EEXIST; + bucket_out->swap(bucket); + return ret; + } + } else { + + ret = store->getRados()->create_bucket(this->get_info(), bucket->get_key(), + zid, placement_rule, swift_ver_location, pquota_info, + attrs, info, pobjv, &ep_objv, creation_time, + pmaster_bucket, pmaster_num_shards, y, dpp, + exclusive); + if (ret == -EEXIST) { + *existed = true; + /* bucket already existed, might have raced with another bucket creation, + * or might be partial bucket creation that never completed. Read existing + * bucket info, verify that the reported bucket owner is the current user. + * If all is ok then update the user's list of buckets. Otherwise inform + * client about a name conflict. + */ + if (info.owner.compare(this->get_id()) != 0) { + return -EEXIST; + } + ret = 0; + } else if (ret != 0) { + return ret; + } + } + + bucket->set_version(ep_objv); + bucket->get_info() = info; + + RadosBucket* rbucket = static_cast<RadosBucket*>(bucket.get()); + ret = rbucket->link(dpp, this, y, false); + if (ret && !*existed && ret != -EEXIST) { + /* if it exists (or previously existed), don't remove it! */ + ret = rbucket->unlink(dpp, this, y); + if (ret < 0) { + ldpp_dout(dpp, 0) << "WARNING: failed to unlink bucket: ret=" << ret + << dendl; + } + } else if (ret == -EEXIST || (ret == 0 && *existed)) { + ret = -ERR_BUCKET_EXISTS; + } + + bucket_out->swap(bucket); + + return ret; +} + +int RadosUser::read_attrs(const DoutPrefixProvider* dpp, optional_yield y) +{ + return store->ctl()->user->get_attrs_by_uid(dpp, get_id(), &attrs, y, &objv_tracker); +} + +int RadosUser::merge_and_store_attrs(const DoutPrefixProvider* dpp, Attrs& new_attrs, optional_yield y) +{ + for(auto& it : new_attrs) { + attrs[it.first] = it.second; + } + return store_user(dpp, y, false); +} + +int RadosUser::read_stats(const DoutPrefixProvider *dpp, + optional_yield y, RGWStorageStats* stats, + ceph::real_time* last_stats_sync, + ceph::real_time* last_stats_update) +{ + return store->ctl()->user->read_stats(dpp, get_id(), stats, y, last_stats_sync, last_stats_update); +} + +int RadosUser::read_stats_async(const DoutPrefixProvider *dpp, RGWGetUserStats_CB* cb) +{ + return store->svc()->user->read_stats_async(dpp, get_id(), cb); +} + +int RadosUser::complete_flush_stats(const DoutPrefixProvider *dpp, optional_yield y) +{ + return store->svc()->user->complete_flush_stats(dpp, get_id(), y); +} + +int RadosUser::read_usage(const DoutPrefixProvider *dpp, uint64_t start_epoch, uint64_t end_epoch, + uint32_t max_entries, bool* is_truncated, + RGWUsageIter& usage_iter, + map<rgw_user_bucket, rgw_usage_log_entry>& usage) +{ + std::string bucket_name; + return store->getRados()->read_usage(dpp, get_id(), bucket_name, start_epoch, + end_epoch, max_entries, is_truncated, + usage_iter, usage); +} + +int RadosUser::trim_usage(const DoutPrefixProvider *dpp, uint64_t start_epoch, uint64_t end_epoch) +{ + std::string bucket_name; + + return store->getRados()->trim_usage(dpp, get_id(), bucket_name, start_epoch, end_epoch); +} + +int RadosUser::load_user(const DoutPrefixProvider* dpp, optional_yield y) +{ + return store->ctl()->user->get_info_by_uid(dpp, info.user_id, &info, y, RGWUserCtl::GetParams().set_objv_tracker(&objv_tracker).set_attrs(&attrs)); +} + +int RadosUser::store_user(const DoutPrefixProvider* dpp, optional_yield y, bool exclusive, RGWUserInfo* old_info) +{ + return store->ctl()->user->store_info(dpp, info, y, + RGWUserCtl::PutParams().set_objv_tracker(&objv_tracker) + .set_exclusive(exclusive) + .set_attrs(&attrs) + .set_old_info(old_info)); +} + +int RadosUser::remove_user(const DoutPrefixProvider* dpp, optional_yield y) +{ + return store->ctl()->user->remove_info(dpp, info, y, + RGWUserCtl::RemoveParams().set_objv_tracker(&objv_tracker)); +} + +int RadosUser::verify_mfa(const std::string& mfa_str, bool* verified, + const DoutPrefixProvider* dpp, optional_yield y) +{ + vector<string> params; + get_str_vec(mfa_str, " ", params); + + if (params.size() != 2) { + ldpp_dout(dpp, 5) << "NOTICE: invalid mfa string provided: " << mfa_str << dendl; + return -EINVAL; + } + + string& serial = params[0]; + string& pin = params[1]; + + auto i = info.mfa_ids.find(serial); + if (i == info.mfa_ids.end()) { + ldpp_dout(dpp, 5) << "NOTICE: user does not have mfa device with serial=" << serial << dendl; + return -EACCES; + } + + int ret = store->svc()->cls->mfa.check_mfa(dpp, info.user_id, serial, pin, y); + if (ret < 0) { + ldpp_dout(dpp, 20) << "NOTICE: failed to check MFA, serial=" << serial << dendl; + return -EACCES; + } + + *verified = true; + + return 0; +} + +RadosBucket::~RadosBucket() {} + +int RadosBucket::remove_bucket(const DoutPrefixProvider* dpp, + bool delete_children, + bool forward_to_master, + req_info* req_info, + optional_yield y) +{ + int ret; + + // Refresh info + ret = load_bucket(dpp, y); + if (ret < 0) { + return ret; + } + + ListParams params; + params.list_versions = true; + params.allow_unordered = true; + + ListResults results; + + do { + results.objs.clear(); + + ret = list(dpp, params, 1000, results, y); + if (ret < 0) { + return ret; + } + + if (!results.objs.empty() && !delete_children) { + ldpp_dout(dpp, -1) << "ERROR: could not remove non-empty bucket " << info.bucket.name << + dendl; + return -ENOTEMPTY; + } + + for (const auto& obj : results.objs) { + rgw_obj_key key(obj.key); + /* xxx dang */ + ret = rgw_remove_object(dpp, store, this, key); + if (ret < 0 && ret != -ENOENT) { + return ret; + } + } + } while(results.is_truncated); + + ret = abort_multiparts(dpp, store->ctx()); + if (ret < 0) { + return ret; + } + + // remove lifecycle config, if any (XXX note could be made generic) + (void) store->getRados()->get_lc()->remove_bucket_config( + this, get_attrs()); + + ret = store->ctl()->bucket->sync_user_stats(dpp, info.owner, info, y, nullptr); + if (ret < 0) { + ldout(store->ctx(), 1) << "WARNING: failed sync user stats before bucket delete. ret=" << ret << dendl; + } + + RGWObjVersionTracker ot; + + // if we deleted children above we will force delete, as any that + // remain is detrius from a prior bug + ret = store->getRados()->delete_bucket(info, ot, y, dpp, !delete_children); + if (ret < 0) { + ldpp_dout(dpp, -1) << "ERROR: could not remove bucket " << + info.bucket.name << dendl; + return ret; + } + + // if bucket has notification definitions associated with it + // they should be removed (note that any pending notifications on the bucket are still going to be sent) + const RGWPubSub ps(store, info.owner.tenant); + const RGWPubSub::Bucket ps_bucket(ps, this); + const auto ps_ret = ps_bucket.remove_notifications(dpp, y); + if (ps_ret < 0 && ps_ret != -ENOENT) { + ldpp_dout(dpp, -1) << "ERROR: unable to remove notifications from bucket. ret=" << ps_ret << dendl; + } + + ret = store->ctl()->bucket->unlink_bucket(info.owner, info.bucket, y, dpp, false); + if (ret < 0) { + ldpp_dout(dpp, -1) << "ERROR: unable to remove user bucket information" << dendl; + } + + if (forward_to_master) { + bufferlist in_data; + ret = store->forward_request_to_master(dpp, owner, &ot.read_version, in_data, nullptr, *req_info, y); + if (ret < 0) { + if (ret == -ENOENT) { + /* adjust error, we want to return with NoSuchBucket and not + * NoSuchKey */ + ret = -ERR_NO_SUCH_BUCKET; + } + return ret; + } + } + + return ret; +} + +int RadosBucket::remove_bucket_bypass_gc(int concurrent_max, bool + keep_index_consistent, + optional_yield y, const + DoutPrefixProvider *dpp) +{ + int ret; + map<RGWObjCategory, RGWStorageStats> stats; + map<string, bool> common_prefixes; + RGWObjectCtx obj_ctx(store); + CephContext *cct = store->ctx(); + + string bucket_ver, master_ver; + + ret = load_bucket(dpp, y); + if (ret < 0) + return ret; + + const auto& index = info.get_current_index(); + ret = read_stats(dpp, index, RGW_NO_SHARD, &bucket_ver, &master_ver, stats, NULL); + if (ret < 0) + return ret; + + ret = abort_multiparts(dpp, cct); + if (ret < 0) { + return ret; + } + + rgw::sal::Bucket::ListParams params; + rgw::sal::Bucket::ListResults results; + + params.list_versions = true; + params.allow_unordered = true; + + std::list<librados::AioCompletion*> handles; + + int max_aio = concurrent_max; + results.is_truncated = true; + + while (results.is_truncated) { + ret = list(dpp, params, listing_max_entries, results, y); + if (ret < 0) + return ret; + + std::vector<rgw_bucket_dir_entry>::iterator it = results.objs.begin(); + for (; it != results.objs.end(); ++it) { + RGWObjState *astate = NULL; + RGWObjManifest *amanifest = nullptr; + rgw_obj obj{get_key(), it->key}; + + ret = store->getRados()->get_obj_state(dpp, &obj_ctx, get_info(), + obj, &astate, &amanifest, + false, y); + if (ret == -ENOENT) { + ldpp_dout(dpp, 1) << "WARNING: cannot find obj state for obj " << obj << dendl; + continue; + } + if (ret < 0) { + ldpp_dout(dpp, -1) << "ERROR: get obj state returned with error " << ret << dendl; + return ret; + } + + if (amanifest) { + RGWObjManifest& manifest = *amanifest; + RGWObjManifest::obj_iterator miter = manifest.obj_begin(dpp); + const rgw_obj head_obj = manifest.get_obj(); + rgw_raw_obj raw_head_obj; + store->get_raw_obj(manifest.get_head_placement_rule(), head_obj, &raw_head_obj); + + for (; miter != manifest.obj_end(dpp) && max_aio--; ++miter) { + if (!max_aio) { + ret = drain_aio(handles); + if (ret < 0) { + ldpp_dout(dpp, -1) << "ERROR: could not drain handles as aio completion returned with " << ret << dendl; + return ret; + } + max_aio = concurrent_max; + } + + rgw_raw_obj last_obj = miter.get_location().get_raw_obj(store->getRados()); + if (last_obj == raw_head_obj) { + // have the head obj deleted at the end + continue; + } + + ret = store->getRados()->delete_raw_obj_aio(dpp, last_obj, handles); + if (ret < 0) { + ldpp_dout(dpp, -1) << "ERROR: delete obj aio failed with " << ret << dendl; + return ret; + } + } // for all shadow objs + + ret = store->getRados()->delete_obj_aio(dpp, head_obj, get_info(), astate, + handles, keep_index_consistent, y); + if (ret < 0) { + ldpp_dout(dpp, -1) << "ERROR: delete obj aio failed with " << ret << dendl; + return ret; + } + } + + if (!max_aio) { + ret = drain_aio(handles); + if (ret < 0) { + ldpp_dout(dpp, -1) << "ERROR: could not drain handles as aio completion returned with " << ret << dendl; + return ret; + } + max_aio = concurrent_max; + } + obj_ctx.invalidate(obj); + } // for all RGW objects in results + } // while is_truncated + + ret = drain_aio(handles); + if (ret < 0) { + ldpp_dout(dpp, -1) << "ERROR: could not drain handles as aio completion returned with " << ret << dendl; + return ret; + } + + sync_user_stats(dpp, y); + if (ret < 0) { + ldpp_dout(dpp, 1) << "WARNING: failed sync user stats before bucket delete. ret=" << ret << dendl; + } + + RGWObjVersionTracker objv_tracker; + + // this function can only be run if caller wanted children to be + // deleted, so we can ignore the check for children as any that + // remain are detritus from a prior bug + ret = remove_bucket(dpp, true, false, nullptr, y); + if (ret < 0) { + ldpp_dout(dpp, -1) << "ERROR: could not remove bucket " << this << dendl; + return ret; + } + + return ret; +} + +int RadosBucket::load_bucket(const DoutPrefixProvider* dpp, optional_yield y, bool get_stats) +{ + int ret; + + RGWSI_MetaBackend_CtxParams bectx_params = RGWSI_MetaBackend_CtxParams_SObj(); + RGWObjVersionTracker ep_ot; + if (info.bucket.bucket_id.empty()) { + ret = store->ctl()->bucket->read_bucket_info(info.bucket, &info, y, dpp, + RGWBucketCtl::BucketInstance::GetParams() + .set_mtime(&mtime) + .set_attrs(&attrs) + .set_bectx_params(bectx_params), + &ep_ot); + } else { + ret = store->ctl()->bucket->read_bucket_instance_info(info.bucket, &info, y, dpp, + RGWBucketCtl::BucketInstance::GetParams() + .set_mtime(&mtime) + .set_attrs(&attrs) + .set_bectx_params(bectx_params)); + } + if (ret != 0) { + return ret; + } + + bucket_version = ep_ot.read_version; + + if (get_stats) { + ret = store->ctl()->bucket->read_bucket_stats(info.bucket, &ent, y, dpp); + } + + return ret; +} + +int RadosBucket::read_stats(const DoutPrefixProvider *dpp, + const bucket_index_layout_generation& idx_layout, + int shard_id, std::string* bucket_ver, std::string* master_ver, + std::map<RGWObjCategory, RGWStorageStats>& stats, + std::string* max_marker, bool* syncstopped) +{ + return store->getRados()->get_bucket_stats(dpp, info, idx_layout, shard_id, bucket_ver, master_ver, stats, max_marker, syncstopped); +} + +int RadosBucket::read_stats_async(const DoutPrefixProvider *dpp, + const bucket_index_layout_generation& idx_layout, + int shard_id, RGWGetBucketStats_CB* ctx) +{ + return store->getRados()->get_bucket_stats_async(dpp, get_info(), idx_layout, shard_id, ctx); +} + +int RadosBucket::sync_user_stats(const DoutPrefixProvider *dpp, optional_yield y) +{ + return store->ctl()->bucket->sync_user_stats(dpp, owner->get_id(), info, y, &ent); +} + +int RadosBucket::update_container_stats(const DoutPrefixProvider* dpp) +{ + int ret; + map<std::string, RGWBucketEnt> m; + + m[info.bucket.name] = ent; + ret = store->getRados()->update_containers_stats(m, dpp); + if (!ret) + return -EEXIST; + if (ret < 0) + return ret; + + map<std::string, RGWBucketEnt>::iterator iter = m.find(info.bucket.name); + if (iter == m.end()) + return -EINVAL; + + ent.count = iter->second.count; + ent.size = iter->second.size; + ent.size_rounded = iter->second.size_rounded; + ent.creation_time = iter->second.creation_time; + ent.placement_rule = std::move(iter->second.placement_rule); + + info.creation_time = ent.creation_time; + info.placement_rule = ent.placement_rule; + + return 0; +} + +int RadosBucket::check_bucket_shards(const DoutPrefixProvider* dpp) +{ + return store->getRados()->check_bucket_shards(info, info.bucket, get_count(), dpp); +} + +int RadosBucket::link(const DoutPrefixProvider* dpp, User* new_user, optional_yield y, bool update_entrypoint, RGWObjVersionTracker* objv) +{ + RGWBucketEntryPoint ep; + ep.bucket = info.bucket; + ep.owner = new_user->get_id(); + ep.creation_time = get_creation_time(); + ep.linked = true; + Attrs ep_attrs; + rgw_ep_info ep_data{ep, ep_attrs}; + + int r = store->ctl()->bucket->link_bucket(new_user->get_id(), info.bucket, + get_creation_time(), y, dpp, update_entrypoint, + &ep_data); + if (r < 0) + return r; + + if (objv) + *objv = ep_data.ep_objv; + + return r; +} + +int RadosBucket::unlink(const DoutPrefixProvider* dpp, User* new_user, optional_yield y, bool update_entrypoint) +{ + return store->ctl()->bucket->unlink_bucket(new_user->get_id(), info.bucket, y, dpp, update_entrypoint); +} + +int RadosBucket::chown(const DoutPrefixProvider* dpp, User& new_user, optional_yield y) +{ + std::string obj_marker; + int r; + + if (!owner) { + ldpp_dout(dpp, 0) << __func__ << " Cannot chown without an owner " << dendl; + return -EINVAL; + } + + r = this->unlink(dpp, owner, y); + if (r < 0) { + return r; + } + + return this->link(dpp, &new_user, y); +} + +int RadosBucket::put_info(const DoutPrefixProvider* dpp, bool exclusive, ceph::real_time _mtime) +{ + mtime = _mtime; + return store->getRados()->put_bucket_instance_info(info, exclusive, mtime, &attrs, dpp, null_yield); +} + +/* Make sure to call get_bucket_info() if you need it first */ +bool RadosBucket::is_owner(User* user) +{ + return (info.owner.compare(user->get_id()) == 0); +} + +int RadosBucket::check_empty(const DoutPrefixProvider* dpp, optional_yield y) +{ + return store->getRados()->check_bucket_empty(dpp, info, y); +} + +int RadosBucket::check_quota(const DoutPrefixProvider *dpp, RGWQuota& quota, uint64_t obj_size, + optional_yield y, bool check_size_only) +{ + return store->getRados()->check_quota(dpp, info.owner, get_key(), + quota, obj_size, y, check_size_only); +} + +int RadosBucket::merge_and_store_attrs(const DoutPrefixProvider* dpp, Attrs& new_attrs, optional_yield y) +{ + for(auto& it : new_attrs) { + attrs[it.first] = it.second; + } + return store->ctl()->bucket->set_bucket_instance_attrs(get_info(), + new_attrs, &get_info().objv_tracker, y, dpp); +} + +int RadosBucket::try_refresh_info(const DoutPrefixProvider* dpp, ceph::real_time* pmtime) +{ + return store->getRados()->try_refresh_bucket_info(info, pmtime, dpp, &attrs); +} + +int RadosBucket::read_usage(const DoutPrefixProvider *dpp, uint64_t start_epoch, uint64_t end_epoch, + uint32_t max_entries, bool* is_truncated, + RGWUsageIter& usage_iter, + map<rgw_user_bucket, rgw_usage_log_entry>& usage) +{ + return store->getRados()->read_usage(dpp, owner->get_id(), get_name(), start_epoch, + end_epoch, max_entries, is_truncated, + usage_iter, usage); +} + +int RadosBucket::trim_usage(const DoutPrefixProvider *dpp, uint64_t start_epoch, uint64_t end_epoch) +{ + return store->getRados()->trim_usage(dpp, owner->get_id(), get_name(), start_epoch, end_epoch); +} + +int RadosBucket::remove_objs_from_index(const DoutPrefixProvider *dpp, std::list<rgw_obj_index_key>& objs_to_unlink) +{ + return store->getRados()->remove_objs_from_index(dpp, info, objs_to_unlink); +} + +int RadosBucket::check_index(const DoutPrefixProvider *dpp, std::map<RGWObjCategory, RGWStorageStats>& existing_stats, std::map<RGWObjCategory, RGWStorageStats>& calculated_stats) +{ + return store->getRados()->bucket_check_index(dpp, info, &existing_stats, &calculated_stats); +} + +int RadosBucket::rebuild_index(const DoutPrefixProvider *dpp) +{ + return store->getRados()->bucket_rebuild_index(dpp, info); +} + +int RadosBucket::set_tag_timeout(const DoutPrefixProvider *dpp, uint64_t timeout) +{ + return store->getRados()->cls_obj_set_bucket_tag_timeout(dpp, info, timeout); +} + +int RadosBucket::purge_instance(const DoutPrefixProvider* dpp) +{ + int max_shards = (info.layout.current_index.layout.normal.num_shards > 0 ? info.layout.current_index.layout.normal.num_shards : 1); + for (int i = 0; i < max_shards; i++) { + RGWRados::BucketShard bs(store->getRados()); + int shard_id = (info.layout.current_index.layout.normal.num_shards > 0 ? i : -1); + int ret = bs.init(dpp, info, info.layout.current_index, shard_id); + if (ret < 0) { + cerr << "ERROR: bs.init(bucket=" << info.bucket << ", shard=" << shard_id + << "): " << cpp_strerror(-ret) << std::endl; + return ret; + } + ret = store->getRados()->bi_remove(dpp, bs); + if (ret < 0) { + cerr << "ERROR: failed to remove bucket index object: " + << cpp_strerror(-ret) << std::endl; + return ret; + } + } + return 0; +} + +int RadosBucket::set_acl(const DoutPrefixProvider* dpp, RGWAccessControlPolicy &acl, optional_yield y) +{ + bufferlist aclbl; + + acls = acl; + acl.encode(aclbl); + map<string, bufferlist>& attrs = get_attrs(); + + attrs[RGW_ATTR_ACL] = aclbl; + info.owner = acl.get_owner().get_id(); + + int r = store->ctl()->bucket->store_bucket_instance_info(info.bucket, + info, y, dpp, + RGWBucketCtl::BucketInstance::PutParams().set_attrs(&attrs)); + if (r < 0) { + cerr << "ERROR: failed to set bucket owner: " << cpp_strerror(-r) << std::endl; + return r; + } + + return 0; +} + +std::unique_ptr<Object> RadosBucket::get_object(const rgw_obj_key& k) +{ + return std::make_unique<RadosObject>(this->store, k, this); +} + +int RadosBucket::list(const DoutPrefixProvider* dpp, ListParams& params, int max, ListResults& results, optional_yield y) +{ + RGWRados::Bucket target(store->getRados(), get_info()); + if (params.shard_id >= 0) { + target.set_shard_id(params.shard_id); + } + RGWRados::Bucket::List list_op(&target); + + list_op.params.prefix = params.prefix; + list_op.params.delim = params.delim; + list_op.params.marker = params.marker; + list_op.params.ns = params.ns; + list_op.params.end_marker = params.end_marker; + list_op.params.ns = params.ns; + list_op.params.enforce_ns = params.enforce_ns; + list_op.params.access_list_filter = params.access_list_filter; + list_op.params.force_check_filter = params.force_check_filter; + list_op.params.list_versions = params.list_versions; + list_op.params.allow_unordered = params.allow_unordered; + + int ret = list_op.list_objects(dpp, max, &results.objs, &results.common_prefixes, &results.is_truncated, y); + if (ret >= 0) { + results.next_marker = list_op.get_next_marker(); + params.marker = results.next_marker; + } + + return ret; +} + +std::unique_ptr<MultipartUpload> RadosBucket::get_multipart_upload( + const std::string& oid, + std::optional<std::string> upload_id, + ACLOwner owner, ceph::real_time mtime) +{ + return std::make_unique<RadosMultipartUpload>(this->store, this, oid, upload_id, + std::move(owner), mtime); +} + +int RadosBucket::list_multiparts(const DoutPrefixProvider *dpp, + const string& prefix, + string& marker, + const string& delim, + const int& max_uploads, + vector<std::unique_ptr<MultipartUpload>>& uploads, + map<string, bool> *common_prefixes, + bool *is_truncated) +{ + rgw::sal::Bucket::ListParams params; + rgw::sal::Bucket::ListResults results; + MultipartMetaFilter mp_filter; + + params.prefix = prefix; + params.delim = delim; + params.marker = marker; + params.ns = RGW_OBJ_NS_MULTIPART; + params.access_list_filter = &mp_filter; + + int ret = list(dpp, params, max_uploads, results, null_yield); + + if (ret < 0) + return ret; + + if (!results.objs.empty()) { + for (const rgw_bucket_dir_entry& dentry : results.objs) { + rgw_obj_key key(dentry.key); + ACLOwner owner(rgw_user(dentry.meta.owner)); + owner.set_name(dentry.meta.owner_display_name); + uploads.push_back(this->get_multipart_upload(key.name, + std::nullopt, std::move(owner), dentry.meta.mtime)); + } + } + if (common_prefixes) { + *common_prefixes = std::move(results.common_prefixes); + } + *is_truncated = results.is_truncated; + marker = params.marker.name; + + return 0; +} + +int RadosBucket::abort_multiparts(const DoutPrefixProvider* dpp, + CephContext* cct) +{ + constexpr int max = 1000; + int ret, num_deleted = 0; + vector<std::unique_ptr<MultipartUpload>> uploads; + string marker; + bool is_truncated; + + const std::string empty_delim; + const std::string empty_prefix; + + do { + ret = list_multiparts(dpp, empty_prefix, marker, empty_delim, + max, uploads, nullptr, &is_truncated); + if (ret < 0) { + ldpp_dout(dpp, 0) << __func__ << + " ERROR : calling list_bucket_multiparts; ret=" << ret << + "; bucket=\"" << this << "\"" << dendl; + return ret; + } + ldpp_dout(dpp, 20) << __func__ << + " INFO: aborting and cleaning up multipart upload(s); bucket=\"" << + this << "\"; uploads.size()=" << uploads.size() << + "; is_truncated=" << is_truncated << dendl; + + if (!uploads.empty()) { + for (const auto& upload : uploads) { + ret = upload->abort(dpp, cct); + if (ret < 0) { + // we're doing a best-effort; if something cannot be found, + // log it and keep moving forward + if (ret != -ENOENT && ret != -ERR_NO_SUCH_UPLOAD) { + ldpp_dout(dpp, 0) << __func__ << + " ERROR : failed to abort and clean-up multipart upload \"" << + upload->get_meta() << "\"" << dendl; + return ret; + } else { + ldpp_dout(dpp, 10) << __func__ << + " NOTE : unable to find part(s) of " + "aborted multipart upload of \"" << upload->get_meta() << + "\" for cleaning up" << dendl; + } + } + num_deleted++; + } + if (num_deleted) { + ldpp_dout(dpp, 0) << __func__ << + " WARNING : aborted " << num_deleted << + " incomplete multipart uploads" << dendl; + } + } + } while (is_truncated); + + return 0; +} + +std::string RadosBucket::topics_oid() const { + return pubsub_oid_prefix + get_tenant() + ".bucket." + get_name() + "/" + get_marker(); +} + +int RadosBucket::read_topics(rgw_pubsub_bucket_topics& notifications, + RGWObjVersionTracker* objv_tracker, optional_yield y, const DoutPrefixProvider *dpp) +{ + bufferlist bl; + const int ret = rgw_get_system_obj(store->svc()->sysobj, + store->svc()->zone->get_zone_params().log_pool, + topics_oid(), + bl, + objv_tracker, + nullptr, y, dpp, nullptr); + if (ret < 0) { + return ret; + } + + auto iter = bl.cbegin(); + try { + decode(notifications, iter); + } catch (buffer::error& err) { + ldpp_dout(dpp, 20) << " failed to decode bucket notifications from oid: " << topics_oid() << ". for bucket: " + << get_name() << ". error: " << err.what() << dendl; + return -EIO; + } + + return 0; +} + +int RadosBucket::write_topics(const rgw_pubsub_bucket_topics& notifications, + RGWObjVersionTracker* objv_tracker, optional_yield y, const DoutPrefixProvider *dpp) { + bufferlist bl; + encode(notifications, bl); + + return rgw_put_system_obj(dpp, store->svc()->sysobj, + store->svc()->zone->get_zone_params().log_pool, + topics_oid(), + bl, false, objv_tracker, real_time(), y); +} + +int RadosBucket::remove_topics(RGWObjVersionTracker* objv_tracker, + optional_yield y, const DoutPrefixProvider *dpp) { + return rgw_delete_system_obj(dpp, store->svc()->sysobj, + store->svc()->zone->get_zone_params().log_pool, + topics_oid(), + objv_tracker, y); +} + +std::unique_ptr<User> RadosStore::get_user(const rgw_user &u) +{ + return std::make_unique<RadosUser>(this, u); +} + +std::string RadosStore::get_cluster_id(const DoutPrefixProvider* dpp, optional_yield y) +{ + return getRados()->get_cluster_fsid(dpp, y); +} + +int RadosStore::get_user_by_access_key(const DoutPrefixProvider* dpp, const std::string& key, optional_yield y, std::unique_ptr<User>* user) +{ + RGWUserInfo uinfo; + User* u; + RGWObjVersionTracker objv_tracker; + + int r = ctl()->user->get_info_by_access_key(dpp, key, &uinfo, y, RGWUserCtl::GetParams().set_objv_tracker(&objv_tracker)); + if (r < 0) + return r; + + u = new RadosUser(this, uinfo); + if (!u) + return -ENOMEM; + + u->get_version_tracker() = objv_tracker; + + user->reset(u); + return 0; +} + +int RadosStore::get_user_by_email(const DoutPrefixProvider* dpp, const std::string& email, optional_yield y, std::unique_ptr<User>* user) +{ + RGWUserInfo uinfo; + User* u; + RGWObjVersionTracker objv_tracker; + + int r = ctl()->user->get_info_by_email(dpp, email, &uinfo, y, RGWUserCtl::GetParams().set_objv_tracker(&objv_tracker)); + if (r < 0) + return r; + + u = new RadosUser(this, uinfo); + if (!u) + return -ENOMEM; + + u->get_version_tracker() = objv_tracker; + + user->reset(u); + return 0; +} + +int RadosStore::get_user_by_swift(const DoutPrefixProvider* dpp, const std::string& user_str, optional_yield y, std::unique_ptr<User>* user) +{ + RGWUserInfo uinfo; + User* u; + RGWObjVersionTracker objv_tracker; + + int r = ctl()->user->get_info_by_swift(dpp, user_str, &uinfo, y, RGWUserCtl::GetParams().set_objv_tracker(&objv_tracker)); + if (r < 0) + return r; + + u = new RadosUser(this, uinfo); + if (!u) + return -ENOMEM; + + u->get_version_tracker() = objv_tracker; + + user->reset(u); + return 0; +} + +std::unique_ptr<Object> RadosStore::get_object(const rgw_obj_key& k) +{ + return std::make_unique<RadosObject>(this, k); +} + +int RadosStore::get_bucket(const DoutPrefixProvider* dpp, User* u, const rgw_bucket& b, std::unique_ptr<Bucket>* bucket, optional_yield y) +{ + int ret; + Bucket* bp; + + bp = new RadosBucket(this, b, u); + ret = bp->load_bucket(dpp, y); + if (ret < 0) { + delete bp; + return ret; + } + + bucket->reset(bp); + return 0; +} + +int RadosStore::get_bucket(User* u, const RGWBucketInfo& i, std::unique_ptr<Bucket>* bucket) +{ + Bucket* bp; + + bp = new RadosBucket(this, i, u); + /* Don't need to fetch the bucket info, use the provided one */ + + bucket->reset(bp); + return 0; +} + +int RadosStore::get_bucket(const DoutPrefixProvider* dpp, User* u, const std::string& tenant, const std::string& name, std::unique_ptr<Bucket>* bucket, optional_yield y) +{ + rgw_bucket b; + + b.tenant = tenant; + b.name = name; + + return get_bucket(dpp, u, b, bucket, y); +} + +bool RadosStore::is_meta_master() +{ + return svc()->zone->is_meta_master(); +} + +int RadosStore::forward_request_to_master(const DoutPrefixProvider *dpp, User* user, obj_version* objv, + bufferlist& in_data, + JSONParser* jp, req_info& info, + optional_yield y) +{ + if (is_meta_master()) { + /* We're master, don't forward */ + return 0; + } + + if (!svc()->zone->get_master_conn()) { + ldpp_dout(dpp, 0) << "rest connection is invalid" << dendl; + return -EINVAL; + } + ldpp_dout(dpp, 0) << "sending request to master zonegroup" << dendl; + bufferlist response; + std::string uid_str = user->get_id().to_str(); +#define MAX_REST_RESPONSE (128 * 1024) // we expect a very small response + int ret = svc()->zone->get_master_conn()->forward(dpp, rgw_user(uid_str), info, + objv, MAX_REST_RESPONSE, + &in_data, &response, y); + if (ret < 0) + return ret; + + ldpp_dout(dpp, 20) << "response: " << response.c_str() << dendl; + if (jp && !jp->parse(response.c_str(), response.length())) { + ldpp_dout(dpp, 0) << "failed parsing response from master zonegroup" << dendl; + return -EINVAL; + } + + return 0; +} + +int RadosStore::forward_iam_request_to_master(const DoutPrefixProvider *dpp, const RGWAccessKey& key, obj_version* objv, + bufferlist& in_data, + RGWXMLDecoder::XMLParser* parser, req_info& info, + optional_yield y) +{ + if (is_meta_master()) { + /* We're master, don't forward */ + return 0; + } + + if (!svc()->zone->get_master_conn()) { + ldpp_dout(dpp, 0) << "rest connection is invalid" << dendl; + return -EINVAL; + } + ldpp_dout(dpp, 0) << "sending request to master zonegroup" << dendl; + bufferlist response; +#define MAX_REST_RESPONSE (128 * 1024) // we expect a very small response + int ret = svc()->zone->get_master_conn()->forward_iam_request(dpp, key, info, + objv, MAX_REST_RESPONSE, + &in_data, &response, y); + if (ret < 0) + return ret; + + ldpp_dout(dpp, 20) << "response: " << response.c_str() << dendl; + + std::string r = response.c_str(); + std::string str_to_search = """; + std::string str_to_replace = "\""; + boost::replace_all(r, str_to_search, str_to_replace); + ldpp_dout(dpp, 20) << "r: " << r.c_str() << dendl; + + if (parser && !parser->parse(r.c_str(), r.length(), 1)) { + ldpp_dout(dpp, 0) << "ERROR: failed to parse response from master zonegroup" << dendl; + return -EIO; + } + + return 0; +} + +std::string RadosStore::zone_unique_id(uint64_t unique_num) +{ + return svc()->zone_utils->unique_id(unique_num); +} + +std::string RadosStore::zone_unique_trans_id(const uint64_t unique_num) +{ + return svc()->zone_utils->unique_trans_id(unique_num); +} + +int RadosStore::get_zonegroup(const std::string& id, + std::unique_ptr<ZoneGroup>* zonegroup) +{ + ZoneGroup* zg; + RGWZoneGroup rzg; + int r = svc()->zone->get_zonegroup(id, rzg); + if (r < 0) + return r; + + zg = new RadosZoneGroup(this, rzg); + if (!zg) + return -ENOMEM; + + zonegroup->reset(zg); + return 0; +} + +int RadosStore::list_all_zones(const DoutPrefixProvider* dpp, std::list<std::string>& zone_ids) +{ + return svc()->zone->list_zones(dpp, zone_ids); +} + +int RadosStore::cluster_stat(RGWClusterStat& stats) +{ + rados_cluster_stat_t rados_stats; + int ret; + + ret = rados->get_rados_handle()->cluster_stat(rados_stats); + if (ret < 0) + return ret; + + stats.kb = rados_stats.kb; + stats.kb_used = rados_stats.kb_used; + stats.kb_avail = rados_stats.kb_avail; + stats.num_objects = rados_stats.num_objects; + + return ret; +} + +std::unique_ptr<Lifecycle> RadosStore::get_lifecycle(void) +{ + return std::make_unique<RadosLifecycle>(this); +} + +std::unique_ptr<Completions> RadosStore::get_completions(void) +{ + return std::make_unique<RadosCompletions>(); +} + +std::unique_ptr<Notification> RadosStore::get_notification( + rgw::sal::Object* obj, rgw::sal::Object* src_obj, req_state* s, rgw::notify::EventType event_type, optional_yield y, const std::string* object_name) +{ + return std::make_unique<RadosNotification>(s, this, obj, src_obj, s, event_type, y, object_name); +} + +std::unique_ptr<Notification> RadosStore::get_notification(const DoutPrefixProvider* dpp, rgw::sal::Object* obj, rgw::sal::Object* src_obj, rgw::notify::EventType event_type, rgw::sal::Bucket* _bucket, std::string& _user_id, std::string& _user_tenant, std::string& _req_id, optional_yield y) +{ + return std::make_unique<RadosNotification>(dpp, this, obj, src_obj, event_type, _bucket, _user_id, _user_tenant, _req_id, y); +} + +std::string RadosStore::topics_oid(const std::string& tenant) const { + return pubsub_oid_prefix + tenant; +} + +int RadosStore::read_topics(const std::string& tenant, rgw_pubsub_topics& topics, RGWObjVersionTracker* objv_tracker, + optional_yield y, const DoutPrefixProvider *dpp) { + bufferlist bl; + const int ret = rgw_get_system_obj(svc()->sysobj, + svc()->zone->get_zone_params().log_pool, + topics_oid(tenant), + bl, + objv_tracker, + nullptr, y, dpp, nullptr); + if (ret < 0) { + return ret; + } + + auto iter = bl.cbegin(); + try { + decode(topics, iter); + } catch (buffer::error& err) { + ldpp_dout(dpp, 20) << " failed to decode topics from oid: " << topics_oid(tenant) << + ". error: " << err.what() << dendl; + return -EIO; + } + + return 0; +} + +int RadosStore::write_topics(const std::string& tenant, const rgw_pubsub_topics& topics, RGWObjVersionTracker* objv_tracker, + optional_yield y, const DoutPrefixProvider *dpp) { + bufferlist bl; + encode(topics, bl); + + return rgw_put_system_obj(dpp, svc()->sysobj, + svc()->zone->get_zone_params().log_pool, + topics_oid(tenant), + bl, false, objv_tracker, real_time(), y); +} + +int RadosStore::remove_topics(const std::string& tenant, RGWObjVersionTracker* objv_tracker, + optional_yield y, const DoutPrefixProvider *dpp) { + return rgw_delete_system_obj(dpp, svc()->sysobj, + svc()->zone->get_zone_params().log_pool, + topics_oid(tenant), + objv_tracker, y); +} + +int RadosStore::delete_raw_obj(const DoutPrefixProvider *dpp, const rgw_raw_obj& obj) +{ + return rados->delete_raw_obj(dpp, obj); +} + +int RadosStore::delete_raw_obj_aio(const DoutPrefixProvider *dpp, const rgw_raw_obj& obj, Completions* aio) +{ + RadosCompletions* raio = static_cast<RadosCompletions*>(aio); + + return rados->delete_raw_obj_aio(dpp, obj, raio->handles); +} + +void RadosStore::get_raw_obj(const rgw_placement_rule& placement_rule, const rgw_obj& obj, rgw_raw_obj* raw_obj) +{ + rados->obj_to_raw(placement_rule, obj, raw_obj); +} + +int RadosStore::get_raw_chunk_size(const DoutPrefixProvider* dpp, const rgw_raw_obj& obj, uint64_t* chunk_size) +{ + return rados->get_max_chunk_size(obj.pool, chunk_size, dpp); +} + +int RadosStore::initialize(CephContext *cct, const DoutPrefixProvider *dpp) +{ + std::unique_ptr<ZoneGroup> zg = + std::make_unique<RadosZoneGroup>(this, svc()->zone->get_zonegroup()); + zone = make_unique<RadosZone>(this, std::move(zg)); + return 0; +} + +int RadosStore::log_usage(const DoutPrefixProvider *dpp, map<rgw_user_bucket, RGWUsageBatch>& usage_info) +{ + return rados->log_usage(dpp, usage_info); +} + +int RadosStore::log_op(const DoutPrefixProvider *dpp, std::string& oid, bufferlist& bl) +{ + rgw_raw_obj obj(svc()->zone->get_zone_params().log_pool, oid); + + int ret = rados->append_async(dpp, obj, bl.length(), bl); + if (ret == -ENOENT) { + ret = rados->create_pool(dpp, svc()->zone->get_zone_params().log_pool); + if (ret < 0) + return ret; + // retry + ret = rados->append_async(dpp, obj, bl.length(), bl); + } + + return ret; +} + +int RadosStore::register_to_service_map(const DoutPrefixProvider *dpp, const std::string& daemon_type, + const map<std::string, std::string>& meta) +{ + return rados->register_to_service_map(dpp, daemon_type, meta); +} + +void RadosStore::get_quota(RGWQuota& quota) +{ + quota.bucket_quota = svc()->quota->get_bucket_quota(); + quota.user_quota = svc()->quota->get_user_quota(); +} + +void RadosStore::get_ratelimit(RGWRateLimitInfo& bucket_ratelimit, RGWRateLimitInfo& user_ratelimit, RGWRateLimitInfo& anon_ratelimit) +{ + bucket_ratelimit = svc()->zone->get_current_period().get_config().bucket_ratelimit; + user_ratelimit = svc()->zone->get_current_period().get_config().user_ratelimit; + anon_ratelimit = svc()->zone->get_current_period().get_config().anon_ratelimit; +} + +int RadosStore::set_buckets_enabled(const DoutPrefixProvider* dpp, vector<rgw_bucket>& buckets, bool enabled) +{ + return rados->set_buckets_enabled(buckets, enabled, dpp); +} + +int RadosStore::get_sync_policy_handler(const DoutPrefixProvider* dpp, + std::optional<rgw_zone_id> zone, + std::optional<rgw_bucket> bucket, + RGWBucketSyncPolicyHandlerRef* phandler, + optional_yield y) +{ + return ctl()->bucket->get_sync_policy_handler(zone, bucket, phandler, y, dpp); +} + +RGWDataSyncStatusManager* RadosStore::get_data_sync_manager(const rgw_zone_id& source_zone) +{ + return rados->get_data_sync_manager(source_zone); +} + +int RadosStore::read_all_usage(const DoutPrefixProvider *dpp, uint64_t start_epoch, uint64_t end_epoch, + uint32_t max_entries, bool* is_truncated, + RGWUsageIter& usage_iter, + map<rgw_user_bucket, rgw_usage_log_entry>& usage) +{ + rgw_user uid; + std::string bucket_name; + + return rados->read_usage(dpp, uid, bucket_name, start_epoch, end_epoch, max_entries, + is_truncated, usage_iter, usage); +} + +int RadosStore::trim_all_usage(const DoutPrefixProvider *dpp, uint64_t start_epoch, uint64_t end_epoch) +{ + rgw_user uid; + std::string bucket_name; + + return rados->trim_usage(dpp, uid, bucket_name, start_epoch, end_epoch); +} + +int RadosStore::get_config_key_val(std::string name, bufferlist* bl) +{ + return svc()->config_key->get(name, true, bl); +} + +int RadosStore::meta_list_keys_init(const DoutPrefixProvider *dpp, const std::string& section, const std::string& marker, void** phandle) +{ + return ctl()->meta.mgr->list_keys_init(dpp, section, marker, phandle); +} + +int RadosStore::meta_list_keys_next(const DoutPrefixProvider *dpp, void* handle, int max, list<std::string>& keys, bool* truncated) +{ + return ctl()->meta.mgr->list_keys_next(dpp, handle, max, keys, truncated); +} + +void RadosStore::meta_list_keys_complete(void* handle) +{ + ctl()->meta.mgr->list_keys_complete(handle); +} + +std::string RadosStore::meta_get_marker(void* handle) +{ + return ctl()->meta.mgr->get_marker(handle); +} + +int RadosStore::meta_remove(const DoutPrefixProvider* dpp, std::string& metadata_key, optional_yield y) +{ + return ctl()->meta.mgr->remove(metadata_key, y, dpp); +} + +void RadosStore::finalize(void) +{ + if (rados) + rados->finalize(); +} + +void RadosStore::register_admin_apis(RGWRESTMgr* mgr) +{ + mgr->register_resource("user", new RGWRESTMgr_User); + mgr->register_resource("bucket", new RGWRESTMgr_Bucket); + /*Registering resource for /admin/metadata */ + mgr->register_resource("metadata", new RGWRESTMgr_Metadata); + mgr->register_resource("log", new RGWRESTMgr_Log); + /* XXX These may become global when cbodley is done with his zone work */ + mgr->register_resource("config", new RGWRESTMgr_Config); + mgr->register_resource("realm", new RGWRESTMgr_Realm); + mgr->register_resource("ratelimit", new RGWRESTMgr_Ratelimit); +} + +std::unique_ptr<LuaManager> RadosStore::get_lua_manager() +{ + return std::make_unique<RadosLuaManager>(this); +} + +std::unique_ptr<RGWRole> RadosStore::get_role(std::string name, + std::string tenant, + std::string path, + std::string trust_policy, + std::string max_session_duration_str, + std::multimap<std::string,std::string> tags) +{ + return std::make_unique<RadosRole>(this, name, tenant, path, trust_policy, max_session_duration_str, tags); +} + +std::unique_ptr<RGWRole> RadosStore::get_role(std::string id) +{ + return std::make_unique<RadosRole>(this, id); +} + +std::unique_ptr<RGWRole> RadosStore::get_role(const RGWRoleInfo& info) +{ + return std::make_unique<RadosRole>(this, info); +} + +int RadosStore::get_roles(const DoutPrefixProvider *dpp, + optional_yield y, + const std::string& path_prefix, + const std::string& tenant, + vector<std::unique_ptr<RGWRole>>& roles) +{ + auto pool = svc()->zone->get_zone_params().roles_pool; + std::string prefix; + + // List all roles if path prefix is empty + if (! path_prefix.empty()) { + prefix = tenant + RGWRole::role_path_oid_prefix + path_prefix; + } else { + prefix = tenant + RGWRole::role_path_oid_prefix; + } + + //Get the filtered objects + list<std::string> result; + bool is_truncated; + RGWListRawObjsCtx ctx; + do { + list<std::string> oids; + int r = rados->list_raw_objects(dpp, pool, prefix, 1000, ctx, oids, &is_truncated); + if (r < 0) { + ldpp_dout(dpp, 0) << "ERROR: listing filtered objects failed: " + << prefix << ": " << cpp_strerror(-r) << dendl; + return r; + } + for (const auto& iter : oids) { + result.push_back(iter.substr(RGWRole::role_path_oid_prefix.size())); + } + } while (is_truncated); + + for (const auto& it : result) { + //Find the role oid prefix from the end + size_t pos = it.rfind(RGWRole::role_oid_prefix); + if (pos == std::string::npos) { + continue; + } + // Split the result into path and info_oid + id + std::string path = it.substr(0, pos); + + /*Make sure that prefix is part of path (False results could've been returned) + because of the role info oid + id appended to the path)*/ + if(path_prefix.empty() || path.find(path_prefix) != std::string::npos) { + //Get id from info oid prefix + id + std::string id = it.substr(pos + RGWRole::role_oid_prefix.length()); + + std::unique_ptr<rgw::sal::RGWRole> role = get_role(id); + int ret = role->read_info(dpp, y); + if (ret < 0) { + return ret; + } + roles.push_back(std::move(role)); + } + } + + return 0; +} + +std::unique_ptr<RGWOIDCProvider> RadosStore::get_oidc_provider() +{ + return std::make_unique<RadosOIDCProvider>(this); +} + +int RadosStore::get_oidc_providers(const DoutPrefixProvider *dpp, + const std::string& tenant, + vector<std::unique_ptr<RGWOIDCProvider>>& providers) +{ + std::string prefix = tenant + RGWOIDCProvider::oidc_url_oid_prefix; + auto pool = svc()->zone->get_zone_params().oidc_pool; + + //Get the filtered objects + list<std::string> result; + bool is_truncated; + RGWListRawObjsCtx ctx; + do { + list<std::string> oids; + int r = rados->list_raw_objects(dpp, pool, prefix, 1000, ctx, oids, &is_truncated); + if (r < 0) { + ldpp_dout(dpp, 0) << "ERROR: listing filtered objects failed: OIDC pool: " + << pool.name << ": " << prefix << ": " << cpp_strerror(-r) << dendl; + return r; + } + for (const auto& iter : oids) { + std::unique_ptr<rgw::sal::RGWOIDCProvider> provider = get_oidc_provider(); + bufferlist bl; + + r = rgw_get_system_obj(svc()->sysobj, pool, iter, bl, nullptr, nullptr, null_yield, dpp); + if (r < 0) { + return r; + } + + try { + using ceph::decode; + auto iter = bl.cbegin(); + decode(*provider, iter); + } catch (buffer::error& err) { + ldpp_dout(dpp, 0) << "ERROR: failed to decode oidc provider info from pool: " + << pool.name << ": " << iter << dendl; + return -EIO; + } + + providers.push_back(std::move(provider)); + } + } while (is_truncated); + + return 0; +} + +std::unique_ptr<Writer> RadosStore::get_append_writer(const DoutPrefixProvider *dpp, + optional_yield y, + rgw::sal::Object* obj, + const rgw_user& owner, + const rgw_placement_rule *ptail_placement_rule, + const std::string& unique_tag, + uint64_t position, + uint64_t *cur_accounted_size) +{ + RGWBucketInfo& bucket_info = obj->get_bucket()->get_info(); + RGWObjectCtx& obj_ctx = static_cast<RadosObject*>(obj)->get_ctx(); + auto aio = rgw::make_throttle(ctx()->_conf->rgw_put_obj_min_window_size, y); + return std::make_unique<RadosAppendWriter>(dpp, y, + bucket_info, obj_ctx, obj->get_obj(), + this, std::move(aio), owner, + ptail_placement_rule, + unique_tag, position, + cur_accounted_size); +} + +std::unique_ptr<Writer> RadosStore::get_atomic_writer(const DoutPrefixProvider *dpp, + optional_yield y, + rgw::sal::Object* obj, + const rgw_user& owner, + const rgw_placement_rule *ptail_placement_rule, + uint64_t olh_epoch, + const std::string& unique_tag) +{ + RGWBucketInfo& bucket_info = obj->get_bucket()->get_info(); + RGWObjectCtx& obj_ctx = static_cast<RadosObject*>(obj)->get_ctx(); + auto aio = rgw::make_throttle(ctx()->_conf->rgw_put_obj_min_window_size, y); + return std::make_unique<RadosAtomicWriter>(dpp, y, + bucket_info, obj_ctx, obj->get_obj(), + this, std::move(aio), owner, + ptail_placement_rule, + olh_epoch, unique_tag); +} + +const std::string& RadosStore::get_compression_type(const rgw_placement_rule& rule) +{ + return svc()->zone->get_zone_params().get_compression_type(rule); +} + +bool RadosStore::valid_placement(const rgw_placement_rule& rule) +{ + return svc()->zone->get_zone_params().valid_placement(rule); +} + +int RadosStore::get_obj_head_ioctx(const DoutPrefixProvider *dpp, const RGWBucketInfo& bucket_info, const rgw_obj& obj, librados::IoCtx* ioctx) +{ + return rados->get_obj_head_ioctx(dpp, bucket_info, obj, ioctx); +} + +RadosObject::~RadosObject() +{ + if (rados_ctx_owned) + delete rados_ctx; +} + +int RadosObject::get_obj_state(const DoutPrefixProvider* dpp, RGWObjState **pstate, optional_yield y, bool follow_olh) +{ + int ret = store->getRados()->get_obj_state(dpp, rados_ctx, bucket->get_info(), get_obj(), pstate, &manifest, follow_olh, y); + if (ret < 0) { + return ret; + } + + /* Don't overwrite obj, atomic, or prefetch */ + rgw_obj obj = get_obj(); + bool is_atomic = state.is_atomic; + bool prefetch_data = state.prefetch_data; + + state = **pstate; + + state.obj = obj; + state.is_atomic = is_atomic; + state.prefetch_data = prefetch_data; + return ret; +} + +int RadosObject::read_attrs(const DoutPrefixProvider* dpp, RGWRados::Object::Read &read_op, optional_yield y, rgw_obj* target_obj) +{ + read_op.params.attrs = &state.attrset; + read_op.params.target_obj = target_obj; + read_op.params.obj_size = &state.size; + read_op.params.lastmod = &state.mtime; + + return read_op.prepare(y, dpp); +} + +int RadosObject::set_obj_attrs(const DoutPrefixProvider* dpp, Attrs* setattrs, Attrs* delattrs, optional_yield y) +{ + Attrs empty; + return store->getRados()->set_attrs(dpp, rados_ctx, + bucket->get_info(), + get_obj(), + setattrs ? *setattrs : empty, + delattrs ? delattrs : nullptr, + y); +} + +int RadosObject::get_obj_attrs(optional_yield y, const DoutPrefixProvider* dpp, rgw_obj* target_obj) +{ + RGWRados::Object op_target(store->getRados(), bucket->get_info(), *rados_ctx, get_obj()); + RGWRados::Object::Read read_op(&op_target); + + return read_attrs(dpp, read_op, y, target_obj); +} + +int RadosObject::modify_obj_attrs(const char* attr_name, bufferlist& attr_val, optional_yield y, const DoutPrefixProvider* dpp) +{ + rgw_obj target = get_obj(); + rgw_obj save = get_obj(); + int r = get_obj_attrs(y, dpp, &target); + if (r < 0) { + return r; + } + + /* Temporarily set target */ + state.obj = target; + set_atomic(); + state.attrset[attr_name] = attr_val; + r = set_obj_attrs(dpp, &state.attrset, nullptr, y); + /* Restore target */ + state.obj = save; + + return r; +} + +int RadosObject::delete_obj_attrs(const DoutPrefixProvider* dpp, const char* attr_name, optional_yield y) +{ + Attrs rmattr; + bufferlist bl; + + set_atomic(); + rmattr[attr_name] = bl; + return set_obj_attrs(dpp, nullptr, &rmattr, y); +} + +bool RadosObject::is_expired() { + auto iter = state.attrset.find(RGW_ATTR_DELETE_AT); + if (iter == state.attrset.end()) { + return false; + } + utime_t delete_at; + try { + auto bufit = iter->second.cbegin(); + decode(delete_at, bufit); + } catch (buffer::error& err) { + ldout(store->ctx(), 0) << "ERROR: " << __func__ << ": failed to decode " RGW_ATTR_DELETE_AT " attr" << dendl; + return false; + } + + return delete_at <= ceph_clock_now() && !delete_at.is_zero(); +} + +void RadosObject::gen_rand_obj_instance_name() +{ + store->getRados()->gen_rand_obj_instance_name(&state.obj.key); +} + +void RadosObject::raw_obj_to_obj(const rgw_raw_obj& raw_obj) +{ + rgw_obj tobj = get_obj(); + RGWSI_Tier_RADOS::raw_obj_to_obj(get_bucket()->get_key(), raw_obj, &tobj); + set_key(tobj.key); +} + +void RadosObject::get_raw_obj(rgw_raw_obj* raw_obj) +{ + store->getRados()->obj_to_raw((bucket->get_info()).placement_rule, get_obj(), raw_obj); +} + +int RadosObject::omap_get_vals(const DoutPrefixProvider *dpp, const std::string& marker, uint64_t count, + std::map<std::string, bufferlist> *m, + bool* pmore, optional_yield y) +{ + rgw_raw_obj raw_obj; + get_raw_obj(&raw_obj); + auto sysobj = store->svc()->sysobj->get_obj(raw_obj); + + return sysobj.omap().get_vals(dpp, marker, count, m, pmore, y); +} + +int RadosObject::omap_get_all(const DoutPrefixProvider *dpp, std::map<std::string, bufferlist> *m, + optional_yield y) +{ + rgw_raw_obj raw_obj; + get_raw_obj(&raw_obj); + auto sysobj = store->svc()->sysobj->get_obj(raw_obj); + + return sysobj.omap().get_all(dpp, m, y); +} + +int RadosObject::omap_get_vals_by_keys(const DoutPrefixProvider *dpp, const std::string& oid, + const std::set<std::string>& keys, + Attrs* vals) +{ + int ret; + rgw_raw_obj head_obj; + librados::IoCtx cur_ioctx; + rgw_obj obj = get_obj(); + + store->getRados()->obj_to_raw(bucket->get_placement_rule(), obj, &head_obj); + ret = store->get_obj_head_ioctx(dpp, bucket->get_info(), obj, &cur_ioctx); + if (ret < 0) { + return ret; + } + + return cur_ioctx.omap_get_vals_by_keys(oid, keys, vals); +} + +int RadosObject::omap_set_val_by_key(const DoutPrefixProvider *dpp, const std::string& key, bufferlist& val, + bool must_exist, optional_yield y) +{ + rgw_raw_obj raw_meta_obj; + rgw_obj obj = get_obj(); + + store->getRados()->obj_to_raw(bucket->get_placement_rule(), obj, &raw_meta_obj); + + auto sysobj = store->svc()->sysobj->get_obj(raw_meta_obj); + + return sysobj.omap().set_must_exist(must_exist).set(dpp, key, val, y); +} + +int RadosObject::chown(User& new_user, const DoutPrefixProvider* dpp, optional_yield y) +{ + int r = get_obj_attrs(y, dpp); + if (r < 0) { + ldpp_dout(dpp, 0) << "ERROR: failed to read object attrs " << get_name() << cpp_strerror(-r) << dendl; + return r; + } + + const auto& aiter = get_attrs().find(RGW_ATTR_ACL); + if (aiter == get_attrs().end()) { + ldpp_dout(dpp, 0) << "ERROR: no acls found for object " << get_name() << dendl; + return -EINVAL; + } + + bufferlist& bl = aiter->second; + RGWAccessControlPolicy policy(store->ctx()); + ACLOwner owner; + auto bliter = bl.cbegin(); + try { + policy.decode(bliter); + owner = policy.get_owner(); + } catch (buffer::error& err) { + ldpp_dout(dpp, 0) << "ERROR: decode policy failed" << err.what() + << dendl; + return -EIO; + } + + //Get the ACL from the policy + RGWAccessControlList& acl = policy.get_acl(); + + //Remove grant that is set to old owner + acl.remove_canon_user_grant(owner.get_id()); + + //Create a grant and add grant + ACLGrant grant; + grant.set_canon(new_user.get_id(), new_user.get_display_name(), RGW_PERM_FULL_CONTROL); + acl.add_grant(&grant); + + //Update the ACL owner to the new user + owner.set_id(new_user.get_id()); + owner.set_name(new_user.get_display_name()); + policy.set_owner(owner); + + bl.clear(); + encode(policy, bl); + + set_atomic(); + map<string, bufferlist> attrs; + attrs[RGW_ATTR_ACL] = bl; + r = set_obj_attrs(dpp, &attrs, nullptr, y); + if (r < 0) { + ldpp_dout(dpp, 0) << "ERROR: modify attr failed " << cpp_strerror(-r) << dendl; + return r; + } + + return 0; +} + +std::unique_ptr<MPSerializer> RadosObject::get_serializer(const DoutPrefixProvider *dpp, const std::string& lock_name) +{ + return std::make_unique<MPRadosSerializer>(dpp, store, this, lock_name); +} + +int RadosObject::transition(Bucket* bucket, + const rgw_placement_rule& placement_rule, + const real_time& mtime, + uint64_t olh_epoch, + const DoutPrefixProvider* dpp, + optional_yield y) +{ + return store->getRados()->transition_obj(*rados_ctx, bucket->get_info(), get_obj(), placement_rule, mtime, olh_epoch, dpp, y); +} + +int RadosObject::transition_to_cloud(Bucket* bucket, + rgw::sal::PlacementTier* tier, + rgw_bucket_dir_entry& o, + std::set<std::string>& cloud_targets, + CephContext* cct, + bool update_object, + const DoutPrefixProvider* dpp, + optional_yield y) +{ + /* init */ + rgw::sal::RadosPlacementTier* rtier = static_cast<rgw::sal::RadosPlacementTier*>(tier); + string id = "cloudid"; + string endpoint = rtier->get_rt().t.s3.endpoint; + RGWAccessKey key = rtier->get_rt().t.s3.key; + string region = rtier->get_rt().t.s3.region; + HostStyle host_style = rtier->get_rt().t.s3.host_style; + string bucket_name = rtier->get_rt().t.s3.target_path; + const rgw::sal::ZoneGroup& zonegroup = store->get_zone()->get_zonegroup(); + + if (bucket_name.empty()) { + bucket_name = "rgwx-" + zonegroup.get_name() + "-" + tier->get_storage_class() + + "-cloud-bucket"; + boost::algorithm::to_lower(bucket_name); + } + + /* Create RGW REST connection */ + S3RESTConn conn(cct, id, { endpoint }, key, zonegroup.get_id(), region, host_style); + + RGWLCCloudTierCtx tier_ctx(cct, dpp, o, store, bucket->get_info(), + this, conn, bucket_name, + rtier->get_rt().t.s3.target_storage_class); + tier_ctx.acl_mappings = rtier->get_rt().t.s3.acl_mappings; + tier_ctx.multipart_min_part_size = rtier->get_rt().t.s3.multipart_min_part_size; + tier_ctx.multipart_sync_threshold = rtier->get_rt().t.s3.multipart_sync_threshold; + tier_ctx.storage_class = tier->get_storage_class(); + + ldpp_dout(dpp, 0) << "Transitioning object(" << o.key << ") to the cloud endpoint(" << endpoint << ")" << dendl; + + /* Transition object to cloud end point */ + int ret = rgw_cloud_tier_transfer_object(tier_ctx, cloud_targets); + + if (ret < 0) { + ldpp_dout(dpp, 0) << "ERROR: failed to transfer object(" << o.key << ") to the cloud endpoint(" << endpoint << ") ret=" << ret << dendl; + return ret; + } + + if (update_object) { + real_time read_mtime; + + std::unique_ptr<rgw::sal::Object::ReadOp> read_op(get_read_op()); + read_op->params.lastmod = &read_mtime; + + ret = read_op->prepare(y, dpp); + if (ret < 0) { + ldpp_dout(dpp, 0) << "ERROR: Updating tier object(" << o.key << ") failed ret=" << ret << dendl; + return ret; + } + + if (read_mtime != tier_ctx.o.meta.mtime) { + /* raced */ + ldpp_dout(dpp, 0) << "ERROR: Updating tier object(" << o.key << ") failed ret=" << -ECANCELED << dendl; + return -ECANCELED; + } + + rgw_placement_rule target_placement; + target_placement.inherit_from(tier_ctx.bucket_info.placement_rule); + target_placement.storage_class = tier->get_storage_class(); + + ret = write_cloud_tier(dpp, y, tier_ctx.o.versioned_epoch, + tier, tier_ctx.is_multipart_upload, + target_placement, tier_ctx.obj); + + } + + return ret; +} + +int RadosObject::write_cloud_tier(const DoutPrefixProvider* dpp, + optional_yield y, + uint64_t olh_epoch, + PlacementTier* tier, + bool is_multipart_upload, + rgw_placement_rule& target_placement, + Object* head_obj) +{ + rgw::sal::RadosPlacementTier* rtier = static_cast<rgw::sal::RadosPlacementTier*>(tier); + map<string, bufferlist> attrs = get_attrs(); + RGWRados::Object op_target(store->getRados(), bucket->get_info(), *rados_ctx, get_obj()); + RGWRados::Object::Write obj_op(&op_target); + + obj_op.meta.modify_tail = true; + obj_op.meta.flags = PUT_OBJ_CREATE; + obj_op.meta.category = RGWObjCategory::CloudTiered; + obj_op.meta.delete_at = real_time(); + bufferlist blo; + obj_op.meta.data = &blo; + obj_op.meta.if_match = NULL; + obj_op.meta.if_nomatch = NULL; + obj_op.meta.user_data = NULL; + obj_op.meta.zones_trace = NULL; + obj_op.meta.delete_at = real_time(); + obj_op.meta.olh_epoch = olh_epoch; + + RGWObjManifest *pmanifest; + RGWObjManifest manifest; + + pmanifest = &manifest; + RGWObjTier tier_config; + tier_config.name = tier->get_storage_class(); + tier_config.tier_placement = rtier->get_rt(); + tier_config.is_multipart_upload = is_multipart_upload; + + pmanifest->set_tier_type("cloud-s3"); + pmanifest->set_tier_config(tier_config); + + /* check if its necessary */ + pmanifest->set_head(target_placement, head_obj->get_obj(), 0); + pmanifest->set_tail_placement(target_placement, head_obj->get_obj().bucket); + pmanifest->set_obj_size(0); + obj_op.meta.manifest = pmanifest; + + /* update storage class */ + bufferlist bl; + bl.append(tier->get_storage_class()); + attrs[RGW_ATTR_STORAGE_CLASS] = bl; + + attrs.erase(RGW_ATTR_ID_TAG); + attrs.erase(RGW_ATTR_TAIL_TAG); + + return obj_op.write_meta(dpp, 0, 0, attrs, y); +} + +int RadosObject::get_max_chunk_size(const DoutPrefixProvider* dpp, rgw_placement_rule placement_rule, uint64_t* max_chunk_size, uint64_t* alignment) +{ + return store->getRados()->get_max_chunk_size(placement_rule, get_obj(), max_chunk_size, dpp, alignment); +} + +void RadosObject::get_max_aligned_size(uint64_t size, uint64_t alignment, + uint64_t* max_size) +{ + store->getRados()->get_max_aligned_size(size, alignment, max_size); +} + +bool RadosObject::placement_rules_match(rgw_placement_rule& r1, rgw_placement_rule& r2) +{ + rgw_obj obj; + rgw_pool p1, p2; + + obj = get_obj(); + + if (r1 == r2) + return true; + + if (!store->getRados()->get_obj_data_pool(r1, obj, &p1)) { + return false; + } + if (!store->getRados()->get_obj_data_pool(r2, obj, &p2)) { + return false; + } + + return p1 == p2; +} + +int RadosObject::dump_obj_layout(const DoutPrefixProvider *dpp, optional_yield y, Formatter* f) +{ + int ret; + RGWObjManifest *amanifest{nullptr}; + rgw_raw_obj head_obj; + + RGWRados::Object op_target(store->getRados(), bucket->get_info(), *rados_ctx, get_obj()); + RGWRados::Object::Read parent_op(&op_target); + uint64_t obj_size; + + parent_op.params.obj_size = &obj_size; + parent_op.params.attrs = &get_attrs(); + + ret = parent_op.prepare(y, dpp); + if (ret < 0) { + return ret; + } + + head_obj = parent_op.state.head_obj; + + ret = op_target.get_manifest(dpp, &amanifest, y); + if (ret < 0) { + return ret; + } + + ::encode_json("head", head_obj, f); + ::encode_json("manifest", *amanifest, f); + f->open_array_section("data_location"); + for (auto miter = amanifest->obj_begin(dpp); miter != amanifest->obj_end(dpp); ++miter) { + f->open_object_section("obj"); + rgw_raw_obj raw_loc = miter.get_location().get_raw_obj(store->getRados()); + uint64_t ofs = miter.get_ofs(); + uint64_t left = amanifest->get_obj_size() - ofs; + ::encode_json("ofs", miter.get_ofs(), f); + ::encode_json("loc", raw_loc, f); + ::encode_json("loc_ofs", miter.location_ofs(), f); + uint64_t loc_size = miter.get_stripe_size(); + if (loc_size > left) { + loc_size = left; + } + ::encode_json("loc_size", loc_size, f); + f->close_section(); + } + f->close_section(); + + return 0; +} + +std::unique_ptr<Object::ReadOp> RadosObject::get_read_op() +{ + return std::make_unique<RadosObject::RadosReadOp>(this, rados_ctx); +} + +RadosObject::RadosReadOp::RadosReadOp(RadosObject *_source, RGWObjectCtx *_rctx) : + source(_source), + rctx(_rctx), + op_target(_source->store->getRados(), + _source->get_bucket()->get_info(), + *static_cast<RGWObjectCtx *>(rctx), + _source->get_obj()), + parent_op(&op_target) +{ } + +int RadosObject::RadosReadOp::prepare(optional_yield y, const DoutPrefixProvider* dpp) +{ + uint64_t obj_size; + + parent_op.conds.mod_ptr = params.mod_ptr; + parent_op.conds.unmod_ptr = params.unmod_ptr; + parent_op.conds.high_precision_time = params.high_precision_time; + parent_op.conds.mod_zone_id = params.mod_zone_id; + parent_op.conds.mod_pg_ver = params.mod_pg_ver; + parent_op.conds.if_match = params.if_match; + parent_op.conds.if_nomatch = params.if_nomatch; + parent_op.params.lastmod = params.lastmod; + parent_op.params.target_obj = params.target_obj; + parent_op.params.obj_size = &obj_size; + parent_op.params.attrs = &source->get_attrs(); + + int ret = parent_op.prepare(y, dpp); + if (ret < 0) + return ret; + + source->set_key(parent_op.state.obj.key); + source->set_obj_size(obj_size); + + return ret; +} + +int RadosObject::RadosReadOp::read(int64_t ofs, int64_t end, bufferlist& bl, optional_yield y, const DoutPrefixProvider* dpp) +{ + return parent_op.read(ofs, end, bl, y, dpp); +} + +int RadosObject::RadosReadOp::get_attr(const DoutPrefixProvider* dpp, const char* name, bufferlist& dest, optional_yield y) +{ + return parent_op.get_attr(dpp, name, dest, y); +} + +std::unique_ptr<Object::DeleteOp> RadosObject::get_delete_op() +{ + return std::make_unique<RadosObject::RadosDeleteOp>(this); +} + +RadosObject::RadosDeleteOp::RadosDeleteOp(RadosObject *_source) : + source(_source), + op_target(_source->store->getRados(), + _source->get_bucket()->get_info(), + _source->get_ctx(), + _source->get_obj()), + parent_op(&op_target) +{ } + +int RadosObject::RadosDeleteOp::delete_obj(const DoutPrefixProvider* dpp, optional_yield y) +{ + parent_op.params.bucket_owner = params.bucket_owner.get_id(); + parent_op.params.versioning_status = params.versioning_status; + parent_op.params.obj_owner = params.obj_owner; + parent_op.params.olh_epoch = params.olh_epoch; + parent_op.params.marker_version_id = params.marker_version_id; + parent_op.params.bilog_flags = params.bilog_flags; + parent_op.params.remove_objs = params.remove_objs; + parent_op.params.expiration_time = params.expiration_time; + parent_op.params.unmod_since = params.unmod_since; + parent_op.params.mtime = params.mtime; + parent_op.params.high_precision_time = params.high_precision_time; + parent_op.params.zones_trace = params.zones_trace; + parent_op.params.abortmp = params.abortmp; + parent_op.params.parts_accounted_size = params.parts_accounted_size; + + int ret = parent_op.delete_obj(y, dpp); + if (ret < 0) + return ret; + + result.delete_marker = parent_op.result.delete_marker; + result.version_id = parent_op.result.version_id; + + return ret; +} + +int RadosObject::delete_object(const DoutPrefixProvider* dpp, + optional_yield y, + bool prevent_versioning) +{ + RGWRados::Object del_target(store->getRados(), bucket->get_info(), *rados_ctx, get_obj()); + RGWRados::Object::Delete del_op(&del_target); + + del_op.params.bucket_owner = bucket->get_info().owner; + del_op.params.versioning_status = prevent_versioning ? 0 : bucket->get_info().versioning_status(); + + return del_op.delete_obj(y, dpp); +} + +int RadosObject::delete_obj_aio(const DoutPrefixProvider* dpp, RGWObjState* astate, + Completions* aio, bool keep_index_consistent, + optional_yield y) +{ + RadosCompletions* raio = static_cast<RadosCompletions*>(aio); + + return store->getRados()->delete_obj_aio(dpp, get_obj(), bucket->get_info(), astate, + raio->handles, keep_index_consistent, y); +} + +int RadosObject::copy_object(User* user, + req_info* info, + const rgw_zone_id& source_zone, + rgw::sal::Object* dest_object, + rgw::sal::Bucket* dest_bucket, + rgw::sal::Bucket* src_bucket, + const rgw_placement_rule& dest_placement, + ceph::real_time* src_mtime, + ceph::real_time* mtime, + const ceph::real_time* mod_ptr, + const ceph::real_time* unmod_ptr, + bool high_precision_time, + const char* if_match, + const char* if_nomatch, + AttrsMod attrs_mod, + bool copy_if_newer, + Attrs& attrs, + RGWObjCategory category, + uint64_t olh_epoch, + boost::optional<ceph::real_time> delete_at, + std::string* version_id, + std::string* tag, + std::string* etag, + void (*progress_cb)(off_t, void *), + void* progress_data, + const DoutPrefixProvider* dpp, + optional_yield y) +{ + return store->getRados()->copy_obj(*rados_ctx, + user->get_id(), + info, + source_zone, + dest_object->get_obj(), + get_obj(), + dest_bucket->get_info(), + src_bucket->get_info(), + dest_placement, + src_mtime, + mtime, + mod_ptr, + unmod_ptr, + high_precision_time, + if_match, + if_nomatch, + static_cast<RGWRados::AttrsMod>(attrs_mod), + copy_if_newer, + attrs, + category, + olh_epoch, + (delete_at ? *delete_at : real_time()), + version_id, + tag, + etag, + progress_cb, + progress_data, + dpp, + y); +} + +int RadosObject::RadosReadOp::iterate(const DoutPrefixProvider* dpp, int64_t ofs, int64_t end, RGWGetDataCB* cb, optional_yield y) +{ + return parent_op.iterate(dpp, ofs, end, cb, y); +} + +int RadosObject::swift_versioning_restore(bool& restored, + const DoutPrefixProvider* dpp) +{ + rgw_obj obj = get_obj(); + return store->getRados()->swift_versioning_restore(*rados_ctx, + bucket->get_owner()->get_id(), + bucket->get_info(), + obj, + restored, + dpp); +} + +int RadosObject::swift_versioning_copy(const DoutPrefixProvider* dpp, optional_yield y) +{ + return store->getRados()->swift_versioning_copy(*rados_ctx, + bucket->get_info().owner, + bucket->get_info(), + get_obj(), + dpp, + y); +} + +int RadosMultipartUpload::cleanup_part_history(const DoutPrefixProvider* dpp, + optional_yield y, + RadosMultipartPart *part, + list<rgw_obj_index_key>& remove_objs) +{ + cls_rgw_obj_chain chain; + for (auto& ppfx : part->get_past_prefixes()) { + rgw_obj past_obj; + past_obj.init_ns(bucket->get_key(), ppfx + "." + std::to_string(part->info.num), mp_ns); + rgw_obj_index_key past_key; + past_obj.key.get_index_key(&past_key); + // Remove past upload part objects from index, too. + remove_objs.push_back(past_key); + + RGWObjManifest manifest = part->get_manifest(); + manifest.set_prefix(ppfx); + RGWObjManifest::obj_iterator miter = manifest.obj_begin(dpp); + for (; miter != manifest.obj_end(dpp); ++miter) { + rgw_raw_obj raw_part_obj = miter.get_location().get_raw_obj(store->getRados()); + cls_rgw_obj_key part_key(raw_part_obj.oid); + chain.push_obj(raw_part_obj.pool.to_str(), part_key, raw_part_obj.loc); + } + } + if (store->getRados()->get_gc() == nullptr) { + // Delete objects inline if gc hasn't been initialised (in case when bypass gc is specified) + store->getRados()->delete_objs_inline(dpp, chain, mp_obj.get_upload_id()); + } else { + // use upload id as tag and do it synchronously + auto [ret, leftover_chain] = store->getRados()->send_chain_to_gc(chain, mp_obj.get_upload_id()); + if (ret < 0 && leftover_chain) { + ldpp_dout(dpp, 5) << __func__ << ": gc->send_chain() returned " << ret << dendl; + if (ret == -ENOENT) { + return -ERR_NO_SUCH_UPLOAD; + } + // Delete objects inline if send chain to gc fails + store->getRados()->delete_objs_inline(dpp, *leftover_chain, mp_obj.get_upload_id()); + } + } + return 0; +} + + +int RadosMultipartUpload::abort(const DoutPrefixProvider *dpp, CephContext *cct) +{ + std::unique_ptr<rgw::sal::Object> meta_obj = get_meta_obj(); + meta_obj->set_in_extra_data(true); + meta_obj->set_hash_source(mp_obj.get_key()); + cls_rgw_obj_chain chain; + list<rgw_obj_index_key> remove_objs; + bool truncated; + int marker = 0; + int ret; + uint64_t parts_accounted_size = 0; + + do { + ret = list_parts(dpp, cct, 1000, marker, &marker, &truncated); + if (ret < 0) { + ldpp_dout(dpp, 20) << __func__ << ": RadosMultipartUpload::list_parts returned " << + ret << dendl; + return (ret == -ENOENT) ? -ERR_NO_SUCH_UPLOAD : ret; + } + + for (auto part_it = parts.begin(); + part_it != parts.end(); + ++part_it) { + RadosMultipartPart* obj_part = dynamic_cast<RadosMultipartPart*>(part_it->second.get()); + if (obj_part->info.manifest.empty()) { + std::unique_ptr<rgw::sal::Object> obj = bucket->get_object( + rgw_obj_key(obj_part->oid, std::string(), RGW_OBJ_NS_MULTIPART)); + obj->set_hash_source(mp_obj.get_key()); + ret = obj->delete_object(dpp, null_yield); + if (ret < 0 && ret != -ENOENT) + return ret; + } else { + auto target = meta_obj->get_obj(); + store->getRados()->update_gc_chain(dpp, target, obj_part->info.manifest, &chain); + RGWObjManifest::obj_iterator oiter = obj_part->info.manifest.obj_begin(dpp); + if (oiter != obj_part->info.manifest.obj_end(dpp)) { + std::unique_ptr<rgw::sal::Object> head = bucket->get_object(rgw_obj_key()); + rgw_raw_obj raw_head = oiter.get_location().get_raw_obj(store->getRados()); + dynamic_cast<rgw::sal::RadosObject*>(head.get())->raw_obj_to_obj(raw_head); + + rgw_obj_index_key key; + head->get_key().get_index_key(&key); + remove_objs.push_back(key); + + cleanup_part_history(dpp, null_yield, obj_part, remove_objs); + } + } + parts_accounted_size += obj_part->info.accounted_size; + } + } while (truncated); + + if (store->getRados()->get_gc() == nullptr) { + //Delete objects inline if gc hasn't been initialised (in case when bypass gc is specified) + store->getRados()->delete_objs_inline(dpp, chain, mp_obj.get_upload_id()); + } else { + /* use upload id as tag and do it synchronously */ + auto [ret, leftover_chain] = store->getRados()->send_chain_to_gc(chain, mp_obj.get_upload_id()); + if (ret < 0 && leftover_chain) { + ldpp_dout(dpp, 5) << __func__ << ": gc->send_chain() returned " << ret << dendl; + if (ret == -ENOENT) { + return -ERR_NO_SUCH_UPLOAD; + } + //Delete objects inline if send chain to gc fails + store->getRados()->delete_objs_inline(dpp, *leftover_chain, mp_obj.get_upload_id()); + } + } + + std::unique_ptr<rgw::sal::Object::DeleteOp> del_op = meta_obj->get_delete_op(); + del_op->params.bucket_owner = bucket->get_acl_owner(); + del_op->params.versioning_status = 0; + if (!remove_objs.empty()) { + del_op->params.remove_objs = &remove_objs; + } + + del_op->params.abortmp = true; + del_op->params.parts_accounted_size = parts_accounted_size; + + // and also remove the metadata obj + ret = del_op->delete_obj(dpp, null_yield); + if (ret < 0) { + ldpp_dout(dpp, 20) << __func__ << ": del_op.delete_obj returned " << + ret << dendl; + } + return (ret == -ENOENT) ? -ERR_NO_SUCH_UPLOAD : ret; +} + +std::unique_ptr<rgw::sal::Object> RadosMultipartUpload::get_meta_obj() +{ + return bucket->get_object(rgw_obj_key(get_meta(), string(), mp_ns)); +} + +int RadosMultipartUpload::init(const DoutPrefixProvider *dpp, optional_yield y, ACLOwner& owner, rgw_placement_rule& dest_placement, rgw::sal::Attrs& attrs) +{ + int ret; + std::string oid = mp_obj.get_key(); + RGWObjectCtx obj_ctx(store); + + do { + char buf[33]; + string tmp_obj_name; + std::unique_ptr<rgw::sal::Object> obj; + gen_rand_alphanumeric(store->ctx(), buf, sizeof(buf) - 1); + std::string upload_id = MULTIPART_UPLOAD_ID_PREFIX; /* v2 upload id */ + upload_id.append(buf); + + mp_obj.init(oid, upload_id); + tmp_obj_name = mp_obj.get_meta(); + + obj = bucket->get_object(rgw_obj_key(tmp_obj_name, string(), mp_ns)); + // the meta object will be indexed with 0 size, we c + obj->set_in_extra_data(true); + obj->set_hash_source(oid); + + RGWRados::Object op_target(store->getRados(), + obj->get_bucket()->get_info(), + obj_ctx, obj->get_obj()); + RGWRados::Object::Write obj_op(&op_target); + + op_target.set_versioning_disabled(true); /* no versioning for multipart meta */ + obj_op.meta.owner = owner.get_id(); + obj_op.meta.category = RGWObjCategory::MultiMeta; + obj_op.meta.flags = PUT_OBJ_CREATE_EXCL; + obj_op.meta.mtime = &mtime; + + multipart_upload_info upload_info; + upload_info.dest_placement = dest_placement; + + bufferlist bl; + encode(upload_info, bl); + obj_op.meta.data = &bl; + + ret = obj_op.write_meta(dpp, bl.length(), 0, attrs, y); + } while (ret == -EEXIST); + + return ret; +} + +int RadosMultipartUpload::list_parts(const DoutPrefixProvider *dpp, CephContext *cct, + int num_parts, int marker, + int *next_marker, bool *truncated, + bool assume_unsorted) +{ + map<string, bufferlist> parts_map; + map<string, bufferlist>::iterator iter; + + std::unique_ptr<rgw::sal::Object> obj = bucket->get_object( + rgw_obj_key(get_meta(), std::string(), RGW_OBJ_NS_MULTIPART)); + obj->set_in_extra_data(true); + + bool sorted_omap = is_v2_upload_id(get_upload_id()) && !assume_unsorted; + + parts.clear(); + + int ret; + if (sorted_omap) { + string p; + p = "part."; + char buf[32]; + + snprintf(buf, sizeof(buf), "%08d", marker); + p.append(buf); + + ret = obj->omap_get_vals(dpp, p, num_parts + 1, &parts_map, + nullptr, null_yield); + } else { + ret = obj->omap_get_all(dpp, &parts_map, null_yield); + } + if (ret < 0) { + return ret; + } + + int i; + int last_num = 0; + + uint32_t expected_next = marker + 1; + + for (i = 0, iter = parts_map.begin(); + (i < num_parts || !sorted_omap) && iter != parts_map.end(); + ++iter, ++i) { + bufferlist& bl = iter->second; + auto bli = bl.cbegin(); + std::unique_ptr<RadosMultipartPart> part = std::make_unique<RadosMultipartPart>(); + try { + decode(part->info, bli); + } catch (buffer::error& err) { + ldpp_dout(dpp, 0) << "ERROR: could not part info, caught buffer::error" << + dendl; + return -EIO; + } + if (sorted_omap) { + if (part->info.num != expected_next) { + /* ouch, we expected a specific part num here, but we got a + * different one. Either a part is missing, or it could be a + * case of mixed rgw versions working on the same upload, + * where one gateway doesn't support correctly sorted omap + * keys for multipart upload just assume data is unsorted. + */ + return list_parts(dpp, cct, num_parts, marker, next_marker, truncated, true); + } + expected_next++; + } + if (sorted_omap || + (int)part->info.num > marker) { + last_num = part->info.num; + parts[part->info.num] = std::move(part); + } + } + + if (sorted_omap) { + if (truncated) { + *truncated = (iter != parts_map.end()); + } + } else { + /* rebuild a map with only num_parts entries */ + std::map<uint32_t, std::unique_ptr<MultipartPart>> new_parts; + std::map<uint32_t, std::unique_ptr<MultipartPart>>::iterator piter; + for (i = 0, piter = parts.begin(); + i < num_parts && piter != parts.end(); + ++i, ++piter) { + last_num = piter->first; + new_parts[piter->first] = std::move(piter->second); + } + + if (truncated) { + *truncated = (piter != parts.end()); + } + + parts.swap(new_parts); + } + + if (next_marker) { + *next_marker = last_num; + } + + return 0; +} + +int RadosMultipartUpload::complete(const DoutPrefixProvider *dpp, + optional_yield y, CephContext* cct, + map<int, string>& part_etags, + list<rgw_obj_index_key>& remove_objs, + uint64_t& accounted_size, bool& compressed, + RGWCompressionInfo& cs_info, off_t& ofs, + std::string& tag, ACLOwner& owner, + uint64_t olh_epoch, + rgw::sal::Object* target_obj) +{ + char final_etag[CEPH_CRYPTO_MD5_DIGESTSIZE]; + char final_etag_str[CEPH_CRYPTO_MD5_DIGESTSIZE * 2 + 16]; + std::string etag; + bufferlist etag_bl; + MD5 hash; + // Allow use of MD5 digest in FIPS mode for non-cryptographic purposes + hash.SetFlags(EVP_MD_CTX_FLAG_NON_FIPS_ALLOW); + bool truncated; + int ret; + + int total_parts = 0; + int handled_parts = 0; + int max_parts = 1000; + int marker = 0; + uint64_t min_part_size = cct->_conf->rgw_multipart_min_part_size; + auto etags_iter = part_etags.begin(); + rgw::sal::Attrs attrs = target_obj->get_attrs(); + + do { + ret = list_parts(dpp, cct, max_parts, marker, &marker, &truncated); + if (ret == -ENOENT) { + ret = -ERR_NO_SUCH_UPLOAD; + } + if (ret < 0) + return ret; + + total_parts += parts.size(); + if (!truncated && total_parts != (int)part_etags.size()) { + ldpp_dout(dpp, 0) << "NOTICE: total parts mismatch: have: " << total_parts + << " expected: " << part_etags.size() << dendl; + ret = -ERR_INVALID_PART; + return ret; + } + + for (auto obj_iter = parts.begin(); etags_iter != part_etags.end() && obj_iter != parts.end(); ++etags_iter, ++obj_iter, ++handled_parts) { + RadosMultipartPart* part = dynamic_cast<rgw::sal::RadosMultipartPart*>(obj_iter->second.get()); + uint64_t part_size = part->get_size(); + if (handled_parts < (int)part_etags.size() - 1 && + part_size < min_part_size) { + ret = -ERR_TOO_SMALL; + return ret; + } + + char petag[CEPH_CRYPTO_MD5_DIGESTSIZE]; + if (etags_iter->first != (int)obj_iter->first) { + ldpp_dout(dpp, 0) << "NOTICE: parts num mismatch: next requested: " + << etags_iter->first << " next uploaded: " + << obj_iter->first << dendl; + ret = -ERR_INVALID_PART; + return ret; + } + string part_etag = rgw_string_unquote(etags_iter->second); + if (part_etag.compare(part->get_etag()) != 0) { + ldpp_dout(dpp, 0) << "NOTICE: etag mismatch: part: " << etags_iter->first + << " etag: " << etags_iter->second << dendl; + ret = -ERR_INVALID_PART; + return ret; + } + + hex_to_buf(part->get_etag().c_str(), petag, + CEPH_CRYPTO_MD5_DIGESTSIZE); + hash.Update((const unsigned char *)petag, sizeof(petag)); + + RGWUploadPartInfo& obj_part = part->info; + + /* update manifest for part */ + string oid = mp_obj.get_part(part->info.num); + rgw_obj src_obj; + src_obj.init_ns(bucket->get_key(), oid, mp_ns); + + if (obj_part.manifest.empty()) { + ldpp_dout(dpp, 0) << "ERROR: empty manifest for object part: obj=" + << src_obj << dendl; + ret = -ERR_INVALID_PART; + return ret; + } else { + manifest.append(dpp, obj_part.manifest, store->svc()->zone->get_zonegroup(), store->svc()->zone->get_zone_params()); + auto manifest_prefix = part->info.manifest.get_prefix(); + if (not manifest_prefix.empty()) { + // It has an explicit prefix. Override the default one. + src_obj.init_ns(bucket->get_key(), manifest_prefix + "." + std::to_string(part->info.num), mp_ns); + } + } + + bool part_compressed = (obj_part.cs_info.compression_type != "none"); + if ((handled_parts > 0) && + ((part_compressed != compressed) || + (cs_info.compression_type != obj_part.cs_info.compression_type))) { + ldpp_dout(dpp, 0) << "ERROR: compression type was changed during multipart upload (" + << cs_info.compression_type << ">>" << obj_part.cs_info.compression_type << ")" << dendl; + ret = -ERR_INVALID_PART; + return ret; + } + + if (part_compressed) { + int64_t new_ofs; // offset in compression data for new part + if (cs_info.blocks.size() > 0) + new_ofs = cs_info.blocks.back().new_ofs + cs_info.blocks.back().len; + else + new_ofs = 0; + for (const auto& block : obj_part.cs_info.blocks) { + compression_block cb; + cb.old_ofs = block.old_ofs + cs_info.orig_size; + cb.new_ofs = new_ofs; + cb.len = block.len; + cs_info.blocks.push_back(cb); + new_ofs = cb.new_ofs + cb.len; + } + if (!compressed) + cs_info.compression_type = obj_part.cs_info.compression_type; + cs_info.orig_size += obj_part.cs_info.orig_size; + compressed = true; + } + + rgw_obj_index_key remove_key; + src_obj.key.get_index_key(&remove_key); + + remove_objs.push_back(remove_key); + + cleanup_part_history(dpp, y, part, remove_objs); + + ofs += obj_part.size; + accounted_size += obj_part.accounted_size; + } + } while (truncated); + hash.Final((unsigned char *)final_etag); + + buf_to_hex((unsigned char *)final_etag, sizeof(final_etag), final_etag_str); + snprintf(&final_etag_str[CEPH_CRYPTO_MD5_DIGESTSIZE * 2], + sizeof(final_etag_str) - CEPH_CRYPTO_MD5_DIGESTSIZE * 2, + "-%lld", (long long)part_etags.size()); + etag = final_etag_str; + ldpp_dout(dpp, 10) << "calculated etag: " << etag << dendl; + + etag_bl.append(etag); + + attrs[RGW_ATTR_ETAG] = etag_bl; + + if (compressed) { + // write compression attribute to full object + bufferlist tmp; + encode(cs_info, tmp); + attrs[RGW_ATTR_COMPRESSION] = tmp; + } + + target_obj->set_atomic(); + + RGWRados::Object op_target(store->getRados(), + target_obj->get_bucket()->get_info(), + dynamic_cast<RadosObject*>(target_obj)->get_ctx(), + target_obj->get_obj()); + RGWRados::Object::Write obj_op(&op_target); + + obj_op.meta.manifest = &manifest; + obj_op.meta.remove_objs = &remove_objs; + + obj_op.meta.ptag = &tag; /* use req_id as operation tag */ + obj_op.meta.owner = owner.get_id(); + obj_op.meta.flags = PUT_OBJ_CREATE; + obj_op.meta.modify_tail = true; + obj_op.meta.completeMultipart = true; + obj_op.meta.olh_epoch = olh_epoch; + + ret = obj_op.write_meta(dpp, ofs, accounted_size, attrs, y); + if (ret < 0) + return ret; + + return ret; +} + +int RadosMultipartUpload::get_info(const DoutPrefixProvider *dpp, optional_yield y, rgw_placement_rule** rule, rgw::sal::Attrs* attrs) +{ + if (!rule && !attrs) { + return 0; + } + + if (rule) { + if (!placement.empty()) { + *rule = &placement; + if (!attrs) { + /* Don't need attrs, done */ + return 0; + } + } else { + *rule = nullptr; + } + } + + /* We need either attributes or placement, so we need a read */ + std::unique_ptr<rgw::sal::Object> meta_obj; + meta_obj = get_meta_obj(); + meta_obj->set_in_extra_data(true); + + multipart_upload_info upload_info; + bufferlist headbl; + + /* Read the obj head which contains the multipart_upload_info */ + std::unique_ptr<rgw::sal::Object::ReadOp> read_op = meta_obj->get_read_op(); + meta_obj->set_prefetch_data(); + + int ret = read_op->prepare(y, dpp); + if (ret < 0) { + if (ret == -ENOENT) { + return -ERR_NO_SUCH_UPLOAD; + } + return ret; + } + + extract_span_context(meta_obj->get_attrs(), trace_ctx); + + if (attrs) { + /* Attrs are filled in by prepare */ + *attrs = meta_obj->get_attrs(); + if (!rule || *rule != nullptr) { + /* placement was cached; don't actually read */ + return 0; + } + } + + /* Now read the placement from the head */ + ret = read_op->read(0, store->ctx()->_conf->rgw_max_chunk_size, headbl, y, dpp); + if (ret < 0) { + if (ret == -ENOENT) { + return -ERR_NO_SUCH_UPLOAD; + } + return ret; + } + + if (headbl.length() <= 0) { + return -ERR_NO_SUCH_UPLOAD; + } + + /* Decode multipart_upload_info */ + auto hiter = headbl.cbegin(); + try { + decode(upload_info, hiter); + } catch (buffer::error& err) { + ldpp_dout(dpp, 0) << "ERROR: failed to decode multipart upload info" << dendl; + return -EIO; + } + placement = upload_info.dest_placement; + *rule = &placement; + + return 0; +} + +std::unique_ptr<Writer> RadosMultipartUpload::get_writer( + const DoutPrefixProvider *dpp, + optional_yield y, + rgw::sal::Object* obj, + const rgw_user& owner, + const rgw_placement_rule *ptail_placement_rule, + uint64_t part_num, + const std::string& part_num_str) +{ + RGWBucketInfo& bucket_info = obj->get_bucket()->get_info(); + RGWObjectCtx& obj_ctx = static_cast<RadosObject*>(obj)->get_ctx(); + auto aio = rgw::make_throttle(store->ctx()->_conf->rgw_put_obj_min_window_size, y); + return std::make_unique<RadosMultipartWriter>(dpp, y, get_upload_id(), + bucket_info, obj_ctx, + obj->get_obj(), store, std::move(aio), owner, + ptail_placement_rule, part_num, part_num_str); +} + +MPRadosSerializer::MPRadosSerializer(const DoutPrefixProvider *dpp, RadosStore* store, RadosObject* obj, const std::string& lock_name) : + lock(lock_name) +{ + rgw_pool meta_pool; + rgw_raw_obj raw_obj; + + obj->get_raw_obj(&raw_obj); + oid = raw_obj.oid; + store->getRados()->get_obj_data_pool(obj->get_bucket()->get_placement_rule(), + obj->get_obj(), &meta_pool); + store->getRados()->open_pool_ctx(dpp, meta_pool, ioctx, true, true); +} + +int MPRadosSerializer::try_lock(const DoutPrefixProvider *dpp, utime_t dur, optional_yield y) +{ + op.assert_exists(); + lock.set_duration(dur); + lock.lock_exclusive(&op); + int ret = rgw_rados_operate(dpp, ioctx, oid, &op, y); + if (! ret) { + locked = true; + } + return ret; +} + +LCRadosSerializer::LCRadosSerializer(RadosStore* store, const std::string& _oid, const std::string& lock_name, const std::string& cookie) : + StoreLCSerializer(_oid), + lock(lock_name) +{ + ioctx = &store->getRados()->lc_pool_ctx; + lock.set_cookie(cookie); +} + +int LCRadosSerializer::try_lock(const DoutPrefixProvider *dpp, utime_t dur, optional_yield y) +{ + lock.set_duration(dur); + return lock.lock_exclusive(ioctx, oid); +} + +int RadosLifecycle::get_entry(const std::string& oid, const std::string& marker, + std::unique_ptr<LCEntry>* entry) +{ + cls_rgw_lc_entry cls_entry; + int ret = cls_rgw_lc_get_entry(*store->getRados()->get_lc_pool_ctx(), oid, marker, cls_entry); + if (ret) + return ret; + + LCEntry* e; + e = new StoreLCEntry(cls_entry.bucket, cls_entry.start_time, cls_entry.status); + if (!e) + return -ENOMEM; + + entry->reset(e); + return 0; +} + +int RadosLifecycle::get_next_entry(const std::string& oid, const std::string& marker, + std::unique_ptr<LCEntry>* entry) +{ + cls_rgw_lc_entry cls_entry; + int ret = cls_rgw_lc_get_next_entry(*store->getRados()->get_lc_pool_ctx(), oid, marker, + cls_entry); + + if (ret) + return ret; + + LCEntry* e; + e = new StoreLCEntry(cls_entry.bucket, cls_entry.start_time, cls_entry.status); + if (!e) + return -ENOMEM; + + entry->reset(e); + return 0; +} + +int RadosLifecycle::set_entry(const std::string& oid, LCEntry& entry) +{ + cls_rgw_lc_entry cls_entry; + + cls_entry.bucket = entry.get_bucket(); + cls_entry.start_time = entry.get_start_time(); + cls_entry.status = entry.get_status(); + + return cls_rgw_lc_set_entry(*store->getRados()->get_lc_pool_ctx(), oid, cls_entry); +} + +int RadosLifecycle::list_entries(const std::string& oid, const std::string& marker, + uint32_t max_entries, std::vector<std::unique_ptr<LCEntry>>& entries) +{ + entries.clear(); + + vector<cls_rgw_lc_entry> cls_entries; + int ret = cls_rgw_lc_list(*store->getRados()->get_lc_pool_ctx(), oid, marker, max_entries, cls_entries); + + if (ret < 0) + return ret; + + for (auto& entry : cls_entries) { + entries.push_back(std::make_unique<StoreLCEntry>(entry.bucket, oid, + entry.start_time, entry.status)); + } + + return ret; +} + +int RadosLifecycle::rm_entry(const std::string& oid, LCEntry& entry) +{ + cls_rgw_lc_entry cls_entry; + + cls_entry.bucket = entry.get_bucket(); + cls_entry.start_time = entry.get_start_time(); + cls_entry.status = entry.get_status(); + + return cls_rgw_lc_rm_entry(*store->getRados()->get_lc_pool_ctx(), oid, cls_entry); +} + +int RadosLifecycle::get_head(const std::string& oid, std::unique_ptr<LCHead>* head) +{ + cls_rgw_lc_obj_head cls_head; + int ret = cls_rgw_lc_get_head(*store->getRados()->get_lc_pool_ctx(), oid, cls_head); + if (ret) + return ret; + + LCHead* h; + h = new StoreLCHead(cls_head.start_date, cls_head.shard_rollover_date, cls_head.marker); + if (!h) + return -ENOMEM; + + head->reset(h); + return 0; +} + +int RadosLifecycle::put_head(const std::string& oid, LCHead& head) +{ + cls_rgw_lc_obj_head cls_head; + + cls_head.marker = head.get_marker(); + cls_head.start_date = head.get_start_date(); + cls_head.shard_rollover_date = head.get_shard_rollover_date(); + + return cls_rgw_lc_put_head(*store->getRados()->get_lc_pool_ctx(), oid, cls_head); +} + +std::unique_ptr<LCSerializer> RadosLifecycle::get_serializer(const std::string& lock_name, + const std::string& oid, + const std::string& cookie) +{ + return std::make_unique<LCRadosSerializer>(store, oid, lock_name, cookie); +} + +int RadosNotification::publish_reserve(const DoutPrefixProvider *dpp, RGWObjTags* obj_tags) +{ + return rgw::notify::publish_reserve(dpp, event_type, res, obj_tags); +} + +int RadosNotification::publish_commit(const DoutPrefixProvider* dpp, uint64_t size, + const ceph::real_time& mtime, const std::string& etag, const std::string& version) +{ + return rgw::notify::publish_commit(obj, size, mtime, etag, version, event_type, res, dpp); +} + +int RadosAtomicWriter::prepare(optional_yield y) +{ + return processor.prepare(y); +} + +int RadosAtomicWriter::process(bufferlist&& data, uint64_t offset) +{ + return processor.process(std::move(data), offset); +} + +int RadosAtomicWriter::complete(size_t accounted_size, const std::string& etag, + ceph::real_time *mtime, ceph::real_time set_mtime, + std::map<std::string, bufferlist>& attrs, + ceph::real_time delete_at, + const char *if_match, const char *if_nomatch, + const std::string *user_data, + rgw_zone_set *zones_trace, bool *canceled, + optional_yield y) +{ + return processor.complete(accounted_size, etag, mtime, set_mtime, attrs, delete_at, + if_match, if_nomatch, user_data, zones_trace, canceled, y); +} + +int RadosAppendWriter::prepare(optional_yield y) +{ + return processor.prepare(y); +} + +int RadosAppendWriter::process(bufferlist&& data, uint64_t offset) +{ + return processor.process(std::move(data), offset); +} + +int RadosAppendWriter::complete(size_t accounted_size, const std::string& etag, + ceph::real_time *mtime, ceph::real_time set_mtime, + std::map<std::string, bufferlist>& attrs, + ceph::real_time delete_at, + const char *if_match, const char *if_nomatch, + const std::string *user_data, + rgw_zone_set *zones_trace, bool *canceled, + optional_yield y) +{ + return processor.complete(accounted_size, etag, mtime, set_mtime, attrs, delete_at, + if_match, if_nomatch, user_data, zones_trace, canceled, y); +} + +int RadosMultipartWriter::prepare(optional_yield y) +{ + return processor.prepare(y); +} + +int RadosMultipartWriter::process(bufferlist&& data, uint64_t offset) +{ + return processor.process(std::move(data), offset); +} + +int RadosMultipartWriter::complete(size_t accounted_size, const std::string& etag, + ceph::real_time *mtime, ceph::real_time set_mtime, + std::map<std::string, bufferlist>& attrs, + ceph::real_time delete_at, + const char *if_match, const char *if_nomatch, + const std::string *user_data, + rgw_zone_set *zones_trace, bool *canceled, + optional_yield y) +{ + return processor.complete(accounted_size, etag, mtime, set_mtime, attrs, delete_at, + if_match, if_nomatch, user_data, zones_trace, canceled, y); +} + +const std::string& RadosZoneGroup::get_endpoint() const +{ + if (!group.endpoints.empty()) { + return group.endpoints.front(); + } else { + // use zonegroup's master zone endpoints + auto z = group.zones.find(group.master_zone); + if (z != group.zones.end() && !z->second.endpoints.empty()) { + return z->second.endpoints.front(); + } + } + return empty; +} + +bool RadosZoneGroup::placement_target_exists(std::string& target) const +{ + return !!group.placement_targets.count(target); +} + +void RadosZoneGroup::get_placement_target_names(std::set<std::string>& names) const +{ + for (const auto& target : group.placement_targets) { + names.emplace(target.second.name); + } +} + +int RadosZoneGroup::get_placement_tier(const rgw_placement_rule& rule, + std::unique_ptr<PlacementTier>* tier) +{ + std::map<std::string, RGWZoneGroupPlacementTarget>::const_iterator titer; + titer = group.placement_targets.find(rule.name); + if (titer == group.placement_targets.end()) { + return -ENOENT; + } + + const auto& target_rule = titer->second; + std::map<std::string, RGWZoneGroupPlacementTier>::const_iterator ttier; + ttier = target_rule.tier_targets.find(rule.storage_class); + if (ttier == target_rule.tier_targets.end()) { + // not found + return -ENOENT; + } + + PlacementTier* t; + t = new RadosPlacementTier(store, ttier->second); + if (!t) + return -ENOMEM; + + tier->reset(t); + return 0; +} + +int RadosZoneGroup::get_zone_by_id(const std::string& id, std::unique_ptr<Zone>* zone) +{ + RGWZone* rz = store->svc()->zone->find_zone(id); + if (!rz) + return -ENOENT; + + Zone* z = new RadosZone(store, clone(), *rz); + zone->reset(z); + return 0; +} + +int RadosZoneGroup::get_zone_by_name(const std::string& name, std::unique_ptr<Zone>* zone) +{ + rgw_zone_id id; + int ret = store->svc()->zone->find_zone_id_by_name(name, &id); + if (ret < 0) + return ret; + + RGWZone* rz = store->svc()->zone->find_zone(id.id); + if (!rz) + return -ENOENT; + + Zone* z = new RadosZone(store, clone(), *rz); + zone->reset(z); + return 0; +} + +int RadosZoneGroup::list_zones(std::list<std::string>& zone_ids) +{ + for (const auto& entry : group.zones) + { + zone_ids.push_back(entry.second.id); + } + return 0; +} + +std::unique_ptr<Zone> RadosZone::clone() +{ + if (local_zone) + return std::make_unique<RadosZone>(store, group->clone()); + + return std::make_unique<RadosZone>(store, group->clone(), rgw_zone); +} + +const std::string& RadosZone::get_id() +{ + if (local_zone) + return store->svc()->zone->zone_id().id; + + return rgw_zone.id; +} + +const std::string& RadosZone::get_name() const +{ + if (local_zone) + return store->svc()->zone->zone_name(); + + return rgw_zone.name; +} + +bool RadosZone::is_writeable() +{ + if (local_zone) + return store->svc()->zone->zone_is_writeable(); + + return !rgw_zone.read_only; +} + +bool RadosZone::get_redirect_endpoint(std::string* endpoint) +{ + if (local_zone) + return store->svc()->zone->get_redirect_zone_endpoint(endpoint); + + endpoint = &rgw_zone.redirect_zone; + return true; +} + +bool RadosZone::has_zonegroup_api(const std::string& api) const +{ + return store->svc()->zone->has_zonegroup_api(api); +} + +const std::string& RadosZone::get_current_period_id() +{ + return store->svc()->zone->get_current_period_id(); +} + +const RGWAccessKey& RadosZone::get_system_key() +{ + return store->svc()->zone->get_zone_params().system_key; +} + +const std::string& RadosZone::get_realm_name() +{ + return store->svc()->zone->get_realm().get_name(); +} + +const std::string& RadosZone::get_realm_id() +{ + return store->svc()->zone->get_realm().get_id(); +} + +const std::string_view RadosZone::get_tier_type() +{ + if (local_zone) + return store->svc()->zone->get_zone().tier_type; + + return rgw_zone.id; +} + +RGWBucketSyncPolicyHandlerRef RadosZone::get_sync_policy_handler() +{ + return store->svc()->zone->get_sync_policy_handler(get_id()); +} + +RadosLuaManager::RadosLuaManager(RadosStore* _s) : + store(_s), + pool((store->svc() && store->svc()->zone) ? store->svc()->zone->get_zone_params().log_pool : rgw_pool()) +{ } + +int RadosLuaManager::get_script(const DoutPrefixProvider* dpp, optional_yield y, const std::string& key, std::string& script) +{ + if (pool.empty()) { + ldpp_dout(dpp, 10) << "WARNING: missing pool when reading lua script " << dendl; + return 0; + } + bufferlist bl; + + int r = rgw_get_system_obj(store->svc()->sysobj, pool, key, bl, nullptr, nullptr, y, dpp); + if (r < 0) { + return r; + } + + auto iter = bl.cbegin(); + try { + ceph::decode(script, iter); + } catch (buffer::error& err) { + return -EIO; + } + + return 0; +} + +int RadosLuaManager::put_script(const DoutPrefixProvider* dpp, optional_yield y, const std::string& key, const std::string& script) +{ + if (pool.empty()) { + ldpp_dout(dpp, 10) << "WARNING: missing pool when writing lua script " << dendl; + return 0; + } + bufferlist bl; + ceph::encode(script, bl); + + int r = rgw_put_system_obj(dpp, store->svc()->sysobj, pool, key, bl, false, nullptr, real_time(), y); + if (r < 0) { + return r; + } + + return 0; +} + +int RadosLuaManager::del_script(const DoutPrefixProvider* dpp, optional_yield y, const std::string& key) +{ + if (pool.empty()) { + ldpp_dout(dpp, 10) << "WARNING: missing pool when deleting lua script " << dendl; + return 0; + } + int r = rgw_delete_system_obj(dpp, store->svc()->sysobj, pool, key, nullptr, y); + if (r < 0 && r != -ENOENT) { + return r; + } + + return 0; +} + +const std::string PACKAGE_LIST_OBJECT_NAME = "lua_package_allowlist"; + +int RadosLuaManager::add_package(const DoutPrefixProvider *dpp, optional_yield y, const std::string& package_name) +{ + // add package to list + const bufferlist empty_bl; + std::map<std::string, bufferlist> new_package{{package_name, empty_bl}}; + librados::ObjectWriteOperation op; + op.omap_set(new_package); + auto ret = rgw_rados_operate(dpp, *(store->getRados()->get_lc_pool_ctx()), + PACKAGE_LIST_OBJECT_NAME, &op, y); + + if (ret < 0) { + return ret; + } + return 0; +} + +int RadosLuaManager::remove_package(const DoutPrefixProvider *dpp, optional_yield y, const std::string& package_name) +{ + librados::ObjectWriteOperation op; + size_t pos = package_name.find(" "); + if (pos != package_name.npos) { + // remove specfic version of the the package + op.omap_rm_keys(std::set<std::string>({package_name})); + auto ret = rgw_rados_operate(dpp, *(store->getRados()->get_lc_pool_ctx()), + PACKAGE_LIST_OBJECT_NAME, &op, y); + if (ret < 0) { + return ret; + } + return 0; + } + // otherwise, remove any existing versions of the package + rgw::lua::packages_t packages; + auto ret = list_packages(dpp, y, packages); + if (ret < 0 && ret != -ENOENT) { + return ret; + } + for(const auto& package : packages) { + const std::string package_no_version = package.substr(0, package.find(" ")); + if (package_no_version.compare(package_name) == 0) { + op.omap_rm_keys(std::set<std::string>({package})); + ret = rgw_rados_operate(dpp, *(store->getRados()->get_lc_pool_ctx()), + PACKAGE_LIST_OBJECT_NAME, &op, y); + if (ret < 0) { + return ret; + } + } + } + return 0; +} + +int RadosLuaManager::list_packages(const DoutPrefixProvider *dpp, optional_yield y, rgw::lua::packages_t& packages) +{ + constexpr auto max_chunk = 1024U; + std::string start_after; + bool more = true; + int rval; + while (more) { + librados::ObjectReadOperation op; + rgw::lua::packages_t packages_chunk; + op.omap_get_keys2(start_after, max_chunk, &packages_chunk, &more, &rval); + const auto ret = rgw_rados_operate(dpp, *(store->getRados()->get_lc_pool_ctx()), + PACKAGE_LIST_OBJECT_NAME, &op, nullptr, y); + + if (ret < 0) { + return ret; + } + + packages.merge(packages_chunk); + } + + return 0; +} + +int RadosOIDCProvider::store_url(const DoutPrefixProvider *dpp, const std::string& url, bool exclusive, optional_yield y) +{ + auto sysobj = store->svc()->sysobj; + std::string oid = tenant + get_url_oid_prefix() + url; + + bufferlist bl; + using ceph::encode; + encode(*this, bl); + return rgw_put_system_obj(dpp, sysobj, store->svc()->zone->get_zone_params().oidc_pool, oid, bl, exclusive, nullptr, real_time(), y); +} + +int RadosOIDCProvider::read_url(const DoutPrefixProvider *dpp, const std::string& url, const std::string& tenant) +{ + auto sysobj = store->svc()->sysobj; + auto& pool = store->svc()->zone->get_zone_params().oidc_pool; + std::string oid = tenant + get_url_oid_prefix() + url; + bufferlist bl; + + int ret = rgw_get_system_obj(sysobj, pool, oid, bl, nullptr, nullptr, null_yield, dpp); + if (ret < 0) { + return ret; + } + + try { + using ceph::decode; + auto iter = bl.cbegin(); + decode(*this, iter); + } catch (buffer::error& err) { + ldpp_dout(dpp, 0) << "ERROR: failed to decode oidc provider info from pool: " << pool.name << + ": " << url << dendl; + return -EIO; + } + + return 0; +} + +int RadosOIDCProvider::delete_obj(const DoutPrefixProvider *dpp, optional_yield y) +{ + auto& pool = store->svc()->zone->get_zone_params().oidc_pool; + + std::string url, tenant; + auto ret = get_tenant_url_from_arn(tenant, url); + if (ret < 0) { + ldpp_dout(dpp, 0) << "ERROR: failed to parse arn" << dendl; + return -EINVAL; + } + + if (this->tenant != tenant) { + ldpp_dout(dpp, 0) << "ERROR: tenant in arn doesn't match that of user " << this->tenant << ", " + << tenant << ": " << dendl; + return -EINVAL; + } + + // Delete url + std::string oid = tenant + get_url_oid_prefix() + url; + ret = rgw_delete_system_obj(dpp, store->svc()->sysobj, pool, oid, nullptr, y); + if (ret < 0) { + ldpp_dout(dpp, 0) << "ERROR: deleting oidc url from pool: " << pool.name << ": " + << provider_url << ": " << cpp_strerror(-ret) << dendl; + } + + return ret; +} + +int RadosRole::store_info(const DoutPrefixProvider *dpp, bool exclusive, optional_yield y) +{ + using ceph::encode; + std::string oid; + + oid = info.id; + + bufferlist bl; + encode(this->info, bl); + + if (!this->info.tags.empty()) { + bufferlist bl_tags; + encode(this->info.tags, bl_tags); + map<string, bufferlist> attrs; + attrs.emplace("tagging", bl_tags); + + RGWSI_MBSObj_PutParams params(bl, &attrs, info.mtime, exclusive); + std::unique_ptr<RGWSI_MetaBackend::Context> ctx(store->svc()->role->svc.meta_be->alloc_ctx()); + ctx->init(store->svc()->role->get_be_handler()); + return store->svc()->role->svc.meta_be->put(ctx.get(), oid, params, &info.objv_tracker, y, dpp); + } else { + RGWSI_MBSObj_PutParams params(bl, nullptr, info.mtime, exclusive); + std::unique_ptr<RGWSI_MetaBackend::Context> ctx(store->svc()->role->svc.meta_be->alloc_ctx()); + ctx->init(store->svc()->role->get_be_handler()); + return store->svc()->role->svc.meta_be->put(ctx.get(), oid, params, &info.objv_tracker, y, dpp); + } +} + +int RadosRole::store_name(const DoutPrefixProvider *dpp, bool exclusive, optional_yield y) +{ + auto sysobj = store->svc()->sysobj; + RGWNameToId nameToId; + nameToId.obj_id = info.id; + + std::string oid = info.tenant + get_names_oid_prefix() + info.name; + + bufferlist bl; + using ceph::encode; + encode(nameToId, bl); + + return rgw_put_system_obj(dpp, sysobj, store->svc()->zone->get_zone_params().roles_pool, oid, bl, exclusive, &info.objv_tracker, real_time(), y); +} + +int RadosRole::store_path(const DoutPrefixProvider *dpp, bool exclusive, optional_yield y) +{ + auto sysobj = store->svc()->sysobj; + std::string oid = info.tenant + get_path_oid_prefix() + info.path + get_info_oid_prefix() + info.id; + + bufferlist bl; + + return rgw_put_system_obj(dpp, sysobj, store->svc()->zone->get_zone_params().roles_pool, oid, bl, exclusive, &info.objv_tracker, real_time(), y); +} + +int RadosRole::read_id(const DoutPrefixProvider *dpp, const std::string& role_name, const std::string& tenant, std::string& role_id, optional_yield y) +{ + auto sysobj = store->svc()->sysobj; + std::string oid = info.tenant + get_names_oid_prefix() + role_name; + bufferlist bl; + + int ret = rgw_get_system_obj(sysobj, store->svc()->zone->get_zone_params().roles_pool, oid, bl, nullptr, nullptr, y, dpp); + if (ret < 0) { + return ret; + } + + RGWNameToId nameToId; + try { + auto iter = bl.cbegin(); + using ceph::decode; + decode(nameToId, iter); + } catch (buffer::error& err) { + ldpp_dout(dpp, 0) << "ERROR: failed to decode role from Role pool: " << role_name << dendl; + return -EIO; + } + role_id = nameToId.obj_id; + return 0; +} + +int RadosRole::read_name(const DoutPrefixProvider *dpp, optional_yield y) +{ + auto sysobj = store->svc()->sysobj; + std::string oid = info.tenant + get_names_oid_prefix() + info.name; + bufferlist bl; + + int ret = rgw_get_system_obj(sysobj, store->svc()->zone->get_zone_params().roles_pool, oid, bl, nullptr, nullptr, y, dpp); + if (ret < 0) { + ldpp_dout(dpp, 0) << "ERROR: failed reading role name from Role pool: " << info.name << + ": " << cpp_strerror(-ret) << dendl; + return ret; + } + + RGWNameToId nameToId; + try { + using ceph::decode; + auto iter = bl.cbegin(); + decode(nameToId, iter); + } catch (buffer::error& err) { + ldpp_dout(dpp, 0) << "ERROR: failed to decode role name from Role pool: " << info.name << dendl; + return -EIO; + } + info.id = nameToId.obj_id; + return 0; +} + +int RadosRole::read_info(const DoutPrefixProvider *dpp, optional_yield y) +{ + std::string oid; + + oid = info.id; + ldpp_dout(dpp, 20) << "INFO: oid in read_info is: " << oid << dendl; + + bufferlist bl; + + RGWSI_MBSObj_GetParams params(&bl, &info.attrs, &info.mtime); + std::unique_ptr<RGWSI_MetaBackend::Context> ctx(store->svc()->role->svc.meta_be->alloc_ctx()); + ctx->init(store->svc()->role->get_be_handler()); + int ret = store->svc()->role->svc.meta_be->get(ctx.get(), oid, params, &info.objv_tracker, y, dpp, true); + if (ret < 0) { + ldpp_dout(dpp, 0) << "ERROR: failed reading role info from Role pool: " << info.id << ": " << cpp_strerror(-ret) << dendl; + return ret; + } + + try { + using ceph::decode; + auto iter = bl.cbegin(); + decode(this->info, iter); + } catch (buffer::error& err) { + ldpp_dout(dpp, 0) << "ERROR: failed to decode role info from Role pool: " << info.id << dendl; + return -EIO; + } + + auto it = info.attrs.find("tagging"); + if (it != info.attrs.end()) { + bufferlist bl_tags = it->second; + try { + using ceph::decode; + auto iter = bl_tags.cbegin(); + decode(info.tags, iter); + } catch (buffer::error& err) { + ldpp_dout(dpp, 0) << "ERROR: failed to decode attrs" << info.id << dendl; + return -EIO; + } + } + + return 0; +} + +int RadosRole::create(const DoutPrefixProvider *dpp, bool exclusive, const std::string& role_id, optional_yield y) +{ + int ret; + + if (! validate_input(dpp)) { + return -EINVAL; + } + + if (!role_id.empty()) { + info.id = role_id; + } + + /* check to see the name is not used */ + ret = read_id(dpp, info.name, info.tenant, info.id, y); + if (exclusive && ret == 0) { + ldpp_dout(dpp, 0) << "ERROR: name " << info.name << " already in use for role id " + << info.id << dendl; + return -EEXIST; + } else if ( ret < 0 && ret != -ENOENT) { + ldpp_dout(dpp, 0) << "failed reading role id " << info.id << ": " + << cpp_strerror(-ret) << dendl; + return ret; + } + + if (info.id.empty()) { + /* create unique id */ + uuid_d new_uuid; + char uuid_str[37]; + new_uuid.generate_random(); + new_uuid.print(uuid_str); + info.id = uuid_str; + } + + //arn + info.arn = role_arn_prefix + info.tenant + ":role" + info.path + info.name; + + // Creation time + real_clock::time_point t = real_clock::now(); + + struct timeval tv; + real_clock::to_timeval(t, tv); + + char buf[30]; + struct tm result; + gmtime_r(&tv.tv_sec, &result); + strftime(buf,30,"%Y-%m-%dT%H:%M:%S", &result); + sprintf(buf + strlen(buf),".%dZ",(int)tv.tv_usec/1000); + info.creation_date.assign(buf, strlen(buf)); + + auto& pool = store->svc()->zone->get_zone_params().roles_pool; + ret = store_info(dpp, exclusive, y); + if (ret < 0) { + ldpp_dout(dpp, 0) << "ERROR: storing role info in Role pool: " + << info.id << ": " << cpp_strerror(-ret) << dendl; + return ret; + } + + ret = store_name(dpp, exclusive, y); + if (ret < 0) { + ldpp_dout(dpp, 0) << "ERROR: storing role name in Role pool: " + << info.name << ": " << cpp_strerror(-ret) << dendl; + + //Delete the role info that was stored in the previous call + std::string oid = get_info_oid_prefix() + info.id; + int info_ret = rgw_delete_system_obj(dpp, store->svc()->sysobj, pool, oid, nullptr, y); + if (info_ret < 0) { + ldpp_dout(dpp, 0) << "ERROR: cleanup of role id from Role pool: " + << info.id << ": " << cpp_strerror(-info_ret) << dendl; + } + return ret; + } + + ret = store_path(dpp, exclusive, y); + if (ret < 0) { + ldpp_dout(dpp, 0) << "ERROR: storing role path in Role pool: " + << info.path << ": " << cpp_strerror(-ret) << dendl; + //Delete the role info that was stored in the previous call + std::string oid = get_info_oid_prefix() + info.id; + int info_ret = rgw_delete_system_obj(dpp, store->svc()->sysobj, pool, oid, nullptr, y); + if (info_ret < 0) { + ldpp_dout(dpp, 0) << "ERROR: cleanup of role id from Role pool: " + << info.id << ": " << cpp_strerror(-info_ret) << dendl; + } + //Delete role name that was stored in previous call + oid = info.tenant + get_names_oid_prefix() + info.name; + int name_ret = rgw_delete_system_obj(dpp, store->svc()->sysobj, pool, oid, nullptr, y); + if (name_ret < 0) { + ldpp_dout(dpp, 0) << "ERROR: cleanup of role name from Role pool: " + << info.name << ": " << cpp_strerror(-name_ret) << dendl; + } + return ret; + } + return 0; +} + +int RadosRole::delete_obj(const DoutPrefixProvider *dpp, optional_yield y) +{ + auto& pool = store->svc()->zone->get_zone_params().roles_pool; + + int ret = read_name(dpp, y); + if (ret < 0) { + return ret; + } + + ret = read_info(dpp, y); + if (ret < 0) { + return ret; + } + + if (! info.perm_policy_map.empty()) { + return -ERR_DELETE_CONFLICT; + } + + // Delete id + std::string oid = get_info_oid_prefix() + info.id; + ret = rgw_delete_system_obj(dpp, store->svc()->sysobj, pool, oid, nullptr, y); + if (ret < 0) { + ldpp_dout(dpp, 0) << "ERROR: deleting role id from Role pool: " + << info.id << ": " << cpp_strerror(-ret) << dendl; + } + + // Delete name + oid = info.tenant + get_names_oid_prefix() + info.name; + ret = rgw_delete_system_obj(dpp, store->svc()->sysobj, pool, oid, nullptr, y); + if (ret < 0) { + ldpp_dout(dpp, 0) << "ERROR: deleting role name from Role pool: " + << info.name << ": " << cpp_strerror(-ret) << dendl; + } + + // Delete path + oid = info.tenant + get_path_oid_prefix() + info.path + get_info_oid_prefix() + info.id; + ret = rgw_delete_system_obj(dpp, store->svc()->sysobj, pool, oid, nullptr, y); + if (ret < 0) { + ldpp_dout(dpp, 0) << "ERROR: deleting role path from Role pool: " + << info.path << ": " << cpp_strerror(-ret) << dendl; + } + return ret; +} + +} // namespace rgw::sal + +extern "C" { + +void* newRadosStore(void) +{ + rgw::sal::RadosStore* store = new rgw::sal::RadosStore(); + if (store) { + RGWRados* rados = new RGWRados(); + + if (!rados) { + delete store; store = nullptr; + } else { + store->setRados(rados); + rados->set_store(store); + } + } + + return store; +} + +} |