// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- // vim: ts=8 sw=2 smarttab ft=cpp #include #include #include #include #include #include #include "common/errno.h" #include "common/ceph_json.h" #include "include/scope_guard.h" #include "rgw_datalog.h" #include "rgw_rados.h" #include "rgw_zone.h" #include "rgw_acl.h" #include "rgw_acl_s3.h" #include "rgw_tag_s3.h" #include "include/types.h" #include "rgw_bucket.h" #include "rgw_user.h" #include "rgw_string.h" #include "rgw_multi.h" #include "rgw_op.h" #include "rgw_bucket_sync.h" #include "services/svc_zone.h" #include "services/svc_sys_obj.h" #include "services/svc_bucket.h" #include "services/svc_bucket_sync.h" #include "services/svc_meta.h" #include "services/svc_meta_be_sobj.h" #include "services/svc_user.h" #include "services/svc_cls.h" #include "services/svc_bilog_rados.h" #include "include/rados/librados.hpp" // until everything is moved from rgw_common #include "rgw_common.h" #include "rgw_reshard.h" #include "rgw_lc.h" #include "rgw_bucket_layout.h" // stolen from src/cls/version/cls_version.cc #define VERSION_ATTR "ceph.objclass.version" #include "cls/user/cls_user_types.h" #include "rgw_sal.h" #include "rgw_sal_rados.h" #define dout_context g_ceph_context #define dout_subsys ceph_subsys_rgw #define BUCKET_TAG_TIMEOUT 30 // 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; /* * The tenant_name is always returned on purpose. May be empty, of course. */ static void parse_bucket(const string& bucket, string *tenant_name, string *bucket_name, string *bucket_instance = nullptr /* optional */) { /* * expected format: [tenant/]bucket:bucket_instance */ int pos = bucket.find('/'); if (pos >= 0) { *tenant_name = bucket.substr(0, pos); } else { tenant_name->clear(); } string bn = bucket.substr(pos + 1); pos = bn.find (':'); if (pos < 0) { *bucket_name = std::move(bn); return; } *bucket_name = bn.substr(0, pos); if (bucket_instance) { *bucket_instance = bn.substr(pos + 1); } /* * deal with the possible tenant:bucket:bucket_instance case */ if (tenant_name->empty()) { pos = bucket_instance->find(':'); if (pos >= 0) { *tenant_name = *bucket_name; *bucket_name = bucket_instance->substr(0, pos); *bucket_instance = bucket_instance->substr(pos + 1); } } } /* * Note that this is not a reversal of parse_bucket(). That one deals * with the syntax we need in metadata and such. This one deals with * the representation in RADOS pools. We chose '/' because it's not * acceptable in bucket names and thus qualified buckets cannot conflict * with the legacy or S3 buckets. */ std::string rgw_make_bucket_entry_name(const std::string& tenant_name, const std::string& bucket_name) { std::string bucket_entry; if (bucket_name.empty()) { bucket_entry.clear(); } else if (tenant_name.empty()) { bucket_entry = bucket_name; } else { bucket_entry = tenant_name + "/" + bucket_name; } return bucket_entry; } /* * Tenants are separated from buckets in URLs by a colon in S3. * This function is not to be used on Swift URLs, not even for COPY arguments. */ void rgw_parse_url_bucket(const string &bucket, const string& auth_tenant, string &tenant_name, string &bucket_name) { int pos = bucket.find(':'); if (pos >= 0) { /* * N.B.: We allow ":bucket" syntax with explicit empty tenant in order * to refer to the legacy tenant, in case users in new named tenants * want to access old global buckets. */ tenant_name = bucket.substr(0, pos); bucket_name = bucket.substr(pos + 1); } else { tenant_name = auth_tenant; bucket_name = bucket; } } /** * Get all the buckets owned by a user and fill up an RGWUserBuckets with them. * Returns: 0 on success, -ERR# on failure. */ int rgw_read_user_buckets(const DoutPrefixProvider *dpp, rgw::sal::RGWRadosStore * store, const rgw_user& user_id, rgw::sal::RGWBucketList& buckets, const string& marker, const string& end_marker, uint64_t max, bool need_stats, optional_yield y) { rgw::sal::RGWRadosUser user(store, user_id); return user.list_buckets(dpp, marker, end_marker, max, need_stats, buckets, y); } int rgw_bucket_parse_bucket_instance(const string& bucket_instance, string *bucket_name, string *bucket_id, int *shard_id) { auto pos = bucket_instance.rfind(':'); if (pos == string::npos) { return -EINVAL; } string first = bucket_instance.substr(0, pos); string second = bucket_instance.substr(pos + 1); pos = first.find(':'); if (pos == string::npos) { *shard_id = -1; *bucket_name = first; *bucket_id = second; return 0; } *bucket_name = first.substr(0, pos); *bucket_id = first.substr(pos + 1); string err; *shard_id = strict_strtol(second.c_str(), 10, &err); if (!err.empty()) { return -EINVAL; } return 0; } // parse key in format: [tenant/]name:instance[:shard_id] int rgw_bucket_parse_bucket_key(CephContext *cct, const string& key, rgw_bucket *bucket, int *shard_id) { std::string_view name{key}; std::string_view instance; // split tenant/name auto pos = name.find('/'); if (pos != string::npos) { auto tenant = name.substr(0, pos); bucket->tenant.assign(tenant.begin(), tenant.end()); name = name.substr(pos + 1); } else { bucket->tenant.clear(); } // split name:instance pos = name.find(':'); if (pos != string::npos) { instance = name.substr(pos + 1); name = name.substr(0, pos); } bucket->name.assign(name.begin(), name.end()); // split instance:shard pos = instance.find(':'); if (pos == string::npos) { bucket->bucket_id.assign(instance.begin(), instance.end()); if (shard_id) { *shard_id = -1; } return 0; } // parse shard id auto shard = instance.substr(pos + 1); string err; auto id = strict_strtol(shard.data(), 10, &err); if (!err.empty()) { if (cct) { ldout(cct, 0) << "ERROR: failed to parse bucket shard '" << instance.data() << "': " << err << dendl; } return -EINVAL; } if (shard_id) { *shard_id = id; } instance = instance.substr(0, pos); bucket->bucket_id.assign(instance.begin(), instance.end()); return 0; } static void dump_mulipart_index_results(list& objs_to_unlink, Formatter *f) { for (const auto& o : objs_to_unlink) { f->dump_string("object", o.name); } } void check_bad_user_bucket_mapping(rgw::sal::RGWRadosStore *store, const rgw_user& user_id, bool fix, optional_yield y, const DoutPrefixProvider *dpp) { rgw::sal::RGWBucketList user_buckets; rgw::sal::RGWRadosUser user(store, user_id); string marker; CephContext *cct = store->ctx(); size_t max_entries = cct->_conf->rgw_list_buckets_max_chunk; do { int ret = user.list_buckets(dpp, marker, string(), max_entries, false, user_buckets, y); if (ret < 0) { ldout(store->ctx(), 0) << "failed to read user buckets: " << cpp_strerror(-ret) << dendl; return; } map>& buckets = user_buckets.get_buckets(); for (auto i = buckets.begin(); i != buckets.end(); ++i) { marker = i->first; auto& bucket = i->second; RGWBucketInfo bucket_info; real_time mtime; int r = store->getRados()->get_bucket_info(store->svc(), user_id.tenant, bucket->get_name(), bucket_info, &mtime, null_yield, dpp); if (r < 0) { ldout(store->ctx(), 0) << "could not get bucket info for bucket=" << bucket << dendl; continue; } rgw_bucket& actual_bucket = bucket_info.bucket; if (actual_bucket.name.compare(bucket->get_name()) != 0 || actual_bucket.tenant.compare(bucket->get_tenant()) != 0 || actual_bucket.marker.compare(bucket->get_marker()) != 0 || actual_bucket.bucket_id.compare(bucket->get_bucket_id()) != 0) { cout << "bucket info mismatch: expected " << actual_bucket << " got " << bucket << std::endl; if (fix) { cout << "fixing" << std::endl; r = store->ctl()->bucket->link_bucket(user_id, actual_bucket, bucket_info.creation_time, null_yield, dpp); if (r < 0) { cerr << "failed to fix bucket: " << cpp_strerror(-r) << std::endl; } } } } } while (user_buckets.is_truncated()); } // note: function type conforms to RGWRados::check_filter_t bool rgw_bucket_object_check_filter(const string& oid) { rgw_obj_key key; string ns; return rgw_obj_key::oid_to_key_in_ns(oid, &key, ns); } int rgw_remove_object(const DoutPrefixProvider *dpp, rgw::sal::RGWRadosStore *store, const RGWBucketInfo& bucket_info, const rgw_bucket& bucket, rgw_obj_key& key) { RGWObjectCtx rctx(store); if (key.instance.empty()) { key.instance = "null"; } rgw_obj obj(bucket, key); return store->getRados()->delete_obj(dpp, rctx, bucket_info, obj, bucket_info.versioning_status()); } static int aio_wait(librados::AioCompletion *handle) { librados::AioCompletion *c = (librados::AioCompletion *)handle; c->wait_for_complete(); int ret = c->get_return_value(); c->release(); return ret; } static int drain_handles(list& pending) { int ret = 0; while (!pending.empty()) { librados::AioCompletion *handle = pending.front(); pending.pop_front(); int r = aio_wait(handle); if (r < 0) { ret = r; } } return ret; } int rgw_remove_bucket_bypass_gc(rgw::sal::RGWRadosStore *store, rgw_bucket& bucket, int concurrent_max, bool keep_index_consistent, optional_yield y, const DoutPrefixProvider *dpp) { int ret; map stats; std::vector objs; map common_prefixes; RGWBucketInfo info; RGWObjectCtx obj_ctx(store); CephContext *cct = store->ctx(); string bucket_ver, master_ver; ret = store->getRados()->get_bucket_info(store->svc(), bucket.tenant, bucket.name, info, NULL, null_yield, dpp); if (ret < 0) return ret; ret = store->getRados()->get_bucket_stats(dpp, info, RGW_NO_SHARD, &bucket_ver, &master_ver, stats, NULL); if (ret < 0) return ret; ret = abort_bucket_multiparts(dpp, store, cct, info); if (ret < 0) { return ret; } RGWRados::Bucket target(store->getRados(), info); RGWRados::Bucket::List list_op(&target); list_op.params.list_versions = true; list_op.params.allow_unordered = true; std::list handles; int max_aio = concurrent_max; bool is_truncated = true; while (is_truncated) { objs.clear(); ret = list_op.list_objects(dpp, listing_max_entries, &objs, &common_prefixes, &is_truncated, null_yield); if (ret < 0) return ret; std::vector::iterator it = objs.begin(); for (; it != objs.end(); ++it) { RGWObjState *astate = NULL; rgw_obj obj(bucket, (*it).key); ret = store->getRados()->get_obj_state(dpp, &obj_ctx, info, obj, &astate, false, y); if (ret == -ENOENT) { ldpp_dout(dpp, 1) << "WARNING: cannot find obj state for obj " << obj.get_oid() << dendl; continue; } if (ret < 0) { ldpp_dout(dpp, -1) << "ERROR: get obj state returned with error " << ret << dendl; return ret; } if (astate->manifest) { RGWObjManifest& manifest = *astate->manifest; RGWObjManifest::obj_iterator miter = manifest.obj_begin(dpp); rgw_obj head_obj = manifest.get_obj(); rgw_raw_obj raw_head_obj; store->getRados()->obj_to_raw(info.placement_rule, head_obj, &raw_head_obj); for (; miter != manifest.obj_end(dpp) && max_aio--; ++miter) { if (!max_aio) { ret = drain_handles(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); 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, info, astate, handles, keep_index_consistent, null_yield); if (ret < 0) { ldpp_dout(dpp, -1) << "ERROR: delete obj aio failed with " << ret << dendl; return ret; } } if (!max_aio) { ret = drain_handles(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 } ret = drain_handles(handles); if (ret < 0) { ldpp_dout(dpp, -1) << "ERROR: could not drain handles as aio completion returned with " << ret << dendl; return ret; } ret = store->ctl()->bucket->sync_user_stats(dpp, info.owner, info, 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 = store->getRados()->delete_bucket(info, objv_tracker, y, dpp, false); if (ret < 0) { ldpp_dout(dpp, -1) << "ERROR: could not remove bucket " << bucket.name << dendl; return ret; } ret = store->ctl()->bucket->unlink_bucket(info.owner, bucket, null_yield, dpp, false); if (ret < 0) { ldpp_dout(dpp, -1) << "ERROR: unable to remove user bucket information" << dendl; } return ret; } static void set_err_msg(std::string *sink, std::string msg) { if (sink && !msg.empty()) *sink = msg; } int RGWBucket::init(rgw::sal::RGWRadosStore *storage, RGWBucketAdminOpState& op_state, optional_yield y, const DoutPrefixProvider *dpp, std::string *err_msg, map *pattrs) { if (!storage) { set_err_msg(err_msg, "no storage!"); return -EINVAL; } store = storage; rgw_user user_id = op_state.get_user_id(); bucket.tenant = user_id.tenant; bucket.name = op_state.get_bucket_name(); if (bucket.name.empty() && user_id.empty()) return -EINVAL; // split possible tenant/name auto pos = bucket.name.find('/'); if (pos != string::npos) { bucket.tenant = bucket.name.substr(0, pos); bucket.name = bucket.name.substr(pos + 1); } if (!bucket.name.empty()) { int r = store->ctl()->bucket->read_bucket_info( bucket, &bucket_info, y, dpp, RGWBucketCtl::BucketInstance::GetParams().set_attrs(pattrs), &ep_objv); if (r < 0) { set_err_msg(err_msg, "failed to fetch bucket info for bucket=" + bucket.name); return r; } op_state.set_bucket(bucket_info.bucket); } if (!user_id.empty()) { int r = store->ctl()->user->get_info_by_uid(dpp, user_id, &user_info, y); if (r < 0) { set_err_msg(err_msg, "failed to fetch user info"); return r; } op_state.display_name = user_info.display_name; } clear_failure(); return 0; } bool rgw_find_bucket_by_id(const DoutPrefixProvider *dpp, CephContext *cct, RGWMetadataManager *mgr, const string& marker, const string& bucket_id, rgw_bucket* bucket_out) { void *handle = NULL; bool truncated = false; string s; int ret = mgr->list_keys_init(dpp, "bucket.instance", marker, &handle); if (ret < 0) { cerr << "ERROR: can't get key: " << cpp_strerror(-ret) << std::endl; mgr->list_keys_complete(handle); return -ret; } do { list keys; ret = mgr->list_keys_next(handle, 1000, keys, &truncated); if (ret < 0) { cerr << "ERROR: lists_keys_next(): " << cpp_strerror(-ret) << std::endl; mgr->list_keys_complete(handle); return -ret; } for (list::iterator iter = keys.begin(); iter != keys.end(); ++iter) { s = *iter; ret = rgw_bucket_parse_bucket_key(cct, s, bucket_out, nullptr); if (ret < 0) { continue; } if (bucket_id == bucket_out->bucket_id) { mgr->list_keys_complete(handle); return true; } } } while (truncated); mgr->list_keys_complete(handle); return false; } int RGWBucket::link(RGWBucketAdminOpState& op_state, optional_yield y, const DoutPrefixProvider *dpp, map& attrs, std::string *err_msg) { if (!op_state.is_user_op()) { set_err_msg(err_msg, "empty user id"); return -EINVAL; } string bucket_id = op_state.get_bucket_id(); std::string display_name = op_state.get_user_display_name(); rgw_bucket& bucket = op_state.get_bucket(); if (!bucket_id.empty() && bucket_id != bucket.bucket_id) { set_err_msg(err_msg, "specified bucket id does not match " + bucket.bucket_id); return -EINVAL; } rgw_bucket old_bucket = bucket; rgw_user user_id = op_state.get_user_id(); bucket.tenant = user_id.tenant; if (!op_state.new_bucket_name.empty()) { auto pos = op_state.new_bucket_name.find('/'); if (pos != string::npos) { bucket.tenant = op_state.new_bucket_name.substr(0, pos); bucket.name = op_state.new_bucket_name.substr(pos + 1); } else { bucket.name = op_state.new_bucket_name; } } RGWObjVersionTracker objv_tracker; RGWObjVersionTracker old_version = bucket_info.objv_tracker; map::iterator aiter = attrs.find(RGW_ATTR_ACL); if (aiter == attrs.end()) { // should never happen; only pre-argonaut buckets lacked this. ldpp_dout(dpp, 0) << "WARNING: can't bucket link because no acl on bucket=" << old_bucket.name << dendl; set_err_msg(err_msg, "While crossing the Anavros you have displeased the goddess Hera." " You must sacrifice your ancient bucket " + bucket.bucket_id); return -EINVAL; } bufferlist& aclbl = aiter->second; RGWAccessControlPolicy policy; ACLOwner owner; try { auto iter = aclbl.cbegin(); decode(policy, iter); owner = policy.get_owner(); } catch (buffer::error& err) { set_err_msg(err_msg, "couldn't decode policy"); return -EIO; } auto bucket_ctl = store->ctl()->bucket; int r = bucket_ctl->unlink_bucket(owner.get_id(), old_bucket, y, dpp, false); if (r < 0) { set_err_msg(err_msg, "could not unlink policy from user " + owner.get_id().to_str()); return r; } // now update the user for the bucket... if (display_name.empty()) { ldpp_dout(dpp, 0) << "WARNING: user " << user_info.user_id << " has no display name set" << dendl; } RGWAccessControlPolicy policy_instance; policy_instance.create_default(user_info.user_id, display_name); owner = policy_instance.get_owner(); aclbl.clear(); policy_instance.encode(aclbl); auto instance_params = RGWBucketCtl::BucketInstance::PutParams().set_attrs(&attrs); bucket_info.owner = user_info.user_id; if (bucket != old_bucket) { bucket_info.bucket = bucket; bucket_info.objv_tracker.version_for_read()->ver = 0; instance_params.set_exclusive(true); } r = bucket_ctl->store_bucket_instance_info(bucket, bucket_info, y, dpp, instance_params); if (r < 0) { set_err_msg(err_msg, "ERROR: failed writing bucket instance info: " + cpp_strerror(-r)); return r; } RGWBucketEntryPoint ep; ep.bucket = bucket_info.bucket; ep.owner = user_info.user_id; ep.creation_time = bucket_info.creation_time; ep.linked = true; map ep_attrs; rgw_ep_info ep_data{ep, ep_attrs}; /* link to user */ r = store->ctl()->bucket->link_bucket(user_info.user_id, bucket_info.bucket, ep.creation_time, y, dpp, true, &ep_data); if (r < 0) { set_err_msg(err_msg, "failed to relink bucket"); return r; } if (bucket != old_bucket) { // like RGWRados::delete_bucket -- excepting no bucket_index work. r = bucket_ctl->remove_bucket_entrypoint_info(old_bucket, y, dpp, RGWBucketCtl::Bucket::RemoveParams() .set_objv_tracker(&ep_data.ep_objv)); if (r < 0) { set_err_msg(err_msg, "failed to unlink old bucket endpoint " + old_bucket.tenant + "/" + old_bucket.name); return r; } r = bucket_ctl->remove_bucket_instance_info(old_bucket, bucket_info, y, dpp, RGWBucketCtl::BucketInstance::RemoveParams() .set_objv_tracker(&old_version)); if (r < 0) { set_err_msg(err_msg, "failed to unlink old bucket info"); return r; } } return 0; } int RGWBucket::chown(RGWBucketAdminOpState& op_state, const string& marker, optional_yield y, const DoutPrefixProvider *dpp, std::string *err_msg) { int ret = store->ctl()->bucket->chown(store, bucket_info, user_info.user_id, user_info.display_name, marker, y, dpp); if (ret < 0) { set_err_msg(err_msg, "Failed to change object ownership: " + cpp_strerror(-ret)); } return ret; } int RGWBucket::unlink(RGWBucketAdminOpState& op_state, optional_yield y, const DoutPrefixProvider *dpp, std::string *err_msg) { rgw_bucket bucket = op_state.get_bucket(); if (!op_state.is_user_op()) { set_err_msg(err_msg, "could not fetch user or user bucket info"); return -EINVAL; } int r = store->ctl()->bucket->unlink_bucket(user_info.user_id, bucket, y, dpp); if (r < 0) { set_err_msg(err_msg, "error unlinking bucket" + cpp_strerror(-r)); } return r; } int RGWBucket::set_quota(RGWBucketAdminOpState& op_state, const DoutPrefixProvider *dpp, std::string *err_msg) { rgw_bucket bucket = op_state.get_bucket(); RGWBucketInfo bucket_info; map attrs; int r = store->getRados()->get_bucket_info(store->svc(), bucket.tenant, bucket.name, bucket_info, NULL, null_yield, dpp, &attrs); if (r < 0) { set_err_msg(err_msg, "could not get bucket info for bucket=" + bucket.name + ": " + cpp_strerror(-r)); return r; } bucket_info.quota = op_state.quota; r = store->getRados()->put_bucket_instance_info(bucket_info, false, real_time(), &attrs, dpp); if (r < 0) { set_err_msg(err_msg, "ERROR: failed writing bucket instance info: " + cpp_strerror(-r)); return r; } return r; } int RGWBucket::remove_object(const DoutPrefixProvider *dpp, RGWBucketAdminOpState& op_state, std::string *err_msg) { rgw_bucket bucket = op_state.get_bucket(); std::string object_name = op_state.get_object_name(); rgw_obj_key key(object_name); int ret = rgw_remove_object(dpp, store, bucket_info, bucket, key); if (ret < 0) { set_err_msg(err_msg, "unable to remove object" + cpp_strerror(-ret)); return ret; } return 0; } static void dump_bucket_index(const RGWRados::ent_map_t& result, Formatter *f) { for (auto iter = result.begin(); iter != result.end(); ++iter) { f->dump_string("object", iter->first); } } static void dump_bucket_usage(map& stats, Formatter *formatter) { map::iterator iter; formatter->open_object_section("usage"); for (iter = stats.begin(); iter != stats.end(); ++iter) { RGWStorageStats& s = iter->second; const char *cat_name = rgw_obj_category_name(iter->first); formatter->open_object_section(cat_name); s.dump(formatter); formatter->close_section(); } formatter->close_section(); } static void dump_index_check(map existing_stats, map calculated_stats, Formatter *formatter) { formatter->open_object_section("check_result"); formatter->open_object_section("existing_header"); dump_bucket_usage(existing_stats, formatter); formatter->close_section(); formatter->open_object_section("calculated_header"); dump_bucket_usage(calculated_stats, formatter); formatter->close_section(); formatter->close_section(); } int RGWBucket::check_bad_index_multipart(RGWBucketAdminOpState& op_state, RGWFormatterFlusher& flusher, const DoutPrefixProvider *dpp, std::string *err_msg) { bool fix_index = op_state.will_fix_index(); rgw_bucket bucket = op_state.get_bucket(); map common_prefixes; bool is_truncated; map meta_objs; map all_objs; RGWBucketInfo bucket_info; auto obj_ctx = store->svc()->sysobj->init_obj_ctx(); int r = store->getRados()->get_bucket_instance_info(obj_ctx, bucket, bucket_info, nullptr, nullptr, null_yield, dpp); if (r < 0) { ldpp_dout(dpp, 0) << "ERROR: " << __func__ << "(): get_bucket_instance_info(bucket=" << bucket << ") returned r=" << r << dendl; return r; } RGWRados::Bucket target(store->getRados(), bucket_info); RGWRados::Bucket::List list_op(&target); list_op.params.list_versions = true; list_op.params.ns = RGW_OBJ_NS_MULTIPART; do { vector result; int r = list_op.list_objects(dpp, listing_max_entries, &result, &common_prefixes, &is_truncated, null_yield); if (r < 0) { set_err_msg(err_msg, "failed to list objects in bucket=" + bucket.name + " err=" + cpp_strerror(-r)); return r; } vector::iterator iter; for (iter = result.begin(); iter != result.end(); ++iter) { rgw_obj_index_key key = iter->key; rgw_obj obj(bucket, key); string oid = obj.get_oid(); int pos = oid.find_last_of('.'); if (pos < 0) { /* obj has no suffix */ all_objs[key] = oid; } else { /* obj has suffix */ string name = oid.substr(0, pos); string suffix = oid.substr(pos + 1); if (suffix.compare("meta") == 0) { meta_objs[name] = true; } else { all_objs[key] = name; } } } } while (is_truncated); list objs_to_unlink; Formatter *f = flusher.get_formatter(); f->open_array_section("invalid_multipart_entries"); for (auto aiter = all_objs.begin(); aiter != all_objs.end(); ++aiter) { string& name = aiter->second; if (meta_objs.find(name) == meta_objs.end()) { objs_to_unlink.push_back(aiter->first); } if (objs_to_unlink.size() > listing_max_entries) { if (fix_index) { int r = store->getRados()->remove_objs_from_index(dpp, bucket_info, objs_to_unlink); if (r < 0) { set_err_msg(err_msg, "ERROR: remove_obj_from_index() returned error: " + cpp_strerror(-r)); return r; } } dump_mulipart_index_results(objs_to_unlink, flusher.get_formatter()); flusher.flush(); objs_to_unlink.clear(); } } if (fix_index) { int r = store->getRados()->remove_objs_from_index(dpp, bucket_info, objs_to_unlink); if (r < 0) { set_err_msg(err_msg, "ERROR: remove_obj_from_index() returned error: " + cpp_strerror(-r)); return r; } } dump_mulipart_index_results(objs_to_unlink, f); f->close_section(); flusher.flush(); return 0; } int RGWBucket::check_object_index(const DoutPrefixProvider *dpp, RGWBucketAdminOpState& op_state, RGWFormatterFlusher& flusher, optional_yield y, std::string *err_msg) { bool fix_index = op_state.will_fix_index(); if (!fix_index) { set_err_msg(err_msg, "check-objects flag requires fix index enabled"); return -EINVAL; } store->getRados()->cls_obj_set_bucket_tag_timeout(dpp, bucket_info, BUCKET_TAG_TIMEOUT); string prefix; string empty_delimiter; rgw_obj_index_key marker; bool is_truncated = true; bool cls_filtered = true; Formatter *formatter = flusher.get_formatter(); formatter->open_object_section("objects"); uint16_t expansion_factor = 1; while (is_truncated) { RGWRados::ent_map_t result; result.reserve(listing_max_entries); int r = store->getRados()->cls_bucket_list_ordered( dpp, bucket_info, RGW_NO_SHARD, marker, prefix, empty_delimiter, listing_max_entries, true, expansion_factor, result, &is_truncated, &cls_filtered, &marker, y, rgw_bucket_object_check_filter); if (r == -ENOENT) { break; } else if (r < 0 && r != -ENOENT) { set_err_msg(err_msg, "ERROR: failed operation r=" + cpp_strerror(-r)); } if (result.size() < listing_max_entries / 8) { ++expansion_factor; } else if (result.size() > listing_max_entries * 7 / 8 && expansion_factor > 1) { --expansion_factor; } dump_bucket_index(result, formatter); flusher.flush(); } formatter->close_section(); store->getRados()->cls_obj_set_bucket_tag_timeout(dpp, bucket_info, 0); return 0; } int RGWBucket::check_index(const DoutPrefixProvider *dpp, RGWBucketAdminOpState& op_state, map& existing_stats, map& calculated_stats, std::string *err_msg) { bool fix_index = op_state.will_fix_index(); int r = store->getRados()->bucket_check_index(dpp, bucket_info, &existing_stats, &calculated_stats); if (r < 0) { set_err_msg(err_msg, "failed to check index error=" + cpp_strerror(-r)); return r; } if (fix_index) { r = store->getRados()->bucket_rebuild_index(dpp, bucket_info); if (r < 0) { set_err_msg(err_msg, "failed to rebuild index err=" + cpp_strerror(-r)); return r; } } return 0; } int RGWBucket::sync(RGWBucketAdminOpState& op_state, map *attrs, const DoutPrefixProvider *dpp, std::string *err_msg) { if (!store->svc()->zone->is_meta_master()) { set_err_msg(err_msg, "ERROR: failed to update bucket sync: only allowed on meta master zone"); return -EINVAL; } bool sync = op_state.will_sync_bucket(); if (sync) { bucket_info.flags &= ~BUCKET_DATASYNC_DISABLED; } else { bucket_info.flags |= BUCKET_DATASYNC_DISABLED; } int r = store->getRados()->put_bucket_instance_info(bucket_info, false, real_time(), attrs, dpp); if (r < 0) { set_err_msg(err_msg, "ERROR: failed writing bucket instance info:" + cpp_strerror(-r)); return r; } int shards_num = bucket_info.layout.current_index.layout.normal.num_shards? bucket_info.layout.current_index.layout.normal.num_shards : 1; int shard_id = bucket_info.layout.current_index.layout.normal.num_shards? 0 : -1; if (!sync) { r = store->svc()->bilog_rados->log_stop(dpp, bucket_info, -1); if (r < 0) { set_err_msg(err_msg, "ERROR: failed writing stop bilog:" + cpp_strerror(-r)); return r; } } else { r = store->svc()->bilog_rados->log_start(dpp, bucket_info, -1); if (r < 0) { set_err_msg(err_msg, "ERROR: failed writing resync bilog:" + cpp_strerror(-r)); return r; } } for (int i = 0; i < shards_num; ++i, ++shard_id) { r = store->svc()->datalog_rados->add_entry(dpp, bucket_info, shard_id); if (r < 0) { set_err_msg(err_msg, "ERROR: failed writing data log:" + cpp_strerror(-r)); return r; } } return 0; } int RGWBucket::policy_bl_to_stream(bufferlist& bl, ostream& o) { RGWAccessControlPolicy_S3 policy(g_ceph_context); int ret = decode_bl(bl, policy); if (ret < 0) { ldout(store->ctx(),0) << "failed to decode RGWAccessControlPolicy" << dendl; } policy.to_xml(o); return 0; } int rgw_object_get_attr(const DoutPrefixProvider *dpp, rgw::sal::RGWRadosStore* store, const RGWBucketInfo& bucket_info, const rgw_obj& obj, const char* attr_name, bufferlist& out_bl, optional_yield y) { RGWObjectCtx obj_ctx(store); RGWRados::Object op_target(store->getRados(), bucket_info, obj_ctx, obj); RGWRados::Object::Read rop(&op_target); return rop.get_attr(dpp, attr_name, out_bl, y); } int RGWBucket::get_policy(RGWBucketAdminOpState& op_state, RGWAccessControlPolicy& policy, optional_yield y, const DoutPrefixProvider *dpp) { std::string object_name = op_state.get_object_name(); rgw_bucket bucket = op_state.get_bucket(); RGWBucketInfo bucket_info; map attrs; int ret = store->getRados()->get_bucket_info(store->svc(), bucket.tenant, bucket.name, bucket_info, NULL, null_yield, dpp, &attrs); if (ret < 0) { return ret; } if (!object_name.empty()) { bufferlist bl; rgw_obj obj(bucket, object_name); ret = rgw_object_get_attr(dpp, store, bucket_info, obj, RGW_ATTR_ACL, bl, y); if (ret < 0){ return ret; } ret = decode_bl(bl, policy); if (ret < 0) { ldout(store->ctx(),0) << "failed to decode RGWAccessControlPolicy" << dendl; } return ret; } map::iterator aiter = attrs.find(RGW_ATTR_ACL); if (aiter == attrs.end()) { return -ENOENT; } ret = decode_bl(aiter->second, policy); if (ret < 0) { ldout(store->ctx(),0) << "failed to decode RGWAccessControlPolicy" << dendl; } return ret; } int RGWBucketAdminOp::get_policy(rgw::sal::RGWRadosStore *store, RGWBucketAdminOpState& op_state, RGWAccessControlPolicy& policy, const DoutPrefixProvider *dpp) { RGWBucket bucket; int ret = bucket.init(store, op_state, null_yield, dpp); if (ret < 0) return ret; ret = bucket.get_policy(op_state, policy, null_yield, dpp); if (ret < 0) return ret; return 0; } /* Wrappers to facilitate RESTful interface */ int RGWBucketAdminOp::get_policy(rgw::sal::RGWRadosStore *store, RGWBucketAdminOpState& op_state, RGWFormatterFlusher& flusher, const DoutPrefixProvider *dpp) { RGWAccessControlPolicy policy(store->ctx()); int ret = get_policy(store, op_state, policy, dpp); if (ret < 0) return ret; Formatter *formatter = flusher.get_formatter(); flusher.start(0); formatter->open_object_section("policy"); policy.dump(formatter); formatter->close_section(); flusher.flush(); return 0; } int RGWBucketAdminOp::dump_s3_policy(rgw::sal::RGWRadosStore *store, RGWBucketAdminOpState& op_state, ostream& os, const DoutPrefixProvider *dpp) { RGWAccessControlPolicy_S3 policy(store->ctx()); int ret = get_policy(store, op_state, policy, dpp); if (ret < 0) return ret; policy.to_xml(os); return 0; } int RGWBucketAdminOp::unlink(rgw::sal::RGWRadosStore *store, RGWBucketAdminOpState& op_state, const DoutPrefixProvider *dpp) { RGWBucket bucket; int ret = bucket.init(store, op_state, null_yield, dpp); if (ret < 0) return ret; return bucket.unlink(op_state, null_yield, dpp); } int RGWBucketAdminOp::link(rgw::sal::RGWRadosStore *store, RGWBucketAdminOpState& op_state, const DoutPrefixProvider *dpp, string *err) { RGWBucket bucket; map attrs; int ret = bucket.init(store, op_state, null_yield, dpp, err, &attrs); if (ret < 0) return ret; return bucket.link(op_state, null_yield, dpp, attrs, err); } int RGWBucketAdminOp::chown(rgw::sal::RGWRadosStore *store, RGWBucketAdminOpState& op_state, const string& marker, const DoutPrefixProvider *dpp, string *err) { RGWBucket bucket; map attrs; int ret = bucket.init(store, op_state, null_yield, dpp, err, &attrs); if (ret < 0) return ret; ret = bucket.link(op_state, null_yield, dpp, attrs, err); if (ret < 0) return ret; return bucket.chown(op_state, marker, null_yield, dpp, err); } int RGWBucketAdminOp::check_index(rgw::sal::RGWRadosStore *store, RGWBucketAdminOpState& op_state, RGWFormatterFlusher& flusher, optional_yield y, const DoutPrefixProvider *dpp) { int ret; map existing_stats; map calculated_stats; RGWBucket bucket; ret = bucket.init(store, op_state, null_yield, dpp); if (ret < 0) return ret; Formatter *formatter = flusher.get_formatter(); flusher.start(0); ret = bucket.check_bad_index_multipart(op_state, flusher, dpp); if (ret < 0) return ret; ret = bucket.check_object_index(dpp, op_state, flusher, y); if (ret < 0) return ret; ret = bucket.check_index(dpp, op_state, existing_stats, calculated_stats); if (ret < 0) return ret; dump_index_check(existing_stats, calculated_stats, formatter); flusher.flush(); return 0; } int RGWBucketAdminOp::remove_bucket(rgw::sal::RGWRadosStore *store, RGWBucketAdminOpState& op_state, optional_yield y, const DoutPrefixProvider *dpp, bool bypass_gc, bool keep_index_consistent) { std::unique_ptr bucket; std::unique_ptr user = store->get_user(op_state.get_user_id()); int ret = store->get_bucket(dpp, user.get(), user->get_tenant(), op_state.get_bucket_name(), &bucket, y); if (ret < 0) return ret; if (bypass_gc) ret = rgw_remove_bucket_bypass_gc(store, bucket->get_key(), op_state.get_max_aio(), keep_index_consistent, y, dpp); else ret = bucket->remove_bucket(dpp, op_state.will_delete_children(), false, nullptr, y); return ret; } int RGWBucketAdminOp::remove_object(rgw::sal::RGWRadosStore *store, RGWBucketAdminOpState& op_state, const DoutPrefixProvider *dpp) { RGWBucket bucket; int ret = bucket.init(store, op_state, null_yield, dpp); if (ret < 0) return ret; return bucket.remove_object(dpp, op_state); } int RGWBucketAdminOp::sync_bucket(rgw::sal::RGWRadosStore *store, RGWBucketAdminOpState& op_state, const DoutPrefixProvider *dpp, string *err_msg) { RGWBucket bucket; map attrs; int ret = bucket.init(store, op_state, null_yield, dpp, err_msg, &attrs); if (ret < 0) { return ret; } return bucket.sync(op_state, &attrs, dpp, err_msg); } static int bucket_stats(rgw::sal::RGWRadosStore *store, const std::string& tenant_name, const std::string& bucket_name, Formatter *formatter, const DoutPrefixProvider *dpp) { RGWBucketInfo bucket_info; map stats; map attrs; real_time mtime; int r = store->getRados()->get_bucket_info(store->svc(), tenant_name, bucket_name, bucket_info, &mtime, null_yield, dpp, &attrs); if (r < 0) { return r; } rgw_bucket& bucket = bucket_info.bucket; string bucket_ver, master_ver; string max_marker; int ret = store->getRados()->get_bucket_stats(dpp, bucket_info, RGW_NO_SHARD, &bucket_ver, &master_ver, stats, &max_marker); if (ret < 0) { cerr << "error getting bucket stats bucket=" << bucket.name << " ret=" << ret << std::endl; return ret; } utime_t ut(mtime); utime_t ctime_ut(bucket_info.creation_time); formatter->open_object_section("stats"); formatter->dump_string("bucket", bucket.name); formatter->dump_int("num_shards", bucket_info.layout.current_index.layout.normal.num_shards); formatter->dump_string("tenant", bucket.tenant); formatter->dump_string("zonegroup", bucket_info.zonegroup); formatter->dump_string("placement_rule", bucket_info.placement_rule.to_str()); ::encode_json("explicit_placement", bucket.explicit_placement, formatter); formatter->dump_string("id", bucket.bucket_id); formatter->dump_string("marker", bucket.marker); formatter->dump_stream("index_type") << bucket_info.layout.current_index.layout.type; ::encode_json("owner", bucket_info.owner, formatter); formatter->dump_string("ver", bucket_ver); formatter->dump_string("master_ver", master_ver); ut.gmtime(formatter->dump_stream("mtime")); ctime_ut.gmtime(formatter->dump_stream("creation_time")); formatter->dump_string("max_marker", max_marker); dump_bucket_usage(stats, formatter); encode_json("bucket_quota", bucket_info.quota, formatter); // bucket tags auto iter = attrs.find(RGW_ATTR_TAGS); if (iter != attrs.end()) { RGWObjTagSet_S3 tagset; bufferlist::const_iterator piter{&iter->second}; try { tagset.decode(piter); tagset.dump(formatter); } catch (buffer::error& err) { cerr << "ERROR: caught buffer:error, couldn't decode TagSet" << std::endl; } } // TODO: bucket CORS // TODO: bucket LC formatter->close_section(); return 0; } int RGWBucketAdminOp::limit_check(rgw::sal::RGWRadosStore *store, RGWBucketAdminOpState& op_state, const std::list& user_ids, RGWFormatterFlusher& flusher, optional_yield y, const DoutPrefixProvider *dpp, bool warnings_only) { int ret = 0; const size_t max_entries = store->ctx()->_conf->rgw_list_buckets_max_chunk; const size_t safe_max_objs_per_shard = store->ctx()->_conf->rgw_safe_max_objects_per_shard; uint16_t shard_warn_pct = store->ctx()->_conf->rgw_shard_warning_threshold; if (shard_warn_pct > 100) shard_warn_pct = 90; Formatter *formatter = flusher.get_formatter(); flusher.start(0); formatter->open_array_section("users"); for (const auto& user_id : user_ids) { formatter->open_object_section("user"); formatter->dump_string("user_id", user_id); formatter->open_array_section("buckets"); string marker; rgw::sal::RGWBucketList buckets; do { rgw::sal::RGWRadosUser user(store, rgw_user(user_id)); ret = user.list_buckets(dpp, marker, string(), max_entries, false, buckets, y); if (ret < 0) return ret; map>& m_buckets = buckets.get_buckets(); for (const auto& iter : m_buckets) { auto& bucket = iter.second; uint32_t num_shards = 1; uint64_t num_objects = 0; /* need info for num_shards */ RGWBucketInfo info; marker = bucket->get_name(); /* Casey's location for marker update, * as we may now not reach the end of * the loop body */ ret = store->getRados()->get_bucket_info(store->svc(), bucket->get_tenant(), bucket->get_name(), info, nullptr, null_yield, dpp); if (ret < 0) continue; /* need stats for num_entries */ string bucket_ver, master_ver; std::map stats; ret = store->getRados()->get_bucket_stats(dpp, info, RGW_NO_SHARD, &bucket_ver, &master_ver, stats, nullptr); if (ret < 0) continue; for (const auto& s : stats) { num_objects += s.second.num_objects; } num_shards = info.layout.current_index.layout.normal.num_shards; uint64_t objs_per_shard = (num_shards) ? num_objects/num_shards : num_objects; { bool warn; stringstream ss; uint64_t fill_pct = objs_per_shard * 100 / safe_max_objs_per_shard; if (fill_pct > 100) { ss << "OVER " << fill_pct << "%"; warn = true; } else if (fill_pct >= shard_warn_pct) { ss << "WARN " << fill_pct << "%"; warn = true; } else { ss << "OK"; warn = false; } if (warn || !warnings_only) { formatter->open_object_section("bucket"); formatter->dump_string("bucket", bucket->get_name()); formatter->dump_string("tenant", bucket->get_tenant()); formatter->dump_int("num_objects", num_objects); formatter->dump_int("num_shards", num_shards); formatter->dump_int("objects_per_shard", objs_per_shard); formatter->dump_string("fill_status", ss.str()); formatter->close_section(); } } } formatter->flush(cout); } while (buckets.is_truncated()); /* foreach: bucket */ formatter->close_section(); formatter->close_section(); formatter->flush(cout); } /* foreach: user_id */ formatter->close_section(); formatter->flush(cout); return ret; } /* RGWBucketAdminOp::limit_check */ int RGWBucketAdminOp::info(rgw::sal::RGWRadosStore *store, RGWBucketAdminOpState& op_state, RGWFormatterFlusher& flusher, optional_yield y, const DoutPrefixProvider *dpp) { RGWBucket bucket; int ret = 0; const std::string& bucket_name = op_state.get_bucket_name(); if (!bucket_name.empty()) { ret = bucket.init(store, op_state, null_yield, dpp); if (-ENOENT == ret) return -ERR_NO_SUCH_BUCKET; else if (ret < 0) return ret; } Formatter *formatter = flusher.get_formatter(); flusher.start(0); CephContext *cct = store->ctx(); const size_t max_entries = cct->_conf->rgw_list_buckets_max_chunk; const bool show_stats = op_state.will_fetch_stats(); const rgw_user& user_id = op_state.get_user_id(); if (op_state.is_user_op()) { formatter->open_array_section("buckets"); rgw::sal::RGWBucketList buckets; rgw::sal::RGWRadosUser user(store, op_state.get_user_id()); std::string marker; const std::string empty_end_marker; constexpr bool no_need_stats = false; // set need_stats to false do { ret = user.list_buckets(dpp, marker, empty_end_marker, max_entries, no_need_stats, buckets, y); if (ret < 0) { return ret; } const std::string* marker_cursor = nullptr; map>& m = buckets.get_buckets(); for (const auto& i : m) { const std::string& obj_name = i.first; if (!bucket_name.empty() && bucket_name != obj_name) { continue; } if (show_stats) { bucket_stats(store, user_id.tenant, obj_name, formatter, dpp); } else { formatter->dump_string("bucket", obj_name); } marker_cursor = &obj_name; } // for loop if (marker_cursor) { marker = *marker_cursor; } flusher.flush(); } while (buckets.is_truncated()); formatter->close_section(); } else if (!bucket_name.empty()) { ret = bucket_stats(store, user_id.tenant, bucket_name, formatter, dpp); if (ret < 0) { return ret; } } else { void *handle = nullptr; bool truncated = true; formatter->open_array_section("buckets"); ret = store->ctl()->meta.mgr->list_keys_init(dpp, "bucket", &handle); while (ret == 0 && truncated) { std::list buckets; constexpr int max_keys = 1000; ret = store->ctl()->meta.mgr->list_keys_next(handle, max_keys, buckets, &truncated); for (auto& bucket_name : buckets) { if (show_stats) { bucket_stats(store, user_id.tenant, bucket_name, formatter, dpp); } else { formatter->dump_string("bucket", bucket_name); } } } store->ctl()->meta.mgr->list_keys_complete(handle); formatter->close_section(); } flusher.flush(); return 0; } int RGWBucketAdminOp::set_quota(rgw::sal::RGWRadosStore *store, RGWBucketAdminOpState& op_state, const DoutPrefixProvider *dpp) { RGWBucket bucket; int ret = bucket.init(store, op_state, null_yield, dpp); if (ret < 0) return ret; return bucket.set_quota(op_state, dpp); } static int purge_bucket_instance(rgw::sal::RGWRadosStore *store, const RGWBucketInfo& bucket_info, const DoutPrefixProvider *dpp) { int max_shards = (bucket_info.layout.current_index.layout.normal.num_shards > 0 ? bucket_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 = (bucket_info.layout.current_index.layout.normal.num_shards > 0 ? i : -1); int ret = bs.init(bucket_info.bucket, shard_id, bucket_info.layout.current_index, nullptr, dpp); if (ret < 0) { cerr << "ERROR: bs.init(bucket=" << bucket_info.bucket << ", shard=" << shard_id << "): " << cpp_strerror(-ret) << std::endl; return ret; } ret = store->getRados()->bi_remove(bs); if (ret < 0) { cerr << "ERROR: failed to remove bucket index object: " << cpp_strerror(-ret) << std::endl; return ret; } } return 0; } inline auto split_tenant(const std::string& bucket_name){ auto p = bucket_name.find('/'); if(p != std::string::npos) { return std::make_pair(bucket_name.substr(0,p), bucket_name.substr(p+1)); } return std::make_pair(std::string(), bucket_name); } using bucket_instance_ls = std::vector; void get_stale_instances(rgw::sal::RGWRadosStore *store, const std::string& bucket_name, const vector& lst, bucket_instance_ls& stale_instances, const DoutPrefixProvider *dpp) { auto obj_ctx = store->svc()->sysobj->init_obj_ctx(); bucket_instance_ls other_instances; // first iterate over the entries, and pick up the done buckets; these // are guaranteed to be stale for (const auto& bucket_instance : lst){ RGWBucketInfo binfo; int r = store->getRados()->get_bucket_instance_info(obj_ctx, bucket_instance, binfo, nullptr,nullptr, null_yield, dpp); if (r < 0){ // this can only happen if someone deletes us right when we're processing ldpp_dout(dpp, -1) << "Bucket instance is invalid: " << bucket_instance << cpp_strerror(-r) << dendl; continue; } if (binfo.reshard_status == cls_rgw_reshard_status::DONE) stale_instances.emplace_back(std::move(binfo)); else { other_instances.emplace_back(std::move(binfo)); } } // Read the cur bucket info, if the bucket doesn't exist we can simply return // all the instances auto [tenant, bucket] = split_tenant(bucket_name); RGWBucketInfo cur_bucket_info; int r = store->getRados()->get_bucket_info(store->svc(), tenant, bucket, cur_bucket_info, nullptr, null_yield, dpp); if (r < 0) { if (r == -ENOENT) { // bucket doesn't exist, everything is stale then stale_instances.insert(std::end(stale_instances), std::make_move_iterator(other_instances.begin()), std::make_move_iterator(other_instances.end())); } else { // all bets are off if we can't read the bucket, just return the sureshot stale instances ldpp_dout(dpp, -1) << "error: reading bucket info for bucket: " << bucket << cpp_strerror(-r) << dendl; } return; } // Don't process further in this round if bucket is resharding if (cur_bucket_info.reshard_status == cls_rgw_reshard_status::IN_PROGRESS) return; other_instances.erase(std::remove_if(other_instances.begin(), other_instances.end(), [&cur_bucket_info](const RGWBucketInfo& b){ return (b.bucket.bucket_id == cur_bucket_info.bucket.bucket_id || b.bucket.bucket_id == cur_bucket_info.new_bucket_instance_id); }), other_instances.end()); // check if there are still instances left if (other_instances.empty()) { return; } // Now we have a bucket with instances where the reshard status is none, this // usually happens when the reshard process couldn't complete, lockdown the // bucket and walk through these instances to make sure no one else interferes // with these { RGWBucketReshardLock reshard_lock(store, cur_bucket_info, true); r = reshard_lock.lock(); if (r < 0) { // most likely bucket is under reshard, return the sureshot stale instances ldpp_dout(dpp, 5) << __func__ << "failed to take reshard lock; reshard underway likey" << dendl; return; } auto sg = make_scope_guard([&reshard_lock](){ reshard_lock.unlock();} ); // this should be fast enough that we may not need to renew locks and check // exit status?, should we read the values of the instances again? stale_instances.insert(std::end(stale_instances), std::make_move_iterator(other_instances.begin()), std::make_move_iterator(other_instances.end())); } return; } static int process_stale_instances(rgw::sal::RGWRadosStore *store, RGWBucketAdminOpState& op_state, RGWFormatterFlusher& flusher, const DoutPrefixProvider *dpp, std::function process_f) { std::string marker; void *handle; Formatter *formatter = flusher.get_formatter(); static constexpr auto default_max_keys = 1000; int ret = store->ctl()->meta.mgr->list_keys_init(dpp, "bucket.instance", marker, &handle); if (ret < 0) { cerr << "ERROR: can't get key: " << cpp_strerror(-ret) << std::endl; return ret; } bool truncated; formatter->open_array_section("keys"); auto g = make_scope_guard([&store, &handle, &formatter]() { store->ctl()->meta.mgr->list_keys_complete(handle); formatter->close_section(); // keys formatter->flush(cout); }); do { list keys; ret = store->ctl()->meta.mgr->list_keys_next(handle, default_max_keys, keys, &truncated); if (ret < 0 && ret != -ENOENT) { cerr << "ERROR: lists_keys_next(): " << cpp_strerror(-ret) << std::endl; return ret; } if (ret != -ENOENT) { // partition the list of buckets by buckets as the listing is un sorted, // since it would minimize the reads to bucket_info std::unordered_map> bucket_instance_map; for (auto &key: keys) { auto pos = key.find(':'); if(pos != std::string::npos) bucket_instance_map[key.substr(0,pos)].emplace_back(std::move(key)); } for (const auto& kv: bucket_instance_map) { bucket_instance_ls stale_lst; get_stale_instances(store, kv.first, kv.second, stale_lst, dpp); process_f(stale_lst, formatter, store); } } } while (truncated); return 0; } int RGWBucketAdminOp::list_stale_instances(rgw::sal::RGWRadosStore *store, RGWBucketAdminOpState& op_state, RGWFormatterFlusher& flusher, const DoutPrefixProvider *dpp) { auto process_f = [](const bucket_instance_ls& lst, Formatter *formatter, rgw::sal::RGWRadosStore*){ for (const auto& binfo: lst) formatter->dump_string("key", binfo.bucket.get_key()); }; return process_stale_instances(store, op_state, flusher, dpp, process_f); } int RGWBucketAdminOp::clear_stale_instances(rgw::sal::RGWRadosStore *store, RGWBucketAdminOpState& op_state, RGWFormatterFlusher& flusher, const DoutPrefixProvider *dpp) { auto process_f = [dpp](const bucket_instance_ls& lst, Formatter *formatter, rgw::sal::RGWRadosStore *store) { for (const auto &binfo: lst) { int ret = purge_bucket_instance(store, binfo, dpp); if (ret == 0){ auto md_key = "bucket.instance:" + binfo.bucket.get_key(); ret = store->ctl()->meta.mgr->remove(md_key, null_yield, dpp); } formatter->open_object_section("delete_status"); formatter->dump_string("bucket_instance", binfo.bucket.get_key()); formatter->dump_int("status", -ret); formatter->close_section(); } }; return process_stale_instances(store, op_state, flusher, dpp, process_f); } static int fix_single_bucket_lc(rgw::sal::RGWRadosStore *store, const std::string& tenant_name, const std::string& bucket_name, const DoutPrefixProvider *dpp) { RGWBucketInfo bucket_info; map bucket_attrs; int ret = store->getRados()->get_bucket_info(store->svc(), tenant_name, bucket_name, bucket_info, nullptr, null_yield, dpp, &bucket_attrs); if (ret < 0) { // TODO: Should we handle the case where the bucket could've been removed between // listing and fetching? return ret; } return rgw::lc::fix_lc_shard_entry(dpp, store, store->get_rgwlc()->get_lc(), bucket_info, bucket_attrs); } static void format_lc_status(Formatter* formatter, const std::string& tenant_name, const std::string& bucket_name, int status) { formatter->open_object_section("bucket_entry"); std::string entry = tenant_name.empty() ? bucket_name : tenant_name + "/" + bucket_name; formatter->dump_string("bucket", entry); formatter->dump_int("status", status); formatter->close_section(); // bucket_entry } static void process_single_lc_entry(rgw::sal::RGWRadosStore *store, Formatter *formatter, const std::string& tenant_name, const std::string& bucket_name, const DoutPrefixProvider *dpp) { int ret = fix_single_bucket_lc(store, tenant_name, bucket_name, dpp); format_lc_status(formatter, tenant_name, bucket_name, -ret); } int RGWBucketAdminOp::fix_lc_shards(rgw::sal::RGWRadosStore *store, RGWBucketAdminOpState& op_state, RGWFormatterFlusher& flusher, const DoutPrefixProvider *dpp) { std::string marker; void *handle; Formatter *formatter = flusher.get_formatter(); static constexpr auto default_max_keys = 1000; bool truncated; if (const std::string& bucket_name = op_state.get_bucket_name(); ! bucket_name.empty()) { const rgw_user user_id = op_state.get_user_id(); process_single_lc_entry(store, formatter, user_id.tenant, bucket_name, dpp); formatter->flush(cout); } else { int ret = store->ctl()->meta.mgr->list_keys_init(dpp, "bucket", marker, &handle); if (ret < 0) { std::cerr << "ERROR: can't get key: " << cpp_strerror(-ret) << std::endl; return ret; } { formatter->open_array_section("lc_fix_status"); auto sg = make_scope_guard([&store, &handle, &formatter](){ store->ctl()->meta.mgr->list_keys_complete(handle); formatter->close_section(); // lc_fix_status formatter->flush(cout); }); do { list keys; ret = store->ctl()->meta.mgr->list_keys_next(handle, default_max_keys, keys, &truncated); if (ret < 0 && ret != -ENOENT) { std::cerr << "ERROR: lists_keys_next(): " << cpp_strerror(-ret) << std::endl; return ret; } if (ret != -ENOENT) { for (const auto &key:keys) { auto [tenant_name, bucket_name] = split_tenant(key); process_single_lc_entry(store, formatter, tenant_name, bucket_name, dpp); } } formatter->flush(cout); // regularly flush every 1k entries } while (truncated); } } return 0; } static bool has_object_expired(const DoutPrefixProvider *dpp, rgw::sal::RGWRadosStore *store, const RGWBucketInfo& bucket_info, const rgw_obj_key& key, utime_t& delete_at) { rgw_obj obj(bucket_info.bucket, key); bufferlist delete_at_bl; int ret = rgw_object_get_attr(dpp, store, bucket_info, obj, RGW_ATTR_DELETE_AT, delete_at_bl, null_yield); if (ret < 0) { return false; // no delete at attr, proceed } ret = decode_bl(delete_at_bl, delete_at); if (ret < 0) { return false; // failed to parse } if (delete_at <= ceph_clock_now() && !delete_at.is_zero()) { return true; } return false; } static int fix_bucket_obj_expiry(const DoutPrefixProvider *dpp, rgw::sal::RGWRadosStore *store, const RGWBucketInfo& bucket_info, RGWFormatterFlusher& flusher, bool dry_run) { if (bucket_info.bucket.bucket_id == bucket_info.bucket.marker) { ldpp_dout(dpp, -1) << "Not a resharded bucket skipping" << dendl; return 0; // not a resharded bucket, move along } Formatter *formatter = flusher.get_formatter(); formatter->open_array_section("expired_deletion_status"); auto sg = make_scope_guard([&formatter] { formatter->close_section(); formatter->flush(std::cout); }); RGWRados::Bucket target(store->getRados(), bucket_info); RGWRados::Bucket::List list_op(&target); list_op.params.list_versions = bucket_info.versioned(); list_op.params.allow_unordered = true; bool is_truncated {false}; do { std::vector objs; int ret = list_op.list_objects(dpp, listing_max_entries, &objs, nullptr, &is_truncated, null_yield); if (ret < 0) { ldpp_dout(dpp, -1) << "ERROR failed to list objects in the bucket" << dendl; return ret; } for (const auto& obj : objs) { rgw_obj_key key(obj.key); utime_t delete_at; if (has_object_expired(dpp, store, bucket_info, key, delete_at)) { formatter->open_object_section("object_status"); formatter->dump_string("object", key.name); formatter->dump_stream("delete_at") << delete_at; if (!dry_run) { ret = rgw_remove_object(dpp, store, bucket_info, bucket_info.bucket, key); formatter->dump_int("status", ret); } formatter->close_section(); // object_status } } formatter->flush(cout); // regularly flush every 1k entries } while (is_truncated); return 0; } int RGWBucketAdminOp::fix_obj_expiry(rgw::sal::RGWRadosStore *store, RGWBucketAdminOpState& op_state, RGWFormatterFlusher& flusher, const DoutPrefixProvider *dpp, bool dry_run) { RGWBucket admin_bucket; int ret = admin_bucket.init(store, op_state, null_yield, dpp); if (ret < 0) { ldpp_dout(dpp, -1) << "failed to initialize bucket" << dendl; return ret; } return fix_bucket_obj_expiry(dpp, store, admin_bucket.get_bucket_info(), flusher, dry_run); } void RGWBucketCompleteInfo::dump(Formatter *f) const { encode_json("bucket_info", info, f); encode_json("attrs", attrs, f); } void RGWBucketCompleteInfo::decode_json(JSONObj *obj) { JSONDecoder::decode_json("bucket_info", info, obj); JSONDecoder::decode_json("attrs", attrs, obj); } class RGWBucketMetadataHandler : public RGWBucketMetadataHandlerBase { public: struct Svc { RGWSI_Bucket *bucket{nullptr}; } svc; struct Ctl { RGWBucketCtl *bucket{nullptr}; } ctl; RGWBucketMetadataHandler() {} void init(RGWSI_Bucket *bucket_svc, RGWBucketCtl *bucket_ctl) override { base_init(bucket_svc->ctx(), bucket_svc->get_ep_be_handler().get()); svc.bucket = bucket_svc; ctl.bucket = bucket_ctl; } string get_type() override { return "bucket"; } RGWMetadataObject *get_meta_obj(JSONObj *jo, const obj_version& objv, const ceph::real_time& mtime) override { RGWBucketEntryPoint be; try { decode_json_obj(be, jo); } catch (JSONDecoder::err& e) { return nullptr; } return new RGWBucketEntryMetadataObject(be, objv, mtime); } int do_get(RGWSI_MetaBackend_Handler::Op *op, string& entry, RGWMetadataObject **obj, optional_yield y, const DoutPrefixProvider *dpp) override { RGWObjVersionTracker ot; RGWBucketEntryPoint be; real_time mtime; map attrs; RGWSI_Bucket_EP_Ctx ctx(op->ctx()); int ret = svc.bucket->read_bucket_entrypoint_info(ctx, entry, &be, &ot, &mtime, &attrs, y, dpp); if (ret < 0) return ret; RGWBucketEntryMetadataObject *mdo = new RGWBucketEntryMetadataObject(be, ot.read_version, mtime, std::move(attrs)); *obj = mdo; return 0; } int do_put(RGWSI_MetaBackend_Handler::Op *op, string& entry, RGWMetadataObject *obj, RGWObjVersionTracker& objv_tracker, optional_yield y, const DoutPrefixProvider *dpp, RGWMDLogSyncType type, bool from_remote_zone) override; int do_remove(RGWSI_MetaBackend_Handler::Op *op, string& entry, RGWObjVersionTracker& objv_tracker, optional_yield y, const DoutPrefixProvider *dpp) override { RGWBucketEntryPoint be; real_time orig_mtime; RGWSI_Bucket_EP_Ctx ctx(op->ctx()); int ret = svc.bucket->read_bucket_entrypoint_info(ctx, entry, &be, &objv_tracker, &orig_mtime, nullptr, y, dpp); if (ret < 0) return ret; /* * We're unlinking the bucket but we don't want to update the entrypoint here - we're removing * it immediately and don't want to invalidate our cached objv_version or the bucket obj removal * will incorrectly fail. */ ret = ctl.bucket->unlink_bucket(be.owner, be.bucket, y, dpp, false); if (ret < 0) { ldpp_dout(dpp, -1) << "could not unlink bucket=" << entry << " owner=" << be.owner << dendl; } ret = svc.bucket->remove_bucket_entrypoint_info(ctx, entry, &objv_tracker, y, dpp); if (ret < 0) { ldpp_dout(dpp, -1) << "could not delete bucket=" << entry << dendl; } /* idempotent */ return 0; } int call(std::function f) { return call(nullopt, f); } int call(std::optional bectx_params, std::function f) { return be_handler->call(bectx_params, [&](RGWSI_MetaBackend_Handler::Op *op) { RGWSI_Bucket_EP_Ctx ctx(op->ctx()); return f(ctx); }); } }; class RGWMetadataHandlerPut_Bucket : public RGWMetadataHandlerPut_SObj { RGWBucketMetadataHandler *bhandler; RGWBucketEntryMetadataObject *obj; public: RGWMetadataHandlerPut_Bucket(RGWBucketMetadataHandler *_handler, RGWSI_MetaBackend_Handler::Op *op, string& entry, RGWMetadataObject *_obj, RGWObjVersionTracker& objv_tracker, optional_yield y, RGWMDLogSyncType type, bool from_remote_zone) : RGWMetadataHandlerPut_SObj(_handler, op, entry, obj, objv_tracker, y, type, from_remote_zone), bhandler(_handler) { obj = static_cast(_obj); } ~RGWMetadataHandlerPut_Bucket() {} void encode_obj(bufferlist *bl) override { obj->get_ep().encode(*bl); } int put_checked(const DoutPrefixProvider *dpp) override; int put_post(const DoutPrefixProvider *dpp) override; }; int RGWBucketMetadataHandler::do_put(RGWSI_MetaBackend_Handler::Op *op, string& entry, RGWMetadataObject *obj, RGWObjVersionTracker& objv_tracker, optional_yield y, const DoutPrefixProvider *dpp, RGWMDLogSyncType type, bool from_remote_zone) { RGWMetadataHandlerPut_Bucket put_op(this, op, entry, obj, objv_tracker, y, type, from_remote_zone); return do_put_operate(&put_op, dpp); } int RGWMetadataHandlerPut_Bucket::put_checked(const DoutPrefixProvider *dpp) { RGWBucketEntryMetadataObject *orig_obj = static_cast(old_obj); if (orig_obj) { obj->set_pattrs(&orig_obj->get_attrs()); } auto& be = obj->get_ep(); auto mtime = obj->get_mtime(); auto pattrs = obj->get_pattrs(); RGWSI_Bucket_EP_Ctx ctx(op->ctx()); return bhandler->svc.bucket->store_bucket_entrypoint_info(ctx, entry, be, false, mtime, pattrs, &objv_tracker, y, dpp); } int RGWMetadataHandlerPut_Bucket::put_post(const DoutPrefixProvider *dpp) { auto& be = obj->get_ep(); int ret; /* link bucket */ if (be.linked) { ret = bhandler->ctl.bucket->link_bucket(be.owner, be.bucket, be.creation_time, y, dpp, false); } else { ret = bhandler->ctl.bucket->unlink_bucket(be.owner, be.bucket, y, dpp, false); } return ret; } static void get_md5_digest(const RGWBucketEntryPoint *be, string& md5_digest) { char md5[CEPH_CRYPTO_MD5_DIGESTSIZE * 2 + 1]; unsigned char m[CEPH_CRYPTO_MD5_DIGESTSIZE]; bufferlist bl; Formatter *f = new JSONFormatter(false); be->dump(f); f->flush(bl); MD5 hash; // Allow use of MD5 digest in FIPS mode for non-cryptographic purposes hash.SetFlags(EVP_MD_CTX_FLAG_NON_FIPS_ALLOW); hash.Update((const unsigned char *)bl.c_str(), bl.length()); hash.Final(m); buf_to_hex(m, CEPH_CRYPTO_MD5_DIGESTSIZE, md5); delete f; md5_digest = md5; } #define ARCHIVE_META_ATTR RGW_ATTR_PREFIX "zone.archive.info" struct archive_meta_info { rgw_bucket orig_bucket; bool from_attrs(CephContext *cct, map& attrs) { auto iter = attrs.find(ARCHIVE_META_ATTR); if (iter == attrs.end()) { return false; } auto bliter = iter->second.cbegin(); try { decode(bliter); } catch (buffer::error& err) { ldout(cct, 0) << "ERROR: failed to decode archive meta info" << dendl; return false; } return true; } void store_in_attrs(map& attrs) const { encode(attrs[ARCHIVE_META_ATTR]); } void encode(bufferlist& bl) const { ENCODE_START(1, 1, bl); encode(orig_bucket, bl); ENCODE_FINISH(bl); } void decode(bufferlist::const_iterator& bl) { DECODE_START(1, bl); decode(orig_bucket, bl); DECODE_FINISH(bl); } }; WRITE_CLASS_ENCODER(archive_meta_info) class RGWArchiveBucketMetadataHandler : public RGWBucketMetadataHandler { public: RGWArchiveBucketMetadataHandler() {} int do_remove(RGWSI_MetaBackend_Handler::Op *op, string& entry, RGWObjVersionTracker& objv_tracker, optional_yield y, const DoutPrefixProvider *dpp) override { auto cct = svc.bucket->ctx(); RGWSI_Bucket_EP_Ctx ctx(op->ctx()); ldpp_dout(dpp, 5) << "SKIP: bucket removal is not allowed on archive zone: bucket:" << entry << " ... proceeding to rename" << dendl; string tenant_name, bucket_name; parse_bucket(entry, &tenant_name, &bucket_name); rgw_bucket entry_bucket; entry_bucket.tenant = tenant_name; entry_bucket.name = bucket_name; real_time mtime; /* read original entrypoint */ RGWBucketEntryPoint be; map attrs; int ret = svc.bucket->read_bucket_entrypoint_info(ctx, entry, &be, &objv_tracker, &mtime, &attrs, y, dpp); if (ret < 0) { return ret; } string bi_meta_name = RGWSI_Bucket::get_bi_meta_key(be.bucket); /* read original bucket instance info */ map attrs_m; ceph::real_time orig_mtime; RGWBucketInfo old_bi; ret = ctl.bucket->read_bucket_instance_info(be.bucket, &old_bi, y, dpp, RGWBucketCtl::BucketInstance::GetParams() .set_mtime(&orig_mtime) .set_attrs(&attrs_m)); if (ret < 0) { return ret; } archive_meta_info ami; if (!ami.from_attrs(svc.bucket->ctx(), attrs_m)) { ami.orig_bucket = old_bi.bucket; ami.store_in_attrs(attrs_m); } /* generate a new bucket instance. We could have avoided this if we could just point a new * bucket entry point to the old bucket instance, however, due to limitation in the way * we index buckets under the user, bucket entrypoint and bucket instance of the same * bucket need to have the same name, so we need to copy the old bucket instance into * to a new entry with the new name */ string new_bucket_name; RGWBucketInfo new_bi = old_bi; RGWBucketEntryPoint new_be = be; string md5_digest; get_md5_digest(&new_be, md5_digest); new_bucket_name = ami.orig_bucket.name + "-deleted-" + md5_digest; new_bi.bucket.name = new_bucket_name; new_bi.objv_tracker.clear(); new_be.bucket.name = new_bucket_name; ret = ctl.bucket->store_bucket_instance_info(be.bucket, new_bi, y, dpp, RGWBucketCtl::BucketInstance::PutParams() .set_exclusive(false) .set_mtime(orig_mtime) .set_attrs(&attrs_m) .set_orig_info(&old_bi)); if (ret < 0) { ldpp_dout(dpp, 0) << "ERROR: failed to put new bucket instance info for bucket=" << new_bi.bucket << " ret=" << ret << dendl; return ret; } /* store a new entrypoint */ RGWObjVersionTracker ot; ot.generate_new_write_ver(cct); ret = svc.bucket->store_bucket_entrypoint_info(ctx, RGWSI_Bucket::get_entrypoint_meta_key(new_be.bucket), new_be, true, mtime, &attrs, nullptr, y, dpp); if (ret < 0) { ldpp_dout(dpp, 0) << "ERROR: failed to put new bucket entrypoint for bucket=" << new_be.bucket << " ret=" << ret << dendl; return ret; } /* link new bucket */ ret = ctl.bucket->link_bucket(new_be.owner, new_be.bucket, new_be.creation_time, y, dpp, false); if (ret < 0) { ldpp_dout(dpp, 0) << "ERROR: failed to link new bucket for bucket=" << new_be.bucket << " ret=" << ret << dendl; return ret; } /* clean up old stuff */ ret = ctl.bucket->unlink_bucket(be.owner, entry_bucket, y, dpp, false); if (ret < 0) { ldpp_dout(dpp, -1) << "could not unlink bucket=" << entry << " owner=" << be.owner << dendl; } // if (ret == -ECANCELED) it means that there was a race here, and someone // wrote to the bucket entrypoint just before we removed it. The question is // whether it was a newly created bucket entrypoint ... in which case we // should ignore the error and move forward, or whether it is a higher version // of the same bucket instance ... in which we should retry ret = svc.bucket->remove_bucket_entrypoint_info(ctx, RGWSI_Bucket::get_entrypoint_meta_key(be.bucket), &objv_tracker, y, dpp); if (ret < 0) { ldpp_dout(dpp, 0) << "ERROR: failed to put new bucket entrypoint for bucket=" << new_be.bucket << " ret=" << ret << dendl; return ret; } ret = ctl.bucket->remove_bucket_instance_info(be.bucket, old_bi, y, dpp); if (ret < 0) { ldpp_dout(dpp, -1) << "could not delete bucket=" << entry << dendl; } /* idempotent */ return 0; } int do_put(RGWSI_MetaBackend_Handler::Op *op, string& entry, RGWMetadataObject *obj, RGWObjVersionTracker& objv_tracker, optional_yield y, const DoutPrefixProvider *dpp, RGWMDLogSyncType type, bool from_remote_zone) override { if (entry.find("-deleted-") != string::npos) { RGWObjVersionTracker ot; RGWMetadataObject *robj; int ret = do_get(op, entry, &robj, y, dpp); if (ret != -ENOENT) { if (ret < 0) { return ret; } ot.read_version = robj->get_version(); delete robj; ret = do_remove(op, entry, ot, y, dpp); if (ret < 0) { return ret; } } } return RGWBucketMetadataHandler::do_put(op, entry, obj, objv_tracker, y, dpp, type, from_remote_zone); } }; class RGWBucketInstanceMetadataHandler : public RGWBucketInstanceMetadataHandlerBase { int read_bucket_instance_entry(RGWSI_Bucket_BI_Ctx& ctx, const string& entry, RGWBucketCompleteInfo *bi, ceph::real_time *pmtime, optional_yield y, const DoutPrefixProvider *dpp) { return svc.bucket->read_bucket_instance_info(ctx, entry, &bi->info, pmtime, &bi->attrs, y, dpp); } public: struct Svc { RGWSI_Zone *zone{nullptr}; RGWSI_Bucket *bucket{nullptr}; RGWSI_BucketIndex *bi{nullptr}; } svc; RGWBucketInstanceMetadataHandler() {} void init(RGWSI_Zone *zone_svc, RGWSI_Bucket *bucket_svc, RGWSI_BucketIndex *bi_svc) override { base_init(bucket_svc->ctx(), bucket_svc->get_bi_be_handler().get()); svc.zone = zone_svc; svc.bucket = bucket_svc; svc.bi = bi_svc; } string get_type() override { return "bucket.instance"; } RGWMetadataObject *get_meta_obj(JSONObj *jo, const obj_version& objv, const ceph::real_time& mtime) override { RGWBucketCompleteInfo bci; try { decode_json_obj(bci, jo); } catch (JSONDecoder::err& e) { return nullptr; } return new RGWBucketInstanceMetadataObject(bci, objv, mtime); } int do_get(RGWSI_MetaBackend_Handler::Op *op, string& entry, RGWMetadataObject **obj, optional_yield y, const DoutPrefixProvider *dpp) override { RGWBucketCompleteInfo bci; real_time mtime; RGWSI_Bucket_BI_Ctx ctx(op->ctx()); int ret = svc.bucket->read_bucket_instance_info(ctx, entry, &bci.info, &mtime, &bci.attrs, y, dpp); if (ret < 0) return ret; RGWBucketInstanceMetadataObject *mdo = new RGWBucketInstanceMetadataObject(bci, bci.info.objv_tracker.read_version, mtime); *obj = mdo; return 0; } int do_put(RGWSI_MetaBackend_Handler::Op *op, string& entry, RGWMetadataObject *_obj, RGWObjVersionTracker& objv_tracker, optional_yield y, const DoutPrefixProvider *dpp, RGWMDLogSyncType sync_type, bool from_remote_zone) override; int do_remove(RGWSI_MetaBackend_Handler::Op *op, string& entry, RGWObjVersionTracker& objv_tracker, optional_yield y, const DoutPrefixProvider *dpp) override { RGWBucketCompleteInfo bci; RGWSI_Bucket_BI_Ctx ctx(op->ctx()); int ret = read_bucket_instance_entry(ctx, entry, &bci, nullptr, y, dpp); if (ret < 0 && ret != -ENOENT) return ret; return svc.bucket->remove_bucket_instance_info(ctx, entry, bci.info, &bci.info.objv_tracker, y, dpp); } int call(std::function f) { return call(nullopt, f); } int call(std::optional bectx_params, std::function f) { return be_handler->call(bectx_params, [&](RGWSI_MetaBackend_Handler::Op *op) { RGWSI_Bucket_BI_Ctx ctx(op->ctx()); return f(ctx); }); } }; class RGWMetadataHandlerPut_BucketInstance : public RGWMetadataHandlerPut_SObj { CephContext *cct; RGWBucketInstanceMetadataHandler *bihandler; RGWBucketInstanceMetadataObject *obj; public: RGWMetadataHandlerPut_BucketInstance(CephContext *_cct, RGWBucketInstanceMetadataHandler *_handler, RGWSI_MetaBackend_Handler::Op *_op, string& entry, RGWMetadataObject *_obj, RGWObjVersionTracker& objv_tracker, optional_yield y, RGWMDLogSyncType type, bool from_remote_zone) : RGWMetadataHandlerPut_SObj(_handler, _op, entry, obj, objv_tracker, y, type, from_remote_zone), cct(_cct), bihandler(_handler) { obj = static_cast(_obj); auto& bci = obj->get_bci(); obj->set_pattrs(&bci.attrs); } void encode_obj(bufferlist *bl) override { obj->get_bucket_info().encode(*bl); } int put_check(const DoutPrefixProvider *dpp) override; int put_checked(const DoutPrefixProvider *dpp) override; int put_post(const DoutPrefixProvider *dpp) override; }; int RGWBucketInstanceMetadataHandler::do_put(RGWSI_MetaBackend_Handler::Op *op, string& entry, RGWMetadataObject *obj, RGWObjVersionTracker& objv_tracker, optional_yield y, const DoutPrefixProvider *dpp, RGWMDLogSyncType type, bool from_remote_zone) { RGWMetadataHandlerPut_BucketInstance put_op(svc.bucket->ctx(), this, op, entry, obj, objv_tracker, y, type, from_remote_zone); return do_put_operate(&put_op, dpp); } void init_default_bucket_layout(CephContext *cct, rgw::BucketLayout& layout, const RGWZone& zone, std::optional shards, std::optional type) { layout.current_index.gen = 0; layout.current_index.layout.normal.hash_type = rgw::BucketHashType::Mod; layout.current_index.layout.type = type.value_or(rgw::BucketIndexType::Normal); if (shards) { layout.current_index.layout.normal.num_shards = *shards; } else if (cct->_conf->rgw_override_bucket_index_max_shards > 0) { layout.current_index.layout.normal.num_shards = cct->_conf->rgw_override_bucket_index_max_shards; } else { layout.current_index.layout.normal.num_shards = zone.bucket_index_max_shards; } if (layout.current_index.layout.type == rgw::BucketIndexType::Normal) { layout.logs.push_back(log_layout_from_index( layout.current_index.gen, layout.current_index.layout.normal)); } } int RGWMetadataHandlerPut_BucketInstance::put_check(const DoutPrefixProvider *dpp) { int ret; RGWBucketCompleteInfo& bci = obj->get_bci(); RGWBucketInstanceMetadataObject *orig_obj = static_cast(old_obj); RGWBucketCompleteInfo *old_bci = (orig_obj ? &orig_obj->get_bci() : nullptr); const bool exists = (!!orig_obj); if (from_remote_zone) { // don't sync bucket layout changes if (!exists) { auto& bci_index = bci.info.layout.current_index.layout; auto index_type = bci_index.type; auto num_shards = bci_index.normal.num_shards; init_default_bucket_layout(cct, bci.info.layout, bihandler->svc.zone->get_zone(), num_shards, index_type); } else { bci.info.layout = old_bci->info.layout; } } if (!exists || old_bci->info.bucket.bucket_id != bci.info.bucket.bucket_id) { /* a new bucket, we need to select a new bucket placement for it */ string tenant_name; string bucket_name; string bucket_instance; parse_bucket(entry, &tenant_name, &bucket_name, &bucket_instance); RGWZonePlacementInfo rule_info; bci.info.bucket.name = bucket_name; bci.info.bucket.bucket_id = bucket_instance; bci.info.bucket.tenant = tenant_name; // if the sync module never writes data, don't require the zone to specify all placement targets if (bihandler->svc.zone->sync_module_supports_writes()) { ret = bihandler->svc.zone->select_bucket_location_by_rule(dpp, bci.info.placement_rule, &rule_info, y); if (ret < 0) { ldpp_dout(dpp, 0) << "ERROR: select_bucket_placement() returned " << ret << dendl; return ret; } } bci.info.layout.current_index.layout.type = rule_info.index_type; } else { /* existing bucket, keep its placement */ bci.info.bucket.explicit_placement = old_bci->info.bucket.explicit_placement; bci.info.placement_rule = old_bci->info.placement_rule; } /* record the read version (if any), store the new version */ bci.info.objv_tracker.read_version = objv_tracker.read_version; bci.info.objv_tracker.write_version = objv_tracker.write_version; return 0; } int RGWMetadataHandlerPut_BucketInstance::put_checked(const DoutPrefixProvider *dpp) { RGWBucketInstanceMetadataObject *orig_obj = static_cast(old_obj); RGWBucketInfo *orig_info = (orig_obj ? &orig_obj->get_bucket_info() : nullptr); auto& info = obj->get_bucket_info(); auto mtime = obj->get_mtime(); auto pattrs = obj->get_pattrs(); RGWSI_Bucket_BI_Ctx ctx(op->ctx()); return bihandler->svc.bucket->store_bucket_instance_info(ctx, entry, info, orig_info, false, mtime, pattrs, y, dpp); } int RGWMetadataHandlerPut_BucketInstance::put_post(const DoutPrefixProvider *dpp) { RGWBucketCompleteInfo& bci = obj->get_bci(); objv_tracker = bci.info.objv_tracker; int ret = bihandler->svc.bi->init_index(dpp, bci.info); if (ret < 0) { return ret; } return STATUS_APPLIED; } class RGWArchiveBucketInstanceMetadataHandler : public RGWBucketInstanceMetadataHandler { public: RGWArchiveBucketInstanceMetadataHandler() {} int do_remove(RGWSI_MetaBackend_Handler::Op *op, string& entry, RGWObjVersionTracker& objv_tracker, optional_yield y, const DoutPrefixProvider *dpp) override { ldpp_dout(dpp, 0) << "SKIP: bucket instance removal is not allowed on archive zone: bucket.instance:" << entry << dendl; return 0; } }; RGWBucketCtl::RGWBucketCtl(RGWSI_Zone *zone_svc, RGWSI_Bucket *bucket_svc, RGWSI_Bucket_Sync *bucket_sync_svc, RGWSI_BucketIndex *bi_svc) : cct(zone_svc->ctx()) { svc.zone = zone_svc; svc.bucket = bucket_svc; svc.bucket_sync = bucket_sync_svc; svc.bi = bi_svc; } void RGWBucketCtl::init(RGWUserCtl *user_ctl, RGWBucketMetadataHandler *_bm_handler, RGWBucketInstanceMetadataHandler *_bmi_handler, RGWDataChangesLog *datalog, const DoutPrefixProvider *dpp) { ctl.user = user_ctl; bm_handler = _bm_handler; bmi_handler = _bmi_handler; bucket_be_handler = bm_handler->get_be_handler(); bi_be_handler = bmi_handler->get_be_handler(); datalog->set_bucket_filter( [this](const rgw_bucket& bucket, optional_yield y, const DoutPrefixProvider *dpp) { return bucket_exports_data(bucket, y, dpp); }); } int RGWBucketCtl::call(std::function f) { return bm_handler->call([&](RGWSI_Bucket_EP_Ctx& ep_ctx) { return bmi_handler->call([&](RGWSI_Bucket_BI_Ctx& bi_ctx) { RGWSI_Bucket_X_Ctx ctx{ep_ctx, bi_ctx}; return f(ctx); }); }); } int RGWBucketCtl::read_bucket_entrypoint_info(const rgw_bucket& bucket, RGWBucketEntryPoint *info, optional_yield y, const DoutPrefixProvider *dpp, const Bucket::GetParams& params) { return bm_handler->call(params.bectx_params, [&](RGWSI_Bucket_EP_Ctx& ctx) { return svc.bucket->read_bucket_entrypoint_info(ctx, RGWSI_Bucket::get_entrypoint_meta_key(bucket), info, params.objv_tracker, params.mtime, params.attrs, y, dpp, params.cache_info, params.refresh_version); }); } int RGWBucketCtl::store_bucket_entrypoint_info(const rgw_bucket& bucket, RGWBucketEntryPoint& info, optional_yield y, const DoutPrefixProvider *dpp, const Bucket::PutParams& params) { return bm_handler->call([&](RGWSI_Bucket_EP_Ctx& ctx) { return svc.bucket->store_bucket_entrypoint_info(ctx, RGWSI_Bucket::get_entrypoint_meta_key(bucket), info, params.exclusive, params.mtime, params.attrs, params.objv_tracker, y, dpp); }); } int RGWBucketCtl::remove_bucket_entrypoint_info(const rgw_bucket& bucket, optional_yield y, const DoutPrefixProvider *dpp, const Bucket::RemoveParams& params) { return bm_handler->call([&](RGWSI_Bucket_EP_Ctx& ctx) { return svc.bucket->remove_bucket_entrypoint_info(ctx, RGWSI_Bucket::get_entrypoint_meta_key(bucket), params.objv_tracker, y, dpp); }); } int RGWBucketCtl::read_bucket_instance_info(const rgw_bucket& bucket, RGWBucketInfo *info, optional_yield y, const DoutPrefixProvider *dpp, const BucketInstance::GetParams& params) { int ret = bmi_handler->call(params.bectx_params, [&](RGWSI_Bucket_BI_Ctx& ctx) { return svc.bucket->read_bucket_instance_info(ctx, RGWSI_Bucket::get_bi_meta_key(bucket), info, params.mtime, params.attrs, y, dpp, params.cache_info, params.refresh_version); }); if (ret < 0) { return ret; } if (params.objv_tracker) { *params.objv_tracker = info->objv_tracker; } return 0; } int RGWBucketCtl::read_bucket_info(const rgw_bucket& bucket, RGWBucketInfo *info, optional_yield y, const DoutPrefixProvider *dpp, const BucketInstance::GetParams& params, RGWObjVersionTracker *ep_objv_tracker) { const rgw_bucket *b = &bucket; std::optional ep; if (b->bucket_id.empty()) { ep.emplace(); int r = read_bucket_entrypoint_info(*b, &(*ep), y, dpp, RGWBucketCtl::Bucket::GetParams() .set_bectx_params(params.bectx_params) .set_objv_tracker(ep_objv_tracker)); if (r < 0) { return r; } b = &ep->bucket; } int ret = bmi_handler->call(params.bectx_params, [&](RGWSI_Bucket_BI_Ctx& ctx) { return svc.bucket->read_bucket_instance_info(ctx, RGWSI_Bucket::get_bi_meta_key(*b), info, params.mtime, params.attrs, y, dpp, params.cache_info, params.refresh_version); }); if (ret < 0) { return ret; } if (params.objv_tracker) { *params.objv_tracker = info->objv_tracker; } return 0; } int RGWBucketCtl::do_store_bucket_instance_info(RGWSI_Bucket_BI_Ctx& ctx, const rgw_bucket& bucket, RGWBucketInfo& info, optional_yield y, const DoutPrefixProvider *dpp, const BucketInstance::PutParams& params) { if (params.objv_tracker) { info.objv_tracker = *params.objv_tracker; } return svc.bucket->store_bucket_instance_info(ctx, RGWSI_Bucket::get_bi_meta_key(bucket), info, params.orig_info, params.exclusive, params.mtime, params.attrs, y, dpp); } int RGWBucketCtl::store_bucket_instance_info(const rgw_bucket& bucket, RGWBucketInfo& info, optional_yield y, const DoutPrefixProvider *dpp, const BucketInstance::PutParams& params) { return bmi_handler->call([&](RGWSI_Bucket_BI_Ctx& ctx) { return do_store_bucket_instance_info(ctx, bucket, info, y, dpp, params); }); } int RGWBucketCtl::remove_bucket_instance_info(const rgw_bucket& bucket, RGWBucketInfo& info, optional_yield y, const DoutPrefixProvider *dpp, const BucketInstance::RemoveParams& params) { if (params.objv_tracker) { info.objv_tracker = *params.objv_tracker; } return bmi_handler->call([&](RGWSI_Bucket_BI_Ctx& ctx) { return svc.bucket->remove_bucket_instance_info(ctx, RGWSI_Bucket::get_bi_meta_key(bucket), info, &info.objv_tracker, y, dpp); }); } int RGWBucketCtl::do_store_linked_bucket_info(RGWSI_Bucket_X_Ctx& ctx, RGWBucketInfo& info, RGWBucketInfo *orig_info, bool exclusive, real_time mtime, obj_version *pep_objv, map *pattrs, bool create_entry_point, optional_yield y, const DoutPrefixProvider *dpp) { bool create_head = !info.has_instance_obj || create_entry_point; int ret = svc.bucket->store_bucket_instance_info(ctx.bi, RGWSI_Bucket::get_bi_meta_key(info.bucket), info, orig_info, exclusive, mtime, pattrs, y, dpp); if (ret < 0) { return ret; } if (!create_head) return 0; /* done! */ RGWBucketEntryPoint entry_point; entry_point.bucket = info.bucket; entry_point.owner = info.owner; entry_point.creation_time = info.creation_time; entry_point.linked = true; RGWObjVersionTracker ot; if (pep_objv && !pep_objv->tag.empty()) { ot.write_version = *pep_objv; } else { ot.generate_new_write_ver(cct); if (pep_objv) { *pep_objv = ot.write_version; } } ret = svc.bucket->store_bucket_entrypoint_info(ctx.ep, RGWSI_Bucket::get_entrypoint_meta_key(info.bucket), entry_point, exclusive, mtime, pattrs, &ot, y, dpp); if (ret < 0) return ret; return 0; } int RGWBucketCtl::convert_old_bucket_info(RGWSI_Bucket_X_Ctx& ctx, const rgw_bucket& bucket, optional_yield y, const DoutPrefixProvider *dpp) { RGWBucketEntryPoint entry_point; real_time ep_mtime; RGWObjVersionTracker ot; map attrs; RGWBucketInfo info; auto cct = svc.bucket->ctx(); ldpp_dout(dpp, 10) << "RGWRados::convert_old_bucket_info(): bucket=" << bucket << dendl; int ret = svc.bucket->read_bucket_entrypoint_info(ctx.ep, RGWSI_Bucket::get_entrypoint_meta_key(bucket), &entry_point, &ot, &ep_mtime, &attrs, y, dpp); if (ret < 0) { ldpp_dout(dpp, 0) << "ERROR: get_bucket_entrypoint_info() returned " << ret << " bucket=" << bucket << dendl; return ret; } if (!entry_point.has_bucket_info) { /* already converted! */ return 0; } info = entry_point.old_bucket_info; ot.generate_new_write_ver(cct); ret = do_store_linked_bucket_info(ctx, info, nullptr, false, ep_mtime, &ot.write_version, &attrs, true, y, dpp); if (ret < 0) { ldpp_dout(dpp, 0) << "ERROR: failed to put_linked_bucket_info(): " << ret << dendl; return ret; } return 0; } int RGWBucketCtl::set_bucket_instance_attrs(RGWBucketInfo& bucket_info, map& attrs, RGWObjVersionTracker *objv_tracker, optional_yield y, const DoutPrefixProvider *dpp) { return call([&](RGWSI_Bucket_X_Ctx& ctx) { rgw_bucket& bucket = bucket_info.bucket; if (!bucket_info.has_instance_obj) { /* an old bucket object, need to convert it */ int ret = convert_old_bucket_info(ctx, bucket, y, dpp); if (ret < 0) { ldpp_dout(dpp, 0) << "ERROR: failed converting old bucket info: " << ret << dendl; return ret; } } return do_store_bucket_instance_info(ctx.bi, bucket, bucket_info, y, dpp, BucketInstance::PutParams().set_attrs(&attrs) .set_objv_tracker(objv_tracker) .set_orig_info(&bucket_info)); }); } int RGWBucketCtl::link_bucket(const rgw_user& user_id, const rgw_bucket& bucket, ceph::real_time creation_time, optional_yield y, const DoutPrefixProvider *dpp, bool update_entrypoint, rgw_ep_info *pinfo) { return bm_handler->call([&](RGWSI_Bucket_EP_Ctx& ctx) { return do_link_bucket(ctx, user_id, bucket, creation_time, update_entrypoint, pinfo, y, dpp); }); } int RGWBucketCtl::do_link_bucket(RGWSI_Bucket_EP_Ctx& ctx, const rgw_user& user_id, const rgw_bucket& bucket, ceph::real_time creation_time, bool update_entrypoint, rgw_ep_info *pinfo, optional_yield y, const DoutPrefixProvider *dpp) { int ret; RGWBucketEntryPoint ep; RGWObjVersionTracker ot; RGWObjVersionTracker& rot = (pinfo) ? pinfo->ep_objv : ot; map attrs, *pattrs = nullptr; string meta_key; if (update_entrypoint) { meta_key = RGWSI_Bucket::get_entrypoint_meta_key(bucket); if (pinfo) { ep = pinfo->ep; pattrs = &pinfo->attrs; } else { ret = svc.bucket->read_bucket_entrypoint_info(ctx, meta_key, &ep, &rot, nullptr, &attrs, y, dpp); if (ret < 0 && ret != -ENOENT) { ldpp_dout(dpp, 0) << "ERROR: store->get_bucket_entrypoint_info() returned: " << cpp_strerror(-ret) << dendl; } pattrs = &attrs; } } ret = ctl.user->add_bucket(dpp, user_id, bucket, creation_time, y); if (ret < 0) { ldpp_dout(dpp, 0) << "ERROR: error adding bucket to user directory:" << " user=" << user_id << " bucket=" << bucket << " err=" << cpp_strerror(-ret) << dendl; goto done_err; } if (!update_entrypoint) return 0; ep.linked = true; ep.owner = user_id; ep.bucket = bucket; ret = svc.bucket->store_bucket_entrypoint_info( ctx, meta_key, ep, false, real_time(), pattrs, &rot, y, dpp); if (ret < 0) goto done_err; return 0; done_err: int r = do_unlink_bucket(ctx, user_id, bucket, true, y, dpp); if (r < 0) { ldpp_dout(dpp, 0) << "ERROR: failed unlinking bucket on error cleanup: " << cpp_strerror(-r) << dendl; } return ret; } int RGWBucketCtl::unlink_bucket(const rgw_user& user_id, const rgw_bucket& bucket, optional_yield y, const DoutPrefixProvider *dpp, bool update_entrypoint) { return bm_handler->call([&](RGWSI_Bucket_EP_Ctx& ctx) { return do_unlink_bucket(ctx, user_id, bucket, update_entrypoint, y, dpp); }); } int RGWBucketCtl::do_unlink_bucket(RGWSI_Bucket_EP_Ctx& ctx, const rgw_user& user_id, const rgw_bucket& bucket, bool update_entrypoint, optional_yield y, const DoutPrefixProvider *dpp) { int ret = ctl.user->remove_bucket(dpp, user_id, bucket, y); if (ret < 0) { ldpp_dout(dpp, 0) << "ERROR: error removing bucket from directory: " << cpp_strerror(-ret)<< dendl; } if (!update_entrypoint) return 0; RGWBucketEntryPoint ep; RGWObjVersionTracker ot; map attrs; string meta_key = RGWSI_Bucket::get_entrypoint_meta_key(bucket); ret = svc.bucket->read_bucket_entrypoint_info(ctx, meta_key, &ep, &ot, nullptr, &attrs, y, dpp); if (ret == -ENOENT) return 0; if (ret < 0) return ret; if (!ep.linked) return 0; if (ep.owner != user_id) { ldpp_dout(dpp, 0) << "bucket entry point user mismatch, can't unlink bucket: " << ep.owner << " != " << user_id << dendl; return -EINVAL; } ep.linked = false; return svc.bucket->store_bucket_entrypoint_info(ctx, meta_key, ep, false, real_time(), &attrs, &ot, y, dpp); } int RGWBucketCtl::set_acl(ACLOwner& owner, rgw_bucket& bucket, RGWBucketInfo& bucket_info, bufferlist& bl, optional_yield y, const DoutPrefixProvider *dpp) { // set owner and acl bucket_info.owner = owner.get_id(); std::map attrs{{RGW_ATTR_ACL, bl}}; int r = store_bucket_instance_info(bucket, bucket_info, y, dpp, BucketInstance::PutParams().set_attrs(&attrs)); if (r < 0) { cerr << "ERROR: failed to set bucket owner: " << cpp_strerror(-r) << std::endl; return r; } return 0; } // TODO: remove RGWRados dependency for bucket listing int RGWBucketCtl::chown(rgw::sal::RGWRadosStore *store, RGWBucketInfo& bucket_info, const rgw_user& user_id, const std::string& display_name, const std::string& marker, optional_yield y, const DoutPrefixProvider *dpp) { std::vector objs; map common_prefixes; RGWRados::Bucket target(store->getRados(), bucket_info); RGWRados::Bucket::List list_op(&target); list_op.params.list_versions = true; list_op.params.allow_unordered = true; list_op.params.marker = marker; bool is_truncated = false; int count = 0; int max_entries = 1000; //Loop through objects and update object acls to point to bucket owner do { RGWObjectCtx obj_ctx(store); objs.clear(); int ret = list_op.list_objects(dpp, max_entries, &objs, &common_prefixes, &is_truncated, y); if (ret < 0) { ldpp_dout(dpp, 0) << "ERROR: list objects failed: " << cpp_strerror(-ret) << dendl; return ret; } list_op.params.marker = list_op.get_next_marker(); count += objs.size(); for (const auto& obj : objs) { rgw_obj r_obj(bucket_info.bucket, obj.key); RGWRados::Object op_target(store->getRados(), bucket_info, obj_ctx, r_obj); RGWRados::Object::Read read_op(&op_target); map attrs; read_op.params.attrs = &attrs; ret = read_op.prepare(y, dpp); if (ret < 0){ ldpp_dout(dpp, 0) << "ERROR: failed to read object " << obj.key.name << cpp_strerror(-ret) << dendl; continue; } const auto& aiter = attrs.find(RGW_ATTR_ACL); if (aiter == attrs.end()) { ldpp_dout(dpp, 0) << "ERROR: no acls found for object " << obj.key.name << " .Continuing with next object." << dendl; continue; } else { bufferlist& bl = aiter->second; RGWAccessControlPolicy policy(store->ctx()); ACLOwner owner; try { decode(policy, bl); 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(user_id, display_name, RGW_PERM_FULL_CONTROL); acl.add_grant(&grant); //Update the ACL owner to the new user owner.set_id(user_id); owner.set_name(display_name); policy.set_owner(owner); bl.clear(); encode(policy, bl); obj_ctx.set_atomic(r_obj); ret = store->getRados()->set_attr(dpp, &obj_ctx, bucket_info, r_obj, RGW_ATTR_ACL, bl); if (ret < 0) { ldpp_dout(dpp, 0) << "ERROR: modify attr failed " << cpp_strerror(-ret) << dendl; return ret; } } } cerr << count << " objects processed in " << bucket_info.bucket.name << ". Next marker " << list_op.params.marker.name << std::endl; } while(is_truncated); return 0; } int RGWBucketCtl::read_bucket_stats(const rgw_bucket& bucket, RGWBucketEnt *result, optional_yield y, const DoutPrefixProvider *dpp) { return call([&](RGWSI_Bucket_X_Ctx& ctx) { return svc.bucket->read_bucket_stats(ctx, bucket, result, y, dpp); }); } int RGWBucketCtl::read_buckets_stats(map& m, optional_yield y, const DoutPrefixProvider *dpp) { return call([&](RGWSI_Bucket_X_Ctx& ctx) { return svc.bucket->read_buckets_stats(ctx, m, y, dpp); }); } int RGWBucketCtl::sync_user_stats(const DoutPrefixProvider *dpp, const rgw_user& user_id, const RGWBucketInfo& bucket_info, optional_yield y, RGWBucketEnt* pent) { RGWBucketEnt ent; if (!pent) { pent = &ent; } int r = svc.bi->read_stats(dpp, bucket_info, pent, null_yield); if (r < 0) { ldpp_dout(dpp, 20) << __func__ << "(): failed to read bucket stats (r=" << r << ")" << dendl; return r; } return ctl.user->flush_bucket_stats(dpp, user_id, *pent, y); } int RGWBucketCtl::get_sync_policy_handler(std::optional zone, std::optional bucket, RGWBucketSyncPolicyHandlerRef *phandler, optional_yield y, const DoutPrefixProvider *dpp) { int r = call([&](RGWSI_Bucket_X_Ctx& ctx) { return svc.bucket_sync->get_policy_handler(ctx, zone, bucket, phandler, y, dpp); }); if (r < 0) { ldpp_dout(dpp, 20) << __func__ << "(): failed to get policy handler for bucket=" << bucket << " (r=" << r << ")" << dendl; return r; } return 0; } int RGWBucketCtl::bucket_exports_data(const rgw_bucket& bucket, optional_yield y, const DoutPrefixProvider *dpp) { RGWBucketSyncPolicyHandlerRef handler; int r = get_sync_policy_handler(std::nullopt, bucket, &handler, y, dpp); if (r < 0) { return r; } return handler->bucket_exports_data(); } int RGWBucketCtl::bucket_imports_data(const rgw_bucket& bucket, optional_yield y, const DoutPrefixProvider *dpp) { RGWBucketSyncPolicyHandlerRef handler; int r = get_sync_policy_handler(std::nullopt, bucket, &handler, y, dpp); if (r < 0) { return r; } return handler->bucket_imports_data(); } RGWBucketMetadataHandlerBase *RGWBucketMetaHandlerAllocator::alloc() { return new RGWBucketMetadataHandler(); } RGWBucketInstanceMetadataHandlerBase *RGWBucketInstanceMetaHandlerAllocator::alloc() { return new RGWBucketInstanceMetadataHandler(); } RGWBucketMetadataHandlerBase *RGWArchiveBucketMetaHandlerAllocator::alloc() { return new RGWArchiveBucketMetadataHandler(); } RGWBucketInstanceMetadataHandlerBase *RGWArchiveBucketInstanceMetaHandlerAllocator::alloc() { return new RGWArchiveBucketInstanceMetadataHandler(); }