1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab ft=cpp
10 #include <boost/format.hpp>
12 #include "common/errno.h"
13 #include "common/ceph_json.h"
14 #include "include/scope_guard.h"
16 #include "rgw_datalog.h"
17 #include "rgw_rados.h"
20 #include "rgw_acl_s3.h"
21 #include "rgw_tag_s3.h"
23 #include "include/types.h"
24 #include "rgw_bucket.h"
26 #include "rgw_string.h"
27 #include "rgw_multi.h"
29 #include "rgw_bucket_sync.h"
31 #include "services/svc_zone.h"
32 #include "services/svc_sys_obj.h"
33 #include "services/svc_bucket.h"
34 #include "services/svc_bucket_sync.h"
35 #include "services/svc_meta.h"
36 #include "services/svc_meta_be_sobj.h"
37 #include "services/svc_user.h"
38 #include "services/svc_cls.h"
39 #include "services/svc_bilog_rados.h"
41 #include "include/rados/librados.hpp"
42 // until everything is moved from rgw_common
43 #include "rgw_common.h"
44 #include "rgw_reshard.h"
46 #include "rgw_bucket_layout.h"
48 // stolen from src/cls/version/cls_version.cc
49 #define VERSION_ATTR "ceph.objclass.version"
51 #include "cls/user/cls_user_types.h"
54 #include "rgw_sal_rados.h"
56 #define dout_context g_ceph_context
57 #define dout_subsys ceph_subsys_rgw
59 #define BUCKET_TAG_TIMEOUT 30
61 // default number of entries to list with each bucket listing call
62 // (use marker to bridge between calls)
63 static constexpr size_t listing_max_entries
= 1000;
67 * The tenant_name is always returned on purpose. May be empty, of course.
69 static void parse_bucket(const string
& bucket
,
72 string
*bucket_instance
= nullptr /* optional */)
75 * expected format: [tenant/]bucket:bucket_instance
77 int pos
= bucket
.find('/');
79 *tenant_name
= bucket
.substr(0, pos
);
83 string bn
= bucket
.substr(pos
+ 1);
86 *bucket_name
= std::move(bn
);
89 *bucket_name
= bn
.substr(0, pos
);
90 if (bucket_instance
) {
91 *bucket_instance
= bn
.substr(pos
+ 1);
95 * deal with the possible tenant:bucket:bucket_instance case
97 if (tenant_name
->empty()) {
98 pos
= bucket_instance
->find(':');
100 *tenant_name
= *bucket_name
;
101 *bucket_name
= bucket_instance
->substr(0, pos
);
102 *bucket_instance
= bucket_instance
->substr(pos
+ 1);
108 * Note that this is not a reversal of parse_bucket(). That one deals
109 * with the syntax we need in metadata and such. This one deals with
110 * the representation in RADOS pools. We chose '/' because it's not
111 * acceptable in bucket names and thus qualified buckets cannot conflict
112 * with the legacy or S3 buckets.
114 std::string
rgw_make_bucket_entry_name(const std::string
& tenant_name
,
115 const std::string
& bucket_name
) {
116 std::string bucket_entry
;
118 if (bucket_name
.empty()) {
119 bucket_entry
.clear();
120 } else if (tenant_name
.empty()) {
121 bucket_entry
= bucket_name
;
123 bucket_entry
= tenant_name
+ "/" + bucket_name
;
130 * Tenants are separated from buckets in URLs by a colon in S3.
131 * This function is not to be used on Swift URLs, not even for COPY arguments.
133 void rgw_parse_url_bucket(const string
&bucket
, const string
& auth_tenant
,
134 string
&tenant_name
, string
&bucket_name
) {
136 int pos
= bucket
.find(':');
139 * N.B.: We allow ":bucket" syntax with explicit empty tenant in order
140 * to refer to the legacy tenant, in case users in new named tenants
141 * want to access old global buckets.
143 tenant_name
= bucket
.substr(0, pos
);
144 bucket_name
= bucket
.substr(pos
+ 1);
146 tenant_name
= auth_tenant
;
147 bucket_name
= bucket
;
152 * Get all the buckets owned by a user and fill up an RGWUserBuckets with them.
153 * Returns: 0 on success, -ERR# on failure.
155 int rgw_read_user_buckets(const DoutPrefixProvider
*dpp
,
156 rgw::sal::RGWRadosStore
* store
,
157 const rgw_user
& user_id
,
158 rgw::sal::RGWBucketList
& buckets
,
159 const string
& marker
,
160 const string
& end_marker
,
165 rgw::sal::RGWRadosUser
user(store
, user_id
);
166 return user
.list_buckets(dpp
, marker
, end_marker
, max
, need_stats
, buckets
, y
);
169 int rgw_bucket_parse_bucket_instance(const string
& bucket_instance
, string
*bucket_name
, string
*bucket_id
, int *shard_id
)
171 auto pos
= bucket_instance
.rfind(':');
172 if (pos
== string::npos
) {
176 string first
= bucket_instance
.substr(0, pos
);
177 string second
= bucket_instance
.substr(pos
+ 1);
179 pos
= first
.find(':');
181 if (pos
== string::npos
) {
183 *bucket_name
= first
;
188 *bucket_name
= first
.substr(0, pos
);
189 *bucket_id
= first
.substr(pos
+ 1);
192 *shard_id
= strict_strtol(second
.c_str(), 10, &err
);
200 // parse key in format: [tenant/]name:instance[:shard_id]
201 int rgw_bucket_parse_bucket_key(CephContext
*cct
, const string
& key
,
202 rgw_bucket
*bucket
, int *shard_id
)
204 std::string_view name
{key
};
205 std::string_view instance
;
208 auto pos
= name
.find('/');
209 if (pos
!= string::npos
) {
210 auto tenant
= name
.substr(0, pos
);
211 bucket
->tenant
.assign(tenant
.begin(), tenant
.end());
212 name
= name
.substr(pos
+ 1);
214 bucket
->tenant
.clear();
217 // split name:instance
218 pos
= name
.find(':');
219 if (pos
!= string::npos
) {
220 instance
= name
.substr(pos
+ 1);
221 name
= name
.substr(0, pos
);
223 bucket
->name
.assign(name
.begin(), name
.end());
225 // split instance:shard
226 pos
= instance
.find(':');
227 if (pos
== string::npos
) {
228 bucket
->bucket_id
.assign(instance
.begin(), instance
.end());
236 auto shard
= instance
.substr(pos
+ 1);
238 auto id
= strict_strtol(shard
.data(), 10, &err
);
241 ldout(cct
, 0) << "ERROR: failed to parse bucket shard '"
242 << instance
.data() << "': " << err
<< dendl
;
250 instance
= instance
.substr(0, pos
);
251 bucket
->bucket_id
.assign(instance
.begin(), instance
.end());
255 static void dump_mulipart_index_results(list
<rgw_obj_index_key
>& objs_to_unlink
,
258 for (const auto& o
: objs_to_unlink
) {
259 f
->dump_string("object", o
.name
);
263 void check_bad_user_bucket_mapping(rgw::sal::RGWRadosStore
*store
, const rgw_user
& user_id
,
266 const DoutPrefixProvider
*dpp
)
268 rgw::sal::RGWBucketList user_buckets
;
269 rgw::sal::RGWRadosUser
user(store
, user_id
);
272 CephContext
*cct
= store
->ctx();
274 size_t max_entries
= cct
->_conf
->rgw_list_buckets_max_chunk
;
277 int ret
= user
.list_buckets(dpp
, marker
, string(), max_entries
, false, user_buckets
, y
);
279 ldout(store
->ctx(), 0) << "failed to read user buckets: "
280 << cpp_strerror(-ret
) << dendl
;
284 map
<string
, std::unique_ptr
<rgw::sal::RGWBucket
>>& buckets
= user_buckets
.get_buckets();
285 for (auto i
= buckets
.begin();
290 auto& bucket
= i
->second
;
292 RGWBucketInfo bucket_info
;
294 int r
= store
->getRados()->get_bucket_info(store
->svc(), user_id
.tenant
, bucket
->get_name(), bucket_info
, &mtime
, null_yield
, dpp
);
296 ldout(store
->ctx(), 0) << "could not get bucket info for bucket=" << bucket
<< dendl
;
300 rgw_bucket
& actual_bucket
= bucket_info
.bucket
;
302 if (actual_bucket
.name
.compare(bucket
->get_name()) != 0 ||
303 actual_bucket
.tenant
.compare(bucket
->get_tenant()) != 0 ||
304 actual_bucket
.marker
.compare(bucket
->get_marker()) != 0 ||
305 actual_bucket
.bucket_id
.compare(bucket
->get_bucket_id()) != 0) {
306 cout
<< "bucket info mismatch: expected " << actual_bucket
<< " got " << bucket
<< std::endl
;
308 cout
<< "fixing" << std::endl
;
309 r
= store
->ctl()->bucket
->link_bucket(user_id
, actual_bucket
,
310 bucket_info
.creation_time
,
313 cerr
<< "failed to fix bucket: " << cpp_strerror(-r
) << std::endl
;
318 } while (user_buckets
.is_truncated());
321 // note: function type conforms to RGWRados::check_filter_t
322 bool rgw_bucket_object_check_filter(const string
& oid
)
326 return rgw_obj_key::oid_to_key_in_ns(oid
, &key
, ns
);
329 int rgw_remove_object(const DoutPrefixProvider
*dpp
, rgw::sal::RGWRadosStore
*store
, const RGWBucketInfo
& bucket_info
, const rgw_bucket
& bucket
, rgw_obj_key
& key
)
331 RGWObjectCtx
rctx(store
);
333 if (key
.instance
.empty()) {
334 key
.instance
= "null";
337 rgw_obj
obj(bucket
, key
);
339 return store
->getRados()->delete_obj(dpp
, rctx
, bucket_info
, obj
, bucket_info
.versioning_status());
342 static int aio_wait(librados::AioCompletion
*handle
)
344 librados::AioCompletion
*c
= (librados::AioCompletion
*)handle
;
345 c
->wait_for_complete();
346 int ret
= c
->get_return_value();
351 static int drain_handles(list
<librados::AioCompletion
*>& pending
)
354 while (!pending
.empty()) {
355 librados::AioCompletion
*handle
= pending
.front();
357 int r
= aio_wait(handle
);
365 int rgw_remove_bucket_bypass_gc(rgw::sal::RGWRadosStore
*store
, rgw_bucket
& bucket
,
366 int concurrent_max
, bool keep_index_consistent
,
368 const DoutPrefixProvider
*dpp
)
371 map
<RGWObjCategory
, RGWStorageStats
> stats
;
372 std::vector
<rgw_bucket_dir_entry
> objs
;
373 map
<string
, bool> common_prefixes
;
375 RGWObjectCtx
obj_ctx(store
);
376 CephContext
*cct
= store
->ctx();
378 string bucket_ver
, master_ver
;
380 ret
= store
->getRados()->get_bucket_info(store
->svc(), bucket
.tenant
, bucket
.name
, info
, NULL
, null_yield
, dpp
);
384 ret
= store
->getRados()->get_bucket_stats(dpp
, info
, RGW_NO_SHARD
, &bucket_ver
, &master_ver
, stats
, NULL
);
388 string prefix
, delimiter
;
390 ret
= abort_bucket_multiparts(dpp
, store
, cct
, info
, prefix
, delimiter
);
395 RGWRados::Bucket
target(store
->getRados(), info
);
396 RGWRados::Bucket::List
list_op(&target
);
398 list_op
.params
.list_versions
= true;
399 list_op
.params
.allow_unordered
= true;
401 std::list
<librados::AioCompletion
*> handles
;
403 int max_aio
= concurrent_max
;
404 bool is_truncated
= true;
406 while (is_truncated
) {
408 ret
= list_op
.list_objects(dpp
, listing_max_entries
, &objs
, &common_prefixes
,
409 &is_truncated
, null_yield
);
413 std::vector
<rgw_bucket_dir_entry
>::iterator it
= objs
.begin();
414 for (; it
!= objs
.end(); ++it
) {
415 RGWObjState
*astate
= NULL
;
416 rgw_obj
obj(bucket
, (*it
).key
);
418 ret
= store
->getRados()->get_obj_state(dpp
, &obj_ctx
, info
, obj
, &astate
, false, y
);
419 if (ret
== -ENOENT
) {
420 ldpp_dout(dpp
, 1) << "WARNING: cannot find obj state for obj " << obj
.get_oid() << dendl
;
424 ldpp_dout(dpp
, -1) << "ERROR: get obj state returned with error " << ret
<< dendl
;
428 if (astate
->manifest
) {
429 RGWObjManifest
& manifest
= *astate
->manifest
;
430 RGWObjManifest::obj_iterator miter
= manifest
.obj_begin(dpp
);
431 rgw_obj head_obj
= manifest
.get_obj();
432 rgw_raw_obj raw_head_obj
;
433 store
->getRados()->obj_to_raw(info
.placement_rule
, head_obj
, &raw_head_obj
);
436 for (; miter
!= manifest
.obj_end(dpp
) && max_aio
--; ++miter
) {
438 ret
= drain_handles(handles
);
440 ldpp_dout(dpp
, -1) << "ERROR: could not drain handles as aio completion returned with " << ret
<< dendl
;
443 max_aio
= concurrent_max
;
446 rgw_raw_obj last_obj
= miter
.get_location().get_raw_obj(store
);
447 if (last_obj
== raw_head_obj
) {
448 // have the head obj deleted at the end
452 ret
= store
->getRados()->delete_raw_obj_aio(dpp
, last_obj
, handles
);
454 ldpp_dout(dpp
, -1) << "ERROR: delete obj aio failed with " << ret
<< dendl
;
457 } // for all shadow objs
459 ret
= store
->getRados()->delete_obj_aio(dpp
, head_obj
, info
, astate
, handles
, keep_index_consistent
, null_yield
);
461 ldpp_dout(dpp
, -1) << "ERROR: delete obj aio failed with " << ret
<< dendl
;
467 ret
= drain_handles(handles
);
469 ldpp_dout(dpp
, -1) << "ERROR: could not drain handles as aio completion returned with " << ret
<< dendl
;
472 max_aio
= concurrent_max
;
474 obj_ctx
.invalidate(obj
);
475 } // for all RGW objects
478 ret
= drain_handles(handles
);
480 ldpp_dout(dpp
, -1) << "ERROR: could not drain handles as aio completion returned with " << ret
<< dendl
;
484 ret
= store
->ctl()->bucket
->sync_user_stats(dpp
, info
.owner
, info
, y
);
486 ldpp_dout(dpp
, 1) << "WARNING: failed sync user stats before bucket delete. ret=" << ret
<< dendl
;
489 RGWObjVersionTracker objv_tracker
;
491 // this function can only be run if caller wanted children to be
492 // deleted, so we can ignore the check for children as any that
493 // remain are detritus from a prior bug
494 ret
= store
->getRados()->delete_bucket(info
, objv_tracker
, y
, dpp
, false);
496 ldpp_dout(dpp
, -1) << "ERROR: could not remove bucket " << bucket
.name
<< dendl
;
500 ret
= store
->ctl()->bucket
->unlink_bucket(info
.owner
, bucket
, null_yield
, dpp
, false);
502 ldpp_dout(dpp
, -1) << "ERROR: unable to remove user bucket information" << dendl
;
508 static void set_err_msg(std::string
*sink
, std::string msg
)
510 if (sink
&& !msg
.empty())
514 int RGWBucket::init(rgw::sal::RGWRadosStore
*storage
, RGWBucketAdminOpState
& op_state
,
515 optional_yield y
, const DoutPrefixProvider
*dpp
, std::string
*err_msg
,
516 map
<string
, bufferlist
> *pattrs
)
519 set_err_msg(err_msg
, "no storage!");
525 rgw_user user_id
= op_state
.get_user_id();
526 bucket
.tenant
= user_id
.tenant
;
527 bucket
.name
= op_state
.get_bucket_name();
529 if (bucket
.name
.empty() && user_id
.empty())
532 // split possible tenant/name
533 auto pos
= bucket
.name
.find('/');
534 if (pos
!= string::npos
) {
535 bucket
.tenant
= bucket
.name
.substr(0, pos
);
536 bucket
.name
= bucket
.name
.substr(pos
+ 1);
539 if (!bucket
.name
.empty()) {
540 int r
= store
->ctl()->bucket
->read_bucket_info(
541 bucket
, &bucket_info
, y
, dpp
,
542 RGWBucketCtl::BucketInstance::GetParams().set_attrs(pattrs
),
545 set_err_msg(err_msg
, "failed to fetch bucket info for bucket=" + bucket
.name
);
549 op_state
.set_bucket(bucket_info
.bucket
);
552 if (!user_id
.empty()) {
553 int r
= store
->ctl()->user
->get_info_by_uid(dpp
, user_id
, &user_info
, y
);
555 set_err_msg(err_msg
, "failed to fetch user info");
559 op_state
.display_name
= user_info
.display_name
;
566 bool rgw_find_bucket_by_id(const DoutPrefixProvider
*dpp
, CephContext
*cct
, RGWMetadataManager
*mgr
,
567 const string
& marker
, const string
& bucket_id
, rgw_bucket
* bucket_out
)
570 bool truncated
= false;
573 int ret
= mgr
->list_keys_init(dpp
, "bucket.instance", marker
, &handle
);
575 cerr
<< "ERROR: can't get key: " << cpp_strerror(-ret
) << std::endl
;
576 mgr
->list_keys_complete(handle
);
581 ret
= mgr
->list_keys_next(handle
, 1000, keys
, &truncated
);
583 cerr
<< "ERROR: lists_keys_next(): " << cpp_strerror(-ret
) << std::endl
;
584 mgr
->list_keys_complete(handle
);
587 for (list
<string
>::iterator iter
= keys
.begin(); iter
!= keys
.end(); ++iter
) {
589 ret
= rgw_bucket_parse_bucket_key(cct
, s
, bucket_out
, nullptr);
593 if (bucket_id
== bucket_out
->bucket_id
) {
594 mgr
->list_keys_complete(handle
);
599 mgr
->list_keys_complete(handle
);
603 int RGWBucket::link(RGWBucketAdminOpState
& op_state
, optional_yield y
, const DoutPrefixProvider
*dpp
,
604 map
<string
, bufferlist
>& attrs
, std::string
*err_msg
)
606 if (!op_state
.is_user_op()) {
607 set_err_msg(err_msg
, "empty user id");
611 string bucket_id
= op_state
.get_bucket_id();
613 std::string display_name
= op_state
.get_user_display_name();
614 rgw_bucket
& bucket
= op_state
.get_bucket();
615 if (!bucket_id
.empty() && bucket_id
!= bucket
.bucket_id
) {
617 "specified bucket id does not match " + bucket
.bucket_id
);
620 rgw_bucket old_bucket
= bucket
;
621 rgw_user user_id
= op_state
.get_user_id();
622 bucket
.tenant
= user_id
.tenant
;
623 if (!op_state
.new_bucket_name
.empty()) {
624 auto pos
= op_state
.new_bucket_name
.find('/');
625 if (pos
!= string::npos
) {
626 bucket
.tenant
= op_state
.new_bucket_name
.substr(0, pos
);
627 bucket
.name
= op_state
.new_bucket_name
.substr(pos
+ 1);
629 bucket
.name
= op_state
.new_bucket_name
;
633 RGWObjVersionTracker objv_tracker
;
634 RGWObjVersionTracker old_version
= bucket_info
.objv_tracker
;
636 map
<string
, bufferlist
>::iterator aiter
= attrs
.find(RGW_ATTR_ACL
);
637 if (aiter
== attrs
.end()) {
638 // should never happen; only pre-argonaut buckets lacked this.
639 ldpp_dout(dpp
, 0) << "WARNING: can't bucket link because no acl on bucket=" << old_bucket
.name
<< dendl
;
641 "While crossing the Anavros you have displeased the goddess Hera."
642 " You must sacrifice your ancient bucket " + bucket
.bucket_id
);
645 bufferlist
& aclbl
= aiter
->second
;
646 RGWAccessControlPolicy policy
;
649 auto iter
= aclbl
.cbegin();
650 decode(policy
, iter
);
651 owner
= policy
.get_owner();
652 } catch (buffer::error
& err
) {
653 set_err_msg(err_msg
, "couldn't decode policy");
657 auto bucket_ctl
= store
->ctl()->bucket
;
658 int r
= bucket_ctl
->unlink_bucket(owner
.get_id(), old_bucket
, y
, dpp
, false);
660 set_err_msg(err_msg
, "could not unlink policy from user " + owner
.get_id().to_str());
664 // now update the user for the bucket...
665 if (display_name
.empty()) {
666 ldpp_dout(dpp
, 0) << "WARNING: user " << user_info
.user_id
<< " has no display name set" << dendl
;
669 RGWAccessControlPolicy policy_instance
;
670 policy_instance
.create_default(user_info
.user_id
, display_name
);
671 owner
= policy_instance
.get_owner();
674 policy_instance
.encode(aclbl
);
676 auto instance_params
= RGWBucketCtl::BucketInstance::PutParams().set_attrs(&attrs
);
678 bucket_info
.owner
= user_info
.user_id
;
679 if (bucket
!= old_bucket
) {
680 bucket_info
.bucket
= bucket
;
681 bucket_info
.objv_tracker
.version_for_read()->ver
= 0;
682 instance_params
.set_exclusive(true);
685 r
= bucket_ctl
->store_bucket_instance_info(bucket
, bucket_info
, y
, dpp
, instance_params
);
687 set_err_msg(err_msg
, "ERROR: failed writing bucket instance info: " + cpp_strerror(-r
));
691 RGWBucketEntryPoint ep
;
692 ep
.bucket
= bucket_info
.bucket
;
693 ep
.owner
= user_info
.user_id
;
694 ep
.creation_time
= bucket_info
.creation_time
;
696 map
<string
, bufferlist
> ep_attrs
;
697 rgw_ep_info ep_data
{ep
, ep_attrs
};
700 r
= store
->ctl()->bucket
->link_bucket(user_info
.user_id
,
703 y
, dpp
, true, &ep_data
);
705 set_err_msg(err_msg
, "failed to relink bucket");
709 if (bucket
!= old_bucket
) {
710 // like RGWRados::delete_bucket -- excepting no bucket_index work.
711 r
= bucket_ctl
->remove_bucket_entrypoint_info(old_bucket
, y
, dpp
,
712 RGWBucketCtl::Bucket::RemoveParams()
713 .set_objv_tracker(&ep_data
.ep_objv
));
715 set_err_msg(err_msg
, "failed to unlink old bucket endpoint " + old_bucket
.tenant
+ "/" + old_bucket
.name
);
719 r
= bucket_ctl
->remove_bucket_instance_info(old_bucket
, bucket_info
, y
, dpp
,
720 RGWBucketCtl::BucketInstance::RemoveParams()
721 .set_objv_tracker(&old_version
));
723 set_err_msg(err_msg
, "failed to unlink old bucket info");
731 int RGWBucket::chown(RGWBucketAdminOpState
& op_state
, const string
& marker
,
732 optional_yield y
, const DoutPrefixProvider
*dpp
, std::string
*err_msg
)
734 int ret
= store
->ctl()->bucket
->chown(store
, bucket_info
, user_info
.user_id
,
735 user_info
.display_name
, marker
, y
, dpp
);
737 set_err_msg(err_msg
, "Failed to change object ownership: " + cpp_strerror(-ret
));
743 int RGWBucket::unlink(RGWBucketAdminOpState
& op_state
, optional_yield y
, const DoutPrefixProvider
*dpp
, std::string
*err_msg
)
745 rgw_bucket bucket
= op_state
.get_bucket();
747 if (!op_state
.is_user_op()) {
748 set_err_msg(err_msg
, "could not fetch user or user bucket info");
752 int r
= store
->ctl()->bucket
->unlink_bucket(user_info
.user_id
, bucket
, y
, dpp
);
754 set_err_msg(err_msg
, "error unlinking bucket" + cpp_strerror(-r
));
760 int RGWBucket::set_quota(RGWBucketAdminOpState
& op_state
, const DoutPrefixProvider
*dpp
, std::string
*err_msg
)
762 rgw_bucket bucket
= op_state
.get_bucket();
763 RGWBucketInfo bucket_info
;
764 map
<string
, bufferlist
> attrs
;
765 int r
= store
->getRados()->get_bucket_info(store
->svc(), bucket
.tenant
, bucket
.name
, bucket_info
, NULL
, null_yield
, dpp
, &attrs
);
767 set_err_msg(err_msg
, "could not get bucket info for bucket=" + bucket
.name
+ ": " + cpp_strerror(-r
));
771 bucket_info
.quota
= op_state
.quota
;
772 r
= store
->getRados()->put_bucket_instance_info(bucket_info
, false, real_time(), &attrs
, dpp
);
774 set_err_msg(err_msg
, "ERROR: failed writing bucket instance info: " + cpp_strerror(-r
));
780 int RGWBucket::remove_object(const DoutPrefixProvider
*dpp
, RGWBucketAdminOpState
& op_state
, std::string
*err_msg
)
782 rgw_bucket bucket
= op_state
.get_bucket();
783 std::string object_name
= op_state
.get_object_name();
785 rgw_obj_key
key(object_name
);
787 int ret
= rgw_remove_object(dpp
, store
, bucket_info
, bucket
, key
);
789 set_err_msg(err_msg
, "unable to remove object" + cpp_strerror(-ret
));
796 static void dump_bucket_index(const RGWRados::ent_map_t
& result
, Formatter
*f
)
798 for (auto iter
= result
.begin(); iter
!= result
.end(); ++iter
) {
799 f
->dump_string("object", iter
->first
);
803 static void dump_bucket_usage(map
<RGWObjCategory
, RGWStorageStats
>& stats
, Formatter
*formatter
)
805 map
<RGWObjCategory
, RGWStorageStats
>::iterator iter
;
807 formatter
->open_object_section("usage");
808 for (iter
= stats
.begin(); iter
!= stats
.end(); ++iter
) {
809 RGWStorageStats
& s
= iter
->second
;
810 const char *cat_name
= rgw_obj_category_name(iter
->first
);
811 formatter
->open_object_section(cat_name
);
813 formatter
->close_section();
815 formatter
->close_section();
818 static void dump_index_check(map
<RGWObjCategory
, RGWStorageStats
> existing_stats
,
819 map
<RGWObjCategory
, RGWStorageStats
> calculated_stats
,
820 Formatter
*formatter
)
822 formatter
->open_object_section("check_result");
823 formatter
->open_object_section("existing_header");
824 dump_bucket_usage(existing_stats
, formatter
);
825 formatter
->close_section();
826 formatter
->open_object_section("calculated_header");
827 dump_bucket_usage(calculated_stats
, formatter
);
828 formatter
->close_section();
829 formatter
->close_section();
832 int RGWBucket::check_bad_index_multipart(RGWBucketAdminOpState
& op_state
,
833 RGWFormatterFlusher
& flusher
,
834 const DoutPrefixProvider
*dpp
, std::string
*err_msg
)
836 bool fix_index
= op_state
.will_fix_index();
837 rgw_bucket bucket
= op_state
.get_bucket();
839 map
<string
, bool> common_prefixes
;
842 map
<string
, bool> meta_objs
;
843 map
<rgw_obj_index_key
, string
> all_objs
;
845 RGWBucketInfo bucket_info
;
846 auto obj_ctx
= store
->svc()->sysobj
->init_obj_ctx();
847 int r
= store
->getRados()->get_bucket_instance_info(obj_ctx
, bucket
, bucket_info
, nullptr, nullptr, null_yield
, dpp
);
849 ldpp_dout(dpp
, 0) << "ERROR: " << __func__
<< "(): get_bucket_instance_info(bucket=" << bucket
<< ") returned r=" << r
<< dendl
;
853 RGWRados::Bucket
target(store
->getRados(), bucket_info
);
854 RGWRados::Bucket::List
list_op(&target
);
856 list_op
.params
.list_versions
= true;
857 list_op
.params
.ns
= RGW_OBJ_NS_MULTIPART
;
860 vector
<rgw_bucket_dir_entry
> result
;
861 int r
= list_op
.list_objects(dpp
, listing_max_entries
, &result
,
862 &common_prefixes
, &is_truncated
, null_yield
);
864 set_err_msg(err_msg
, "failed to list objects in bucket=" + bucket
.name
+
865 " err=" + cpp_strerror(-r
));
870 vector
<rgw_bucket_dir_entry
>::iterator iter
;
871 for (iter
= result
.begin(); iter
!= result
.end(); ++iter
) {
872 rgw_obj_index_key key
= iter
->key
;
873 rgw_obj
obj(bucket
, key
);
874 string oid
= obj
.get_oid();
876 int pos
= oid
.find_last_of('.');
878 /* obj has no suffix */
882 string name
= oid
.substr(0, pos
);
883 string suffix
= oid
.substr(pos
+ 1);
885 if (suffix
.compare("meta") == 0) {
886 meta_objs
[name
] = true;
888 all_objs
[key
] = name
;
892 } while (is_truncated
);
894 list
<rgw_obj_index_key
> objs_to_unlink
;
895 Formatter
*f
= flusher
.get_formatter();
897 f
->open_array_section("invalid_multipart_entries");
899 for (auto aiter
= all_objs
.begin(); aiter
!= all_objs
.end(); ++aiter
) {
900 string
& name
= aiter
->second
;
902 if (meta_objs
.find(name
) == meta_objs
.end()) {
903 objs_to_unlink
.push_back(aiter
->first
);
906 if (objs_to_unlink
.size() > listing_max_entries
) {
908 int r
= store
->getRados()->remove_objs_from_index(dpp
, bucket_info
, objs_to_unlink
);
910 set_err_msg(err_msg
, "ERROR: remove_obj_from_index() returned error: " +
916 dump_mulipart_index_results(objs_to_unlink
, flusher
.get_formatter());
918 objs_to_unlink
.clear();
923 int r
= store
->getRados()->remove_objs_from_index(dpp
, bucket_info
, objs_to_unlink
);
925 set_err_msg(err_msg
, "ERROR: remove_obj_from_index() returned error: " +
932 dump_mulipart_index_results(objs_to_unlink
, f
);
939 int RGWBucket::check_object_index(const DoutPrefixProvider
*dpp
,
940 RGWBucketAdminOpState
& op_state
,
941 RGWFormatterFlusher
& flusher
,
943 std::string
*err_msg
)
946 bool fix_index
= op_state
.will_fix_index();
949 set_err_msg(err_msg
, "check-objects flag requires fix index enabled");
953 store
->getRados()->cls_obj_set_bucket_tag_timeout(dpp
, bucket_info
, BUCKET_TAG_TIMEOUT
);
956 string empty_delimiter
;
957 rgw_obj_index_key marker
;
958 bool is_truncated
= true;
959 bool cls_filtered
= true;
961 Formatter
*formatter
= flusher
.get_formatter();
962 formatter
->open_object_section("objects");
963 uint16_t expansion_factor
= 1;
964 while (is_truncated
) {
965 RGWRados::ent_map_t result
;
966 result
.reserve(listing_max_entries
);
968 int r
= store
->getRados()->cls_bucket_list_ordered(
969 dpp
, bucket_info
, RGW_NO_SHARD
, marker
, prefix
, empty_delimiter
,
970 listing_max_entries
, true, expansion_factor
,
971 result
, &is_truncated
, &cls_filtered
, &marker
,
972 y
, rgw_bucket_object_check_filter
);
975 } else if (r
< 0 && r
!= -ENOENT
) {
976 set_err_msg(err_msg
, "ERROR: failed operation r=" + cpp_strerror(-r
));
979 if (result
.size() < listing_max_entries
/ 8) {
981 } else if (result
.size() > listing_max_entries
* 7 / 8 &&
982 expansion_factor
> 1) {
986 dump_bucket_index(result
, formatter
);
990 formatter
->close_section();
992 store
->getRados()->cls_obj_set_bucket_tag_timeout(dpp
, bucket_info
, 0);
998 int RGWBucket::check_index(const DoutPrefixProvider
*dpp
,
999 RGWBucketAdminOpState
& op_state
,
1000 map
<RGWObjCategory
, RGWStorageStats
>& existing_stats
,
1001 map
<RGWObjCategory
, RGWStorageStats
>& calculated_stats
,
1002 std::string
*err_msg
)
1004 bool fix_index
= op_state
.will_fix_index();
1006 int r
= store
->getRados()->bucket_check_index(dpp
, bucket_info
, &existing_stats
, &calculated_stats
);
1008 set_err_msg(err_msg
, "failed to check index error=" + cpp_strerror(-r
));
1013 r
= store
->getRados()->bucket_rebuild_index(dpp
, bucket_info
);
1015 set_err_msg(err_msg
, "failed to rebuild index err=" + cpp_strerror(-r
));
1023 int RGWBucket::sync(RGWBucketAdminOpState
& op_state
, map
<string
, bufferlist
> *attrs
, const DoutPrefixProvider
*dpp
, std::string
*err_msg
)
1025 if (!store
->svc()->zone
->is_meta_master()) {
1026 set_err_msg(err_msg
, "ERROR: failed to update bucket sync: only allowed on meta master zone");
1029 bool sync
= op_state
.will_sync_bucket();
1031 bucket_info
.flags
&= ~BUCKET_DATASYNC_DISABLED
;
1033 bucket_info
.flags
|= BUCKET_DATASYNC_DISABLED
;
1036 int r
= store
->getRados()->put_bucket_instance_info(bucket_info
, false, real_time(), attrs
, dpp
);
1038 set_err_msg(err_msg
, "ERROR: failed writing bucket instance info:" + cpp_strerror(-r
));
1042 int shards_num
= bucket_info
.layout
.current_index
.layout
.normal
.num_shards
? bucket_info
.layout
.current_index
.layout
.normal
.num_shards
: 1;
1043 int shard_id
= bucket_info
.layout
.current_index
.layout
.normal
.num_shards
? 0 : -1;
1046 r
= store
->svc()->bilog_rados
->log_stop(dpp
, bucket_info
, -1);
1048 set_err_msg(err_msg
, "ERROR: failed writing stop bilog:" + cpp_strerror(-r
));
1052 r
= store
->svc()->bilog_rados
->log_start(dpp
, bucket_info
, -1);
1054 set_err_msg(err_msg
, "ERROR: failed writing resync bilog:" + cpp_strerror(-r
));
1059 for (int i
= 0; i
< shards_num
; ++i
, ++shard_id
) {
1060 r
= store
->svc()->datalog_rados
->add_entry(dpp
, bucket_info
, shard_id
);
1062 set_err_msg(err_msg
, "ERROR: failed writing data log:" + cpp_strerror(-r
));
1071 int RGWBucket::policy_bl_to_stream(bufferlist
& bl
, ostream
& o
)
1073 RGWAccessControlPolicy_S3
policy(g_ceph_context
);
1074 int ret
= decode_bl(bl
, policy
);
1076 ldout(store
->ctx(),0) << "failed to decode RGWAccessControlPolicy" << dendl
;
1082 int rgw_object_get_attr(const DoutPrefixProvider
*dpp
,
1083 rgw::sal::RGWRadosStore
* store
, const RGWBucketInfo
& bucket_info
,
1084 const rgw_obj
& obj
, const char* attr_name
,
1085 bufferlist
& out_bl
, optional_yield y
)
1087 RGWObjectCtx
obj_ctx(store
);
1088 RGWRados::Object
op_target(store
->getRados(), bucket_info
, obj_ctx
, obj
);
1089 RGWRados::Object::Read
rop(&op_target
);
1091 return rop
.get_attr(dpp
, attr_name
, out_bl
, y
);
1094 int RGWBucket::get_policy(RGWBucketAdminOpState
& op_state
, RGWAccessControlPolicy
& policy
, optional_yield y
, const DoutPrefixProvider
*dpp
)
1096 std::string object_name
= op_state
.get_object_name();
1097 rgw_bucket bucket
= op_state
.get_bucket();
1099 RGWBucketInfo bucket_info
;
1100 map
<string
, bufferlist
> attrs
;
1101 int ret
= store
->getRados()->get_bucket_info(store
->svc(), bucket
.tenant
, bucket
.name
, bucket_info
, NULL
, null_yield
, dpp
, &attrs
);
1106 if (!object_name
.empty()) {
1108 rgw_obj
obj(bucket
, object_name
);
1110 ret
= rgw_object_get_attr(dpp
, store
, bucket_info
, obj
, RGW_ATTR_ACL
, bl
, y
);
1115 ret
= decode_bl(bl
, policy
);
1117 ldout(store
->ctx(),0) << "failed to decode RGWAccessControlPolicy" << dendl
;
1122 map
<string
, bufferlist
>::iterator aiter
= attrs
.find(RGW_ATTR_ACL
);
1123 if (aiter
== attrs
.end()) {
1127 ret
= decode_bl(aiter
->second
, policy
);
1129 ldout(store
->ctx(),0) << "failed to decode RGWAccessControlPolicy" << dendl
;
1136 int RGWBucketAdminOp::get_policy(rgw::sal::RGWRadosStore
*store
, RGWBucketAdminOpState
& op_state
,
1137 RGWAccessControlPolicy
& policy
, const DoutPrefixProvider
*dpp
)
1141 int ret
= bucket
.init(store
, op_state
, null_yield
, dpp
);
1145 ret
= bucket
.get_policy(op_state
, policy
, null_yield
, dpp
);
1152 /* Wrappers to facilitate RESTful interface */
1155 int RGWBucketAdminOp::get_policy(rgw::sal::RGWRadosStore
*store
, RGWBucketAdminOpState
& op_state
,
1156 RGWFormatterFlusher
& flusher
, const DoutPrefixProvider
*dpp
)
1158 RGWAccessControlPolicy
policy(store
->ctx());
1160 int ret
= get_policy(store
, op_state
, policy
, dpp
);
1164 Formatter
*formatter
= flusher
.get_formatter();
1168 formatter
->open_object_section("policy");
1169 policy
.dump(formatter
);
1170 formatter
->close_section();
1177 int RGWBucketAdminOp::dump_s3_policy(rgw::sal::RGWRadosStore
*store
, RGWBucketAdminOpState
& op_state
,
1178 ostream
& os
, const DoutPrefixProvider
*dpp
)
1180 RGWAccessControlPolicy_S3
policy(store
->ctx());
1182 int ret
= get_policy(store
, op_state
, policy
, dpp
);
1191 int RGWBucketAdminOp::unlink(rgw::sal::RGWRadosStore
*store
, RGWBucketAdminOpState
& op_state
, const DoutPrefixProvider
*dpp
)
1195 int ret
= bucket
.init(store
, op_state
, null_yield
, dpp
);
1199 return bucket
.unlink(op_state
, null_yield
, dpp
);
1202 int RGWBucketAdminOp::link(rgw::sal::RGWRadosStore
*store
, RGWBucketAdminOpState
& op_state
, const DoutPrefixProvider
*dpp
, string
*err
)
1205 map
<string
, bufferlist
> attrs
;
1207 int ret
= bucket
.init(store
, op_state
, null_yield
, dpp
, err
, &attrs
);
1211 return bucket
.link(op_state
, null_yield
, dpp
, attrs
, err
);
1215 int RGWBucketAdminOp::chown(rgw::sal::RGWRadosStore
*store
, RGWBucketAdminOpState
& op_state
, const string
& marker
, const DoutPrefixProvider
*dpp
, string
*err
)
1218 map
<string
, bufferlist
> attrs
;
1220 int ret
= bucket
.init(store
, op_state
, null_yield
, dpp
, err
, &attrs
);
1224 ret
= bucket
.link(op_state
, null_yield
, dpp
, attrs
, err
);
1228 return bucket
.chown(op_state
, marker
, null_yield
, dpp
, err
);
1232 int RGWBucketAdminOp::check_index(rgw::sal::RGWRadosStore
*store
, RGWBucketAdminOpState
& op_state
,
1233 RGWFormatterFlusher
& flusher
, optional_yield y
, const DoutPrefixProvider
*dpp
)
1236 map
<RGWObjCategory
, RGWStorageStats
> existing_stats
;
1237 map
<RGWObjCategory
, RGWStorageStats
> calculated_stats
;
1242 ret
= bucket
.init(store
, op_state
, null_yield
, dpp
);
1246 Formatter
*formatter
= flusher
.get_formatter();
1249 ret
= bucket
.check_bad_index_multipart(op_state
, flusher
, dpp
);
1253 ret
= bucket
.check_object_index(dpp
, op_state
, flusher
, y
);
1257 ret
= bucket
.check_index(dpp
, op_state
, existing_stats
, calculated_stats
);
1261 dump_index_check(existing_stats
, calculated_stats
, formatter
);
1267 int RGWBucketAdminOp::remove_bucket(rgw::sal::RGWRadosStore
*store
, RGWBucketAdminOpState
& op_state
,
1268 optional_yield y
, const DoutPrefixProvider
*dpp
,
1269 bool bypass_gc
, bool keep_index_consistent
)
1271 std::unique_ptr
<rgw::sal::RGWBucket
> bucket
;
1272 std::unique_ptr
<rgw::sal::RGWUser
> user
= store
->get_user(op_state
.get_user_id());
1274 int ret
= store
->get_bucket(dpp
, user
.get(), user
->get_tenant(), op_state
.get_bucket_name(),
1280 ret
= rgw_remove_bucket_bypass_gc(store
, bucket
->get_key(), op_state
.get_max_aio(), keep_index_consistent
, y
, dpp
);
1282 ret
= bucket
->remove_bucket(dpp
, op_state
.will_delete_children(), string(), string(),
1288 int RGWBucketAdminOp::remove_object(rgw::sal::RGWRadosStore
*store
, RGWBucketAdminOpState
& op_state
, const DoutPrefixProvider
*dpp
)
1292 int ret
= bucket
.init(store
, op_state
, null_yield
, dpp
);
1296 return bucket
.remove_object(dpp
, op_state
);
1299 int RGWBucketAdminOp::sync_bucket(rgw::sal::RGWRadosStore
*store
, RGWBucketAdminOpState
& op_state
, const DoutPrefixProvider
*dpp
, string
*err_msg
)
1302 map
<string
, bufferlist
> attrs
;
1303 int ret
= bucket
.init(store
, op_state
, null_yield
, dpp
, err_msg
, &attrs
);
1308 return bucket
.sync(op_state
, &attrs
, dpp
, err_msg
);
1311 static int bucket_stats(rgw::sal::RGWRadosStore
*store
,
1312 const std::string
& tenant_name
,
1313 const std::string
& bucket_name
,
1314 Formatter
*formatter
,
1315 const DoutPrefixProvider
*dpp
)
1317 RGWBucketInfo bucket_info
;
1318 map
<RGWObjCategory
, RGWStorageStats
> stats
;
1319 map
<string
, bufferlist
> attrs
;
1322 int r
= store
->getRados()->get_bucket_info(store
->svc(),
1323 tenant_name
, bucket_name
, bucket_info
,
1324 &mtime
, null_yield
, dpp
, &attrs
);
1329 rgw_bucket
& bucket
= bucket_info
.bucket
;
1331 string bucket_ver
, master_ver
;
1333 int ret
= store
->getRados()->get_bucket_stats(dpp
, bucket_info
, RGW_NO_SHARD
,
1334 &bucket_ver
, &master_ver
, stats
,
1337 cerr
<< "error getting bucket stats bucket=" << bucket
.name
<< " ret=" << ret
<< std::endl
;
1342 utime_t
ctime_ut(bucket_info
.creation_time
);
1344 formatter
->open_object_section("stats");
1345 formatter
->dump_string("bucket", bucket
.name
);
1346 formatter
->dump_int("num_shards",
1347 bucket_info
.layout
.current_index
.layout
.normal
.num_shards
);
1348 formatter
->dump_string("tenant", bucket
.tenant
);
1349 formatter
->dump_string("zonegroup", bucket_info
.zonegroup
);
1350 formatter
->dump_string("placement_rule", bucket_info
.placement_rule
.to_str());
1351 ::encode_json("explicit_placement", bucket
.explicit_placement
, formatter
);
1352 formatter
->dump_string("id", bucket
.bucket_id
);
1353 formatter
->dump_string("marker", bucket
.marker
);
1354 formatter
->dump_stream("index_type") << bucket_info
.layout
.current_index
.layout
.type
;
1355 ::encode_json("owner", bucket_info
.owner
, formatter
);
1356 formatter
->dump_string("ver", bucket_ver
);
1357 formatter
->dump_string("master_ver", master_ver
);
1358 ut
.gmtime(formatter
->dump_stream("mtime"));
1359 ctime_ut
.gmtime(formatter
->dump_stream("creation_time"));
1360 formatter
->dump_string("max_marker", max_marker
);
1361 dump_bucket_usage(stats
, formatter
);
1362 encode_json("bucket_quota", bucket_info
.quota
, formatter
);
1365 auto iter
= attrs
.find(RGW_ATTR_TAGS
);
1366 if (iter
!= attrs
.end()) {
1367 RGWObjTagSet_S3 tagset
;
1368 bufferlist::const_iterator piter
{&iter
->second
};
1370 tagset
.decode(piter
);
1371 tagset
.dump(formatter
);
1372 } catch (buffer::error
& err
) {
1373 cerr
<< "ERROR: caught buffer:error, couldn't decode TagSet" << std::endl
;
1377 // TODO: bucket CORS
1379 formatter
->close_section();
1384 int RGWBucketAdminOp::limit_check(rgw::sal::RGWRadosStore
*store
,
1385 RGWBucketAdminOpState
& op_state
,
1386 const std::list
<std::string
>& user_ids
,
1387 RGWFormatterFlusher
& flusher
, optional_yield y
,
1388 const DoutPrefixProvider
*dpp
,
1392 const size_t max_entries
=
1393 store
->ctx()->_conf
->rgw_list_buckets_max_chunk
;
1395 const size_t safe_max_objs_per_shard
=
1396 store
->ctx()->_conf
->rgw_safe_max_objects_per_shard
;
1398 uint16_t shard_warn_pct
=
1399 store
->ctx()->_conf
->rgw_shard_warning_threshold
;
1400 if (shard_warn_pct
> 100)
1401 shard_warn_pct
= 90;
1403 Formatter
*formatter
= flusher
.get_formatter();
1406 formatter
->open_array_section("users");
1408 for (const auto& user_id
: user_ids
) {
1410 formatter
->open_object_section("user");
1411 formatter
->dump_string("user_id", user_id
);
1412 formatter
->open_array_section("buckets");
1415 rgw::sal::RGWBucketList buckets
;
1417 rgw::sal::RGWRadosUser
user(store
, rgw_user(user_id
));
1419 ret
= user
.list_buckets(dpp
, marker
, string(), max_entries
, false, buckets
, y
);
1424 map
<string
, std::unique_ptr
<rgw::sal::RGWBucket
>>& m_buckets
= buckets
.get_buckets();
1426 for (const auto& iter
: m_buckets
) {
1427 auto& bucket
= iter
.second
;
1428 uint32_t num_shards
= 1;
1429 uint64_t num_objects
= 0;
1431 /* need info for num_shards */
1434 marker
= bucket
->get_name(); /* Casey's location for marker update,
1435 * as we may now not reach the end of
1438 ret
= store
->getRados()->get_bucket_info(store
->svc(), bucket
->get_tenant(),
1439 bucket
->get_name(), info
, nullptr,
1444 /* need stats for num_entries */
1445 string bucket_ver
, master_ver
;
1446 std::map
<RGWObjCategory
, RGWStorageStats
> stats
;
1447 ret
= store
->getRados()->get_bucket_stats(dpp
, info
, RGW_NO_SHARD
, &bucket_ver
,
1448 &master_ver
, stats
, nullptr);
1453 for (const auto& s
: stats
) {
1454 num_objects
+= s
.second
.num_objects
;
1457 num_shards
= info
.layout
.current_index
.layout
.normal
.num_shards
;
1458 uint64_t objs_per_shard
=
1459 (num_shards
) ? num_objects
/num_shards
: num_objects
;
1463 uint64_t fill_pct
= objs_per_shard
* 100 / safe_max_objs_per_shard
;
1464 if (fill_pct
> 100) {
1465 ss
<< "OVER " << fill_pct
<< "%";
1467 } else if (fill_pct
>= shard_warn_pct
) {
1468 ss
<< "WARN " << fill_pct
<< "%";
1475 if (warn
|| !warnings_only
) {
1476 formatter
->open_object_section("bucket");
1477 formatter
->dump_string("bucket", bucket
->get_name());
1478 formatter
->dump_string("tenant", bucket
->get_tenant());
1479 formatter
->dump_int("num_objects", num_objects
);
1480 formatter
->dump_int("num_shards", num_shards
);
1481 formatter
->dump_int("objects_per_shard", objs_per_shard
);
1482 formatter
->dump_string("fill_status", ss
.str());
1483 formatter
->close_section();
1487 formatter
->flush(cout
);
1488 } while (buckets
.is_truncated()); /* foreach: bucket */
1490 formatter
->close_section();
1491 formatter
->close_section();
1492 formatter
->flush(cout
);
1494 } /* foreach: user_id */
1496 formatter
->close_section();
1497 formatter
->flush(cout
);
1500 } /* RGWBucketAdminOp::limit_check */
1502 int RGWBucketAdminOp::info(rgw::sal::RGWRadosStore
*store
,
1503 RGWBucketAdminOpState
& op_state
,
1504 RGWFormatterFlusher
& flusher
,
1506 const DoutPrefixProvider
*dpp
)
1510 const std::string
& bucket_name
= op_state
.get_bucket_name();
1511 if (!bucket_name
.empty()) {
1512 ret
= bucket
.init(store
, op_state
, null_yield
, dpp
);
1514 return -ERR_NO_SUCH_BUCKET
;
1519 Formatter
*formatter
= flusher
.get_formatter();
1522 CephContext
*cct
= store
->ctx();
1524 const size_t max_entries
= cct
->_conf
->rgw_list_buckets_max_chunk
;
1526 const bool show_stats
= op_state
.will_fetch_stats();
1527 const rgw_user
& user_id
= op_state
.get_user_id();
1528 if (op_state
.is_user_op()) {
1529 formatter
->open_array_section("buckets");
1531 rgw::sal::RGWBucketList buckets
;
1532 rgw::sal::RGWRadosUser
user(store
, op_state
.get_user_id());
1534 const std::string empty_end_marker
;
1535 constexpr bool no_need_stats
= false; // set need_stats to false
1538 ret
= user
.list_buckets(dpp
, marker
, empty_end_marker
, max_entries
,
1539 no_need_stats
, buckets
, y
);
1544 const std::string
* marker_cursor
= nullptr;
1545 map
<string
, std::unique_ptr
<rgw::sal::RGWBucket
>>& m
= buckets
.get_buckets();
1547 for (const auto& i
: m
) {
1548 const std::string
& obj_name
= i
.first
;
1549 if (!bucket_name
.empty() && bucket_name
!= obj_name
) {
1554 bucket_stats(store
, user_id
.tenant
, obj_name
, formatter
, dpp
);
1556 formatter
->dump_string("bucket", obj_name
);
1559 marker_cursor
= &obj_name
;
1561 if (marker_cursor
) {
1562 marker
= *marker_cursor
;
1566 } while (buckets
.is_truncated());
1568 formatter
->close_section();
1569 } else if (!bucket_name
.empty()) {
1570 ret
= bucket_stats(store
, user_id
.tenant
, bucket_name
, formatter
, dpp
);
1575 void *handle
= nullptr;
1576 bool truncated
= true;
1578 formatter
->open_array_section("buckets");
1579 ret
= store
->ctl()->meta
.mgr
->list_keys_init(dpp
, "bucket", &handle
);
1580 while (ret
== 0 && truncated
) {
1581 std::list
<std::string
> buckets
;
1582 constexpr int max_keys
= 1000;
1583 ret
= store
->ctl()->meta
.mgr
->list_keys_next(handle
, max_keys
, buckets
,
1585 for (auto& bucket_name
: buckets
) {
1587 bucket_stats(store
, user_id
.tenant
, bucket_name
, formatter
, dpp
);
1589 formatter
->dump_string("bucket", bucket_name
);
1593 store
->ctl()->meta
.mgr
->list_keys_complete(handle
);
1595 formatter
->close_section();
1603 int RGWBucketAdminOp::set_quota(rgw::sal::RGWRadosStore
*store
, RGWBucketAdminOpState
& op_state
, const DoutPrefixProvider
*dpp
)
1607 int ret
= bucket
.init(store
, op_state
, null_yield
, dpp
);
1610 return bucket
.set_quota(op_state
, dpp
);
1613 static int purge_bucket_instance(rgw::sal::RGWRadosStore
*store
, const RGWBucketInfo
& bucket_info
, const DoutPrefixProvider
*dpp
)
1615 int max_shards
= (bucket_info
.layout
.current_index
.layout
.normal
.num_shards
> 0 ? bucket_info
.layout
.current_index
.layout
.normal
.num_shards
: 1);
1616 for (int i
= 0; i
< max_shards
; i
++) {
1617 RGWRados::BucketShard
bs(store
->getRados());
1618 int shard_id
= (bucket_info
.layout
.current_index
.layout
.normal
.num_shards
> 0 ? i
: -1);
1619 int ret
= bs
.init(bucket_info
.bucket
, shard_id
, bucket_info
.layout
.current_index
, nullptr, dpp
);
1621 cerr
<< "ERROR: bs.init(bucket=" << bucket_info
.bucket
<< ", shard=" << shard_id
1622 << "): " << cpp_strerror(-ret
) << std::endl
;
1625 ret
= store
->getRados()->bi_remove(bs
);
1627 cerr
<< "ERROR: failed to remove bucket index object: "
1628 << cpp_strerror(-ret
) << std::endl
;
1635 inline auto split_tenant(const std::string
& bucket_name
){
1636 auto p
= bucket_name
.find('/');
1637 if(p
!= std::string::npos
) {
1638 return std::make_pair(bucket_name
.substr(0,p
), bucket_name
.substr(p
+1));
1640 return std::make_pair(std::string(), bucket_name
);
1643 using bucket_instance_ls
= std::vector
<RGWBucketInfo
>;
1644 void get_stale_instances(rgw::sal::RGWRadosStore
*store
, const std::string
& bucket_name
,
1645 const vector
<std::string
>& lst
,
1646 bucket_instance_ls
& stale_instances
,
1647 const DoutPrefixProvider
*dpp
)
1650 auto obj_ctx
= store
->svc()->sysobj
->init_obj_ctx();
1652 bucket_instance_ls other_instances
;
1653 // first iterate over the entries, and pick up the done buckets; these
1654 // are guaranteed to be stale
1655 for (const auto& bucket_instance
: lst
){
1656 RGWBucketInfo binfo
;
1657 int r
= store
->getRados()->get_bucket_instance_info(obj_ctx
, bucket_instance
,
1658 binfo
, nullptr,nullptr, null_yield
, dpp
);
1660 // this can only happen if someone deletes us right when we're processing
1661 ldpp_dout(dpp
, -1) << "Bucket instance is invalid: " << bucket_instance
1662 << cpp_strerror(-r
) << dendl
;
1665 if (binfo
.reshard_status
== cls_rgw_reshard_status::DONE
)
1666 stale_instances
.emplace_back(std::move(binfo
));
1668 other_instances
.emplace_back(std::move(binfo
));
1672 // Read the cur bucket info, if the bucket doesn't exist we can simply return
1673 // all the instances
1674 auto [tenant
, bucket
] = split_tenant(bucket_name
);
1675 RGWBucketInfo cur_bucket_info
;
1676 int r
= store
->getRados()->get_bucket_info(store
->svc(), tenant
, bucket
, cur_bucket_info
, nullptr, null_yield
, dpp
);
1679 // bucket doesn't exist, everything is stale then
1680 stale_instances
.insert(std::end(stale_instances
),
1681 std::make_move_iterator(other_instances
.begin()),
1682 std::make_move_iterator(other_instances
.end()));
1684 // all bets are off if we can't read the bucket, just return the sureshot stale instances
1685 ldpp_dout(dpp
, -1) << "error: reading bucket info for bucket: "
1686 << bucket
<< cpp_strerror(-r
) << dendl
;
1691 // Don't process further in this round if bucket is resharding
1692 if (cur_bucket_info
.reshard_status
== cls_rgw_reshard_status::IN_PROGRESS
)
1695 other_instances
.erase(std::remove_if(other_instances
.begin(), other_instances
.end(),
1696 [&cur_bucket_info
](const RGWBucketInfo
& b
){
1697 return (b
.bucket
.bucket_id
== cur_bucket_info
.bucket
.bucket_id
||
1698 b
.bucket
.bucket_id
== cur_bucket_info
.new_bucket_instance_id
);
1700 other_instances
.end());
1702 // check if there are still instances left
1703 if (other_instances
.empty()) {
1707 // Now we have a bucket with instances where the reshard status is none, this
1708 // usually happens when the reshard process couldn't complete, lockdown the
1709 // bucket and walk through these instances to make sure no one else interferes
1712 RGWBucketReshardLock
reshard_lock(store
, cur_bucket_info
, true);
1713 r
= reshard_lock
.lock();
1715 // most likely bucket is under reshard, return the sureshot stale instances
1716 ldpp_dout(dpp
, 5) << __func__
1717 << "failed to take reshard lock; reshard underway likey" << dendl
;
1720 auto sg
= make_scope_guard([&reshard_lock
](){ reshard_lock
.unlock();} );
1721 // this should be fast enough that we may not need to renew locks and check
1722 // exit status?, should we read the values of the instances again?
1723 stale_instances
.insert(std::end(stale_instances
),
1724 std::make_move_iterator(other_instances
.begin()),
1725 std::make_move_iterator(other_instances
.end()));
1731 static int process_stale_instances(rgw::sal::RGWRadosStore
*store
, RGWBucketAdminOpState
& op_state
,
1732 RGWFormatterFlusher
& flusher
,
1733 const DoutPrefixProvider
*dpp
,
1734 std::function
<void(const bucket_instance_ls
&,
1736 rgw::sal::RGWRadosStore
*)> process_f
)
1740 Formatter
*formatter
= flusher
.get_formatter();
1741 static constexpr auto default_max_keys
= 1000;
1743 int ret
= store
->ctl()->meta
.mgr
->list_keys_init(dpp
, "bucket.instance", marker
, &handle
);
1745 cerr
<< "ERROR: can't get key: " << cpp_strerror(-ret
) << std::endl
;
1751 formatter
->open_array_section("keys");
1752 auto g
= make_scope_guard([&store
, &handle
, &formatter
]() {
1753 store
->ctl()->meta
.mgr
->list_keys_complete(handle
);
1754 formatter
->close_section(); // keys
1755 formatter
->flush(cout
);
1759 list
<std::string
> keys
;
1761 ret
= store
->ctl()->meta
.mgr
->list_keys_next(handle
, default_max_keys
, keys
, &truncated
);
1762 if (ret
< 0 && ret
!= -ENOENT
) {
1763 cerr
<< "ERROR: lists_keys_next(): " << cpp_strerror(-ret
) << std::endl
;
1765 } if (ret
!= -ENOENT
) {
1766 // partition the list of buckets by buckets as the listing is un sorted,
1767 // since it would minimize the reads to bucket_info
1768 std::unordered_map
<std::string
, std::vector
<std::string
>> bucket_instance_map
;
1769 for (auto &key
: keys
) {
1770 auto pos
= key
.find(':');
1771 if(pos
!= std::string::npos
)
1772 bucket_instance_map
[key
.substr(0,pos
)].emplace_back(std::move(key
));
1774 for (const auto& kv
: bucket_instance_map
) {
1775 bucket_instance_ls stale_lst
;
1776 get_stale_instances(store
, kv
.first
, kv
.second
, stale_lst
, dpp
);
1777 process_f(stale_lst
, formatter
, store
);
1780 } while (truncated
);
1785 int RGWBucketAdminOp::list_stale_instances(rgw::sal::RGWRadosStore
*store
,
1786 RGWBucketAdminOpState
& op_state
,
1787 RGWFormatterFlusher
& flusher
,
1788 const DoutPrefixProvider
*dpp
)
1790 auto process_f
= [](const bucket_instance_ls
& lst
,
1791 Formatter
*formatter
,
1792 rgw::sal::RGWRadosStore
*){
1793 for (const auto& binfo
: lst
)
1794 formatter
->dump_string("key", binfo
.bucket
.get_key());
1796 return process_stale_instances(store
, op_state
, flusher
, dpp
, process_f
);
1800 int RGWBucketAdminOp::clear_stale_instances(rgw::sal::RGWRadosStore
*store
,
1801 RGWBucketAdminOpState
& op_state
,
1802 RGWFormatterFlusher
& flusher
,
1803 const DoutPrefixProvider
*dpp
)
1805 auto process_f
= [dpp
](const bucket_instance_ls
& lst
,
1806 Formatter
*formatter
,
1807 rgw::sal::RGWRadosStore
*store
) {
1808 for (const auto &binfo
: lst
) {
1809 int ret
= purge_bucket_instance(store
, binfo
, dpp
);
1811 auto md_key
= "bucket.instance:" + binfo
.bucket
.get_key();
1812 ret
= store
->ctl()->meta
.mgr
->remove(md_key
, null_yield
, dpp
);
1814 formatter
->open_object_section("delete_status");
1815 formatter
->dump_string("bucket_instance", binfo
.bucket
.get_key());
1816 formatter
->dump_int("status", -ret
);
1817 formatter
->close_section();
1821 return process_stale_instances(store
, op_state
, flusher
, dpp
, process_f
);
1824 static int fix_single_bucket_lc(rgw::sal::RGWRadosStore
*store
,
1825 const std::string
& tenant_name
,
1826 const std::string
& bucket_name
,
1827 const DoutPrefixProvider
*dpp
)
1829 RGWBucketInfo bucket_info
;
1830 map
<std::string
, bufferlist
> bucket_attrs
;
1831 int ret
= store
->getRados()->get_bucket_info(store
->svc(), tenant_name
, bucket_name
,
1832 bucket_info
, nullptr, null_yield
, dpp
, &bucket_attrs
);
1834 // TODO: Should we handle the case where the bucket could've been removed between
1835 // listing and fetching?
1839 return rgw::lc::fix_lc_shard_entry(dpp
, store
, store
->get_rgwlc()->get_lc(), bucket_info
,
1843 static void format_lc_status(Formatter
* formatter
,
1844 const std::string
& tenant_name
,
1845 const std::string
& bucket_name
,
1848 formatter
->open_object_section("bucket_entry");
1849 std::string entry
= tenant_name
.empty() ? bucket_name
: tenant_name
+ "/" + bucket_name
;
1850 formatter
->dump_string("bucket", entry
);
1851 formatter
->dump_int("status", status
);
1852 formatter
->close_section(); // bucket_entry
1855 static void process_single_lc_entry(rgw::sal::RGWRadosStore
*store
,
1856 Formatter
*formatter
,
1857 const std::string
& tenant_name
,
1858 const std::string
& bucket_name
,
1859 const DoutPrefixProvider
*dpp
)
1861 int ret
= fix_single_bucket_lc(store
, tenant_name
, bucket_name
, dpp
);
1862 format_lc_status(formatter
, tenant_name
, bucket_name
, -ret
);
1865 int RGWBucketAdminOp::fix_lc_shards(rgw::sal::RGWRadosStore
*store
,
1866 RGWBucketAdminOpState
& op_state
,
1867 RGWFormatterFlusher
& flusher
,
1868 const DoutPrefixProvider
*dpp
)
1872 Formatter
*formatter
= flusher
.get_formatter();
1873 static constexpr auto default_max_keys
= 1000;
1876 if (const std::string
& bucket_name
= op_state
.get_bucket_name();
1877 ! bucket_name
.empty()) {
1878 const rgw_user user_id
= op_state
.get_user_id();
1879 process_single_lc_entry(store
, formatter
, user_id
.tenant
, bucket_name
, dpp
);
1880 formatter
->flush(cout
);
1882 int ret
= store
->ctl()->meta
.mgr
->list_keys_init(dpp
, "bucket", marker
, &handle
);
1884 std::cerr
<< "ERROR: can't get key: " << cpp_strerror(-ret
) << std::endl
;
1889 formatter
->open_array_section("lc_fix_status");
1890 auto sg
= make_scope_guard([&store
, &handle
, &formatter
](){
1891 store
->ctl()->meta
.mgr
->list_keys_complete(handle
);
1892 formatter
->close_section(); // lc_fix_status
1893 formatter
->flush(cout
);
1896 list
<std::string
> keys
;
1897 ret
= store
->ctl()->meta
.mgr
->list_keys_next(handle
, default_max_keys
, keys
, &truncated
);
1898 if (ret
< 0 && ret
!= -ENOENT
) {
1899 std::cerr
<< "ERROR: lists_keys_next(): " << cpp_strerror(-ret
) << std::endl
;
1901 } if (ret
!= -ENOENT
) {
1902 for (const auto &key
:keys
) {
1903 auto [tenant_name
, bucket_name
] = split_tenant(key
);
1904 process_single_lc_entry(store
, formatter
, tenant_name
, bucket_name
, dpp
);
1907 formatter
->flush(cout
); // regularly flush every 1k entries
1908 } while (truncated
);
1916 static bool has_object_expired(const DoutPrefixProvider
*dpp
,
1917 rgw::sal::RGWRadosStore
*store
,
1918 const RGWBucketInfo
& bucket_info
,
1919 const rgw_obj_key
& key
, utime_t
& delete_at
)
1921 rgw_obj
obj(bucket_info
.bucket
, key
);
1922 bufferlist delete_at_bl
;
1924 int ret
= rgw_object_get_attr(dpp
, store
, bucket_info
, obj
, RGW_ATTR_DELETE_AT
, delete_at_bl
, null_yield
);
1926 return false; // no delete at attr, proceed
1929 ret
= decode_bl(delete_at_bl
, delete_at
);
1931 return false; // failed to parse
1934 if (delete_at
<= ceph_clock_now() && !delete_at
.is_zero()) {
1941 static int fix_bucket_obj_expiry(const DoutPrefixProvider
*dpp
,
1942 rgw::sal::RGWRadosStore
*store
,
1943 const RGWBucketInfo
& bucket_info
,
1944 RGWFormatterFlusher
& flusher
, bool dry_run
)
1946 if (bucket_info
.bucket
.bucket_id
== bucket_info
.bucket
.marker
) {
1947 ldpp_dout(dpp
, -1) << "Not a resharded bucket skipping" << dendl
;
1948 return 0; // not a resharded bucket, move along
1951 Formatter
*formatter
= flusher
.get_formatter();
1952 formatter
->open_array_section("expired_deletion_status");
1953 auto sg
= make_scope_guard([&formatter
] {
1954 formatter
->close_section();
1955 formatter
->flush(std::cout
);
1958 RGWRados::Bucket
target(store
->getRados(), bucket_info
);
1959 RGWRados::Bucket::List
list_op(&target
);
1961 list_op
.params
.list_versions
= bucket_info
.versioned();
1962 list_op
.params
.allow_unordered
= true;
1964 bool is_truncated
{false};
1966 std::vector
<rgw_bucket_dir_entry
> objs
;
1968 int ret
= list_op
.list_objects(dpp
, listing_max_entries
, &objs
, nullptr,
1969 &is_truncated
, null_yield
);
1971 ldpp_dout(dpp
, -1) << "ERROR failed to list objects in the bucket" << dendl
;
1974 for (const auto& obj
: objs
) {
1975 rgw_obj_key
key(obj
.key
);
1977 if (has_object_expired(dpp
, store
, bucket_info
, key
, delete_at
)) {
1978 formatter
->open_object_section("object_status");
1979 formatter
->dump_string("object", key
.name
);
1980 formatter
->dump_stream("delete_at") << delete_at
;
1983 ret
= rgw_remove_object(dpp
, store
, bucket_info
, bucket_info
.bucket
, key
);
1984 formatter
->dump_int("status", ret
);
1987 formatter
->close_section(); // object_status
1990 formatter
->flush(cout
); // regularly flush every 1k entries
1991 } while (is_truncated
);
1996 int RGWBucketAdminOp::fix_obj_expiry(rgw::sal::RGWRadosStore
*store
,
1997 RGWBucketAdminOpState
& op_state
,
1998 RGWFormatterFlusher
& flusher
,
1999 const DoutPrefixProvider
*dpp
, bool dry_run
)
2001 RGWBucket admin_bucket
;
2002 int ret
= admin_bucket
.init(store
, op_state
, null_yield
, dpp
);
2004 ldpp_dout(dpp
, -1) << "failed to initialize bucket" << dendl
;
2008 return fix_bucket_obj_expiry(dpp
, store
, admin_bucket
.get_bucket_info(), flusher
, dry_run
);
2011 void RGWBucketCompleteInfo::dump(Formatter
*f
) const {
2012 encode_json("bucket_info", info
, f
);
2013 encode_json("attrs", attrs
, f
);
2016 void RGWBucketCompleteInfo::decode_json(JSONObj
*obj
) {
2017 JSONDecoder::decode_json("bucket_info", info
, obj
);
2018 JSONDecoder::decode_json("attrs", attrs
, obj
);
2021 class RGWBucketMetadataHandler
: public RGWBucketMetadataHandlerBase
{
2024 RGWSI_Bucket
*bucket
{nullptr};
2028 RGWBucketCtl
*bucket
{nullptr};
2031 RGWBucketMetadataHandler() {}
2033 void init(RGWSI_Bucket
*bucket_svc
,
2034 RGWBucketCtl
*bucket_ctl
) override
{
2035 base_init(bucket_svc
->ctx(),
2036 bucket_svc
->get_ep_be_handler().get());
2037 svc
.bucket
= bucket_svc
;
2038 ctl
.bucket
= bucket_ctl
;
2041 string
get_type() override
{ return "bucket"; }
2043 RGWMetadataObject
*get_meta_obj(JSONObj
*jo
, const obj_version
& objv
, const ceph::real_time
& mtime
) override
{
2044 RGWBucketEntryPoint be
;
2047 decode_json_obj(be
, jo
);
2048 } catch (JSONDecoder::err
& e
) {
2052 return new RGWBucketEntryMetadataObject(be
, objv
, mtime
);
2055 int do_get(RGWSI_MetaBackend_Handler::Op
*op
, string
& entry
, RGWMetadataObject
**obj
, optional_yield y
, const DoutPrefixProvider
*dpp
) override
{
2056 RGWObjVersionTracker ot
;
2057 RGWBucketEntryPoint be
;
2060 map
<string
, bufferlist
> attrs
;
2062 RGWSI_Bucket_EP_Ctx
ctx(op
->ctx());
2064 int ret
= svc
.bucket
->read_bucket_entrypoint_info(ctx
, entry
, &be
, &ot
, &mtime
, &attrs
, y
, dpp
);
2068 RGWBucketEntryMetadataObject
*mdo
= new RGWBucketEntryMetadataObject(be
, ot
.read_version
, mtime
, std::move(attrs
));
2075 int do_put(RGWSI_MetaBackend_Handler::Op
*op
, string
& entry
,
2076 RGWMetadataObject
*obj
,
2077 RGWObjVersionTracker
& objv_tracker
,
2079 const DoutPrefixProvider
*dpp
,
2080 RGWMDLogSyncType type
, bool from_remote_zone
) override
;
2082 int do_remove(RGWSI_MetaBackend_Handler::Op
*op
, string
& entry
, RGWObjVersionTracker
& objv_tracker
,
2083 optional_yield y
, const DoutPrefixProvider
*dpp
) override
{
2084 RGWBucketEntryPoint be
;
2086 real_time orig_mtime
;
2088 RGWSI_Bucket_EP_Ctx
ctx(op
->ctx());
2090 int ret
= svc
.bucket
->read_bucket_entrypoint_info(ctx
, entry
, &be
, &objv_tracker
, &orig_mtime
, nullptr, y
, dpp
);
2095 * We're unlinking the bucket but we don't want to update the entrypoint here - we're removing
2096 * it immediately and don't want to invalidate our cached objv_version or the bucket obj removal
2097 * will incorrectly fail.
2099 ret
= ctl
.bucket
->unlink_bucket(be
.owner
, be
.bucket
, y
, dpp
, false);
2101 ldpp_dout(dpp
, -1) << "could not unlink bucket=" << entry
<< " owner=" << be
.owner
<< dendl
;
2104 ret
= svc
.bucket
->remove_bucket_entrypoint_info(ctx
, entry
, &objv_tracker
, y
, dpp
);
2106 ldpp_dout(dpp
, -1) << "could not delete bucket=" << entry
<< dendl
;
2112 int call(std::function
<int(RGWSI_Bucket_EP_Ctx
& ctx
)> f
) {
2113 return call(nullopt
, f
);
2116 int call(std::optional
<RGWSI_MetaBackend_CtxParams
> bectx_params
,
2117 std::function
<int(RGWSI_Bucket_EP_Ctx
& ctx
)> f
) {
2118 return be_handler
->call(bectx_params
, [&](RGWSI_MetaBackend_Handler::Op
*op
) {
2119 RGWSI_Bucket_EP_Ctx
ctx(op
->ctx());
2125 class RGWMetadataHandlerPut_Bucket
: public RGWMetadataHandlerPut_SObj
2127 RGWBucketMetadataHandler
*bhandler
;
2128 RGWBucketEntryMetadataObject
*obj
;
2130 RGWMetadataHandlerPut_Bucket(RGWBucketMetadataHandler
*_handler
,
2131 RGWSI_MetaBackend_Handler::Op
*op
, string
& entry
,
2132 RGWMetadataObject
*_obj
, RGWObjVersionTracker
& objv_tracker
,
2134 RGWMDLogSyncType type
, bool from_remote_zone
) : RGWMetadataHandlerPut_SObj(_handler
, op
, entry
, obj
, objv_tracker
, y
, type
, from_remote_zone
),
2135 bhandler(_handler
) {
2136 obj
= static_cast<RGWBucketEntryMetadataObject
*>(_obj
);
2138 ~RGWMetadataHandlerPut_Bucket() {}
2140 void encode_obj(bufferlist
*bl
) override
{
2141 obj
->get_ep().encode(*bl
);
2144 int put_checked(const DoutPrefixProvider
*dpp
) override
;
2145 int put_post(const DoutPrefixProvider
*dpp
) override
;
2148 int RGWBucketMetadataHandler::do_put(RGWSI_MetaBackend_Handler::Op
*op
, string
& entry
,
2149 RGWMetadataObject
*obj
,
2150 RGWObjVersionTracker
& objv_tracker
,
2152 const DoutPrefixProvider
*dpp
,
2153 RGWMDLogSyncType type
, bool from_remote_zone
)
2155 RGWMetadataHandlerPut_Bucket
put_op(this, op
, entry
, obj
, objv_tracker
, y
, type
, from_remote_zone
);
2156 return do_put_operate(&put_op
, dpp
);
2159 int RGWMetadataHandlerPut_Bucket::put_checked(const DoutPrefixProvider
*dpp
)
2161 RGWBucketEntryMetadataObject
*orig_obj
= static_cast<RGWBucketEntryMetadataObject
*>(old_obj
);
2164 obj
->set_pattrs(&orig_obj
->get_attrs());
2167 auto& be
= obj
->get_ep();
2168 auto mtime
= obj
->get_mtime();
2169 auto pattrs
= obj
->get_pattrs();
2171 RGWSI_Bucket_EP_Ctx
ctx(op
->ctx());
2173 return bhandler
->svc
.bucket
->store_bucket_entrypoint_info(ctx
, entry
,
2183 int RGWMetadataHandlerPut_Bucket::put_post(const DoutPrefixProvider
*dpp
)
2185 auto& be
= obj
->get_ep();
2191 ret
= bhandler
->ctl
.bucket
->link_bucket(be
.owner
, be
.bucket
, be
.creation_time
, y
, dpp
, false);
2193 ret
= bhandler
->ctl
.bucket
->unlink_bucket(be
.owner
, be
.bucket
, y
, dpp
, false);
2199 static void get_md5_digest(const RGWBucketEntryPoint
*be
, string
& md5_digest
) {
2201 char md5
[CEPH_CRYPTO_MD5_DIGESTSIZE
* 2 + 1];
2202 unsigned char m
[CEPH_CRYPTO_MD5_DIGESTSIZE
];
2205 Formatter
*f
= new JSONFormatter(false);
2210 hash
.Update((const unsigned char *)bl
.c_str(), bl
.length());
2213 buf_to_hex(m
, CEPH_CRYPTO_MD5_DIGESTSIZE
, md5
);
2220 #define ARCHIVE_META_ATTR RGW_ATTR_PREFIX "zone.archive.info"
2222 struct archive_meta_info
{
2223 rgw_bucket orig_bucket
;
2225 bool from_attrs(CephContext
*cct
, map
<string
, bufferlist
>& attrs
) {
2226 auto iter
= attrs
.find(ARCHIVE_META_ATTR
);
2227 if (iter
== attrs
.end()) {
2231 auto bliter
= iter
->second
.cbegin();
2234 } catch (buffer::error
& err
) {
2235 ldout(cct
, 0) << "ERROR: failed to decode archive meta info" << dendl
;
2242 void store_in_attrs(map
<string
, bufferlist
>& attrs
) const {
2243 encode(attrs
[ARCHIVE_META_ATTR
]);
2246 void encode(bufferlist
& bl
) const {
2247 ENCODE_START(1, 1, bl
);
2248 encode(orig_bucket
, bl
);
2252 void decode(bufferlist::const_iterator
& bl
) {
2253 DECODE_START(1, bl
);
2254 decode(orig_bucket
, bl
);
2258 WRITE_CLASS_ENCODER(archive_meta_info
)
2260 class RGWArchiveBucketMetadataHandler
: public RGWBucketMetadataHandler
{
2262 RGWArchiveBucketMetadataHandler() {}
2264 int do_remove(RGWSI_MetaBackend_Handler::Op
*op
, string
& entry
, RGWObjVersionTracker
& objv_tracker
,
2265 optional_yield y
, const DoutPrefixProvider
*dpp
) override
{
2266 auto cct
= svc
.bucket
->ctx();
2268 RGWSI_Bucket_EP_Ctx
ctx(op
->ctx());
2270 ldpp_dout(dpp
, 5) << "SKIP: bucket removal is not allowed on archive zone: bucket:" << entry
<< " ... proceeding to rename" << dendl
;
2272 string tenant_name
, bucket_name
;
2273 parse_bucket(entry
, &tenant_name
, &bucket_name
);
2274 rgw_bucket entry_bucket
;
2275 entry_bucket
.tenant
= tenant_name
;
2276 entry_bucket
.name
= bucket_name
;
2280 /* read original entrypoint */
2282 RGWBucketEntryPoint be
;
2283 map
<string
, bufferlist
> attrs
;
2284 int ret
= svc
.bucket
->read_bucket_entrypoint_info(ctx
, entry
, &be
, &objv_tracker
, &mtime
, &attrs
, y
, dpp
);
2289 string bi_meta_name
= RGWSI_Bucket::get_bi_meta_key(be
.bucket
);
2291 /* read original bucket instance info */
2293 map
<string
, bufferlist
> attrs_m
;
2294 ceph::real_time orig_mtime
;
2295 RGWBucketInfo old_bi
;
2297 ret
= ctl
.bucket
->read_bucket_instance_info(be
.bucket
, &old_bi
, y
, dpp
, RGWBucketCtl::BucketInstance::GetParams()
2298 .set_mtime(&orig_mtime
)
2299 .set_attrs(&attrs_m
));
2304 archive_meta_info ami
;
2306 if (!ami
.from_attrs(svc
.bucket
->ctx(), attrs_m
)) {
2307 ami
.orig_bucket
= old_bi
.bucket
;
2308 ami
.store_in_attrs(attrs_m
);
2311 /* generate a new bucket instance. We could have avoided this if we could just point a new
2312 * bucket entry point to the old bucket instance, however, due to limitation in the way
2313 * we index buckets under the user, bucket entrypoint and bucket instance of the same
2314 * bucket need to have the same name, so we need to copy the old bucket instance into
2315 * to a new entry with the new name
2318 string new_bucket_name
;
2320 RGWBucketInfo new_bi
= old_bi
;
2321 RGWBucketEntryPoint new_be
= be
;
2325 get_md5_digest(&new_be
, md5_digest
);
2326 new_bucket_name
= ami
.orig_bucket
.name
+ "-deleted-" + md5_digest
;
2328 new_bi
.bucket
.name
= new_bucket_name
;
2329 new_bi
.objv_tracker
.clear();
2331 new_be
.bucket
.name
= new_bucket_name
;
2333 ret
= ctl
.bucket
->store_bucket_instance_info(be
.bucket
, new_bi
, y
, dpp
, RGWBucketCtl::BucketInstance::PutParams()
2334 .set_exclusive(false)
2335 .set_mtime(orig_mtime
)
2336 .set_attrs(&attrs_m
)
2337 .set_orig_info(&old_bi
));
2339 ldpp_dout(dpp
, 0) << "ERROR: failed to put new bucket instance info for bucket=" << new_bi
.bucket
<< " ret=" << ret
<< dendl
;
2343 /* store a new entrypoint */
2345 RGWObjVersionTracker ot
;
2346 ot
.generate_new_write_ver(cct
);
2348 ret
= svc
.bucket
->store_bucket_entrypoint_info(ctx
, RGWSI_Bucket::get_entrypoint_meta_key(new_be
.bucket
),
2349 new_be
, true, mtime
, &attrs
, nullptr, y
, dpp
);
2351 ldpp_dout(dpp
, 0) << "ERROR: failed to put new bucket entrypoint for bucket=" << new_be
.bucket
<< " ret=" << ret
<< dendl
;
2355 /* link new bucket */
2357 ret
= ctl
.bucket
->link_bucket(new_be
.owner
, new_be
.bucket
, new_be
.creation_time
, y
, dpp
, false);
2359 ldpp_dout(dpp
, 0) << "ERROR: failed to link new bucket for bucket=" << new_be
.bucket
<< " ret=" << ret
<< dendl
;
2363 /* clean up old stuff */
2365 ret
= ctl
.bucket
->unlink_bucket(be
.owner
, entry_bucket
, y
, dpp
, false);
2367 ldpp_dout(dpp
, -1) << "could not unlink bucket=" << entry
<< " owner=" << be
.owner
<< dendl
;
2370 // if (ret == -ECANCELED) it means that there was a race here, and someone
2371 // wrote to the bucket entrypoint just before we removed it. The question is
2372 // whether it was a newly created bucket entrypoint ... in which case we
2373 // should ignore the error and move forward, or whether it is a higher version
2374 // of the same bucket instance ... in which we should retry
2375 ret
= svc
.bucket
->remove_bucket_entrypoint_info(ctx
,
2376 RGWSI_Bucket::get_entrypoint_meta_key(be
.bucket
),
2381 ldpp_dout(dpp
, 0) << "ERROR: failed to put new bucket entrypoint for bucket=" << new_be
.bucket
<< " ret=" << ret
<< dendl
;
2385 ret
= ctl
.bucket
->remove_bucket_instance_info(be
.bucket
, old_bi
, y
, dpp
);
2387 ldpp_dout(dpp
, -1) << "could not delete bucket=" << entry
<< dendl
;
2396 int do_put(RGWSI_MetaBackend_Handler::Op
*op
, string
& entry
,
2397 RGWMetadataObject
*obj
,
2398 RGWObjVersionTracker
& objv_tracker
,
2399 optional_yield y
, const DoutPrefixProvider
*dpp
,
2400 RGWMDLogSyncType type
, bool from_remote_zone
) override
{
2401 if (entry
.find("-deleted-") != string::npos
) {
2402 RGWObjVersionTracker ot
;
2403 RGWMetadataObject
*robj
;
2404 int ret
= do_get(op
, entry
, &robj
, y
, dpp
);
2405 if (ret
!= -ENOENT
) {
2409 ot
.read_version
= robj
->get_version();
2412 ret
= do_remove(op
, entry
, ot
, y
, dpp
);
2419 return RGWBucketMetadataHandler::do_put(op
, entry
, obj
,
2420 objv_tracker
, y
, dpp
, type
, from_remote_zone
);
2425 class RGWBucketInstanceMetadataHandler
: public RGWBucketInstanceMetadataHandlerBase
{
2426 int read_bucket_instance_entry(RGWSI_Bucket_BI_Ctx
& ctx
,
2427 const string
& entry
,
2428 RGWBucketCompleteInfo
*bi
,
2429 ceph::real_time
*pmtime
,
2431 const DoutPrefixProvider
*dpp
) {
2432 return svc
.bucket
->read_bucket_instance_info(ctx
,
2442 RGWSI_Zone
*zone
{nullptr};
2443 RGWSI_Bucket
*bucket
{nullptr};
2444 RGWSI_BucketIndex
*bi
{nullptr};
2447 RGWBucketInstanceMetadataHandler() {}
2449 void init(RGWSI_Zone
*zone_svc
,
2450 RGWSI_Bucket
*bucket_svc
,
2451 RGWSI_BucketIndex
*bi_svc
) override
{
2452 base_init(bucket_svc
->ctx(),
2453 bucket_svc
->get_bi_be_handler().get());
2454 svc
.zone
= zone_svc
;
2455 svc
.bucket
= bucket_svc
;
2459 string
get_type() override
{ return "bucket.instance"; }
2461 RGWMetadataObject
*get_meta_obj(JSONObj
*jo
, const obj_version
& objv
, const ceph::real_time
& mtime
) override
{
2462 RGWBucketCompleteInfo bci
;
2465 decode_json_obj(bci
, jo
);
2466 } catch (JSONDecoder::err
& e
) {
2470 return new RGWBucketInstanceMetadataObject(bci
, objv
, mtime
);
2473 int do_get(RGWSI_MetaBackend_Handler::Op
*op
, string
& entry
, RGWMetadataObject
**obj
, optional_yield y
, const DoutPrefixProvider
*dpp
) override
{
2474 RGWBucketCompleteInfo bci
;
2477 RGWSI_Bucket_BI_Ctx
ctx(op
->ctx());
2479 int ret
= svc
.bucket
->read_bucket_instance_info(ctx
, entry
, &bci
.info
, &mtime
, &bci
.attrs
, y
, dpp
);
2483 RGWBucketInstanceMetadataObject
*mdo
= new RGWBucketInstanceMetadataObject(bci
, bci
.info
.objv_tracker
.read_version
, mtime
);
2490 int do_put(RGWSI_MetaBackend_Handler::Op
*op
, string
& entry
,
2491 RGWMetadataObject
*_obj
, RGWObjVersionTracker
& objv_tracker
,
2492 optional_yield y
, const DoutPrefixProvider
*dpp
,
2493 RGWMDLogSyncType sync_type
, bool from_remote_zone
) override
;
2495 int do_remove(RGWSI_MetaBackend_Handler::Op
*op
, string
& entry
, RGWObjVersionTracker
& objv_tracker
,
2496 optional_yield y
, const DoutPrefixProvider
*dpp
) override
{
2497 RGWBucketCompleteInfo bci
;
2499 RGWSI_Bucket_BI_Ctx
ctx(op
->ctx());
2501 int ret
= read_bucket_instance_entry(ctx
, entry
, &bci
, nullptr, y
, dpp
);
2502 if (ret
< 0 && ret
!= -ENOENT
)
2505 return svc
.bucket
->remove_bucket_instance_info(ctx
, entry
, bci
.info
, &bci
.info
.objv_tracker
, y
, dpp
);
2508 int call(std::function
<int(RGWSI_Bucket_BI_Ctx
& ctx
)> f
) {
2509 return call(nullopt
, f
);
2512 int call(std::optional
<RGWSI_MetaBackend_CtxParams
> bectx_params
,
2513 std::function
<int(RGWSI_Bucket_BI_Ctx
& ctx
)> f
) {
2514 return be_handler
->call(bectx_params
, [&](RGWSI_MetaBackend_Handler::Op
*op
) {
2515 RGWSI_Bucket_BI_Ctx
ctx(op
->ctx());
2521 class RGWMetadataHandlerPut_BucketInstance
: public RGWMetadataHandlerPut_SObj
2524 RGWBucketInstanceMetadataHandler
*bihandler
;
2525 RGWBucketInstanceMetadataObject
*obj
;
2527 RGWMetadataHandlerPut_BucketInstance(CephContext
*_cct
,
2528 RGWBucketInstanceMetadataHandler
*_handler
,
2529 RGWSI_MetaBackend_Handler::Op
*_op
, string
& entry
,
2530 RGWMetadataObject
*_obj
, RGWObjVersionTracker
& objv_tracker
,
2532 RGWMDLogSyncType type
, bool from_remote_zone
) : RGWMetadataHandlerPut_SObj(_handler
, _op
, entry
, obj
, objv_tracker
, y
, type
, from_remote_zone
),
2533 cct(_cct
), bihandler(_handler
) {
2534 obj
= static_cast<RGWBucketInstanceMetadataObject
*>(_obj
);
2536 auto& bci
= obj
->get_bci();
2537 obj
->set_pattrs(&bci
.attrs
);
2540 void encode_obj(bufferlist
*bl
) override
{
2541 obj
->get_bucket_info().encode(*bl
);
2544 int put_check(const DoutPrefixProvider
*dpp
) override
;
2545 int put_checked(const DoutPrefixProvider
*dpp
) override
;
2546 int put_post(const DoutPrefixProvider
*dpp
) override
;
2549 int RGWBucketInstanceMetadataHandler::do_put(RGWSI_MetaBackend_Handler::Op
*op
,
2551 RGWMetadataObject
*obj
,
2552 RGWObjVersionTracker
& objv_tracker
,
2554 const DoutPrefixProvider
*dpp
,
2555 RGWMDLogSyncType type
, bool from_remote_zone
)
2557 RGWMetadataHandlerPut_BucketInstance
put_op(svc
.bucket
->ctx(), this, op
, entry
, obj
,
2558 objv_tracker
, y
, type
, from_remote_zone
);
2559 return do_put_operate(&put_op
, dpp
);
2562 void init_default_bucket_layout(CephContext
*cct
, rgw::BucketLayout
& layout
,
2563 const RGWZone
& zone
,
2564 std::optional
<uint32_t> shards
,
2565 std::optional
<rgw::BucketIndexType
> type
) {
2566 layout
.current_index
.gen
= 0;
2567 layout
.current_index
.layout
.normal
.hash_type
= rgw::BucketHashType::Mod
;
2569 layout
.current_index
.layout
.type
=
2570 type
.value_or(rgw::BucketIndexType::Normal
);
2573 layout
.current_index
.layout
.normal
.num_shards
= *shards
;
2574 } else if (cct
->_conf
->rgw_override_bucket_index_max_shards
> 0) {
2575 layout
.current_index
.layout
.normal
.num_shards
=
2576 cct
->_conf
->rgw_override_bucket_index_max_shards
;
2578 layout
.current_index
.layout
.normal
.num_shards
=
2579 zone
.bucket_index_max_shards
;
2582 if (layout
.current_index
.layout
.type
== rgw::BucketIndexType::Normal
) {
2583 layout
.logs
.push_back(log_layout_from_index(
2584 layout
.current_index
.gen
,
2585 layout
.current_index
.layout
.normal
));
2589 int RGWMetadataHandlerPut_BucketInstance::put_check(const DoutPrefixProvider
*dpp
)
2593 RGWBucketCompleteInfo
& bci
= obj
->get_bci();
2595 RGWBucketInstanceMetadataObject
*orig_obj
= static_cast<RGWBucketInstanceMetadataObject
*>(old_obj
);
2597 RGWBucketCompleteInfo
*old_bci
= (orig_obj
? &orig_obj
->get_bci() : nullptr);
2599 const bool exists
= (!!orig_obj
);
2601 if (from_remote_zone
) {
2602 // don't sync bucket layout changes
2604 auto& bci_index
= bci
.info
.layout
.current_index
.layout
;
2605 auto index_type
= bci_index
.type
;
2606 auto num_shards
= bci_index
.normal
.num_shards
;
2607 init_default_bucket_layout(cct
, bci
.info
.layout
,
2608 bihandler
->svc
.zone
->get_zone(),
2609 num_shards
, index_type
);
2611 bci
.info
.layout
= old_bci
->info
.layout
;
2615 if (!exists
|| old_bci
->info
.bucket
.bucket_id
!= bci
.info
.bucket
.bucket_id
) {
2616 /* a new bucket, we need to select a new bucket placement for it */
2619 string bucket_instance
;
2620 parse_bucket(entry
, &tenant_name
, &bucket_name
, &bucket_instance
);
2622 RGWZonePlacementInfo rule_info
;
2623 bci
.info
.bucket
.name
= bucket_name
;
2624 bci
.info
.bucket
.bucket_id
= bucket_instance
;
2625 bci
.info
.bucket
.tenant
= tenant_name
;
2626 // if the sync module never writes data, don't require the zone to specify all placement targets
2627 if (bihandler
->svc
.zone
->sync_module_supports_writes()) {
2628 ret
= bihandler
->svc
.zone
->select_bucket_location_by_rule(dpp
, bci
.info
.placement_rule
, &rule_info
, y
);
2630 ldpp_dout(dpp
, 0) << "ERROR: select_bucket_placement() returned " << ret
<< dendl
;
2634 bci
.info
.layout
.current_index
.layout
.type
= rule_info
.index_type
;
2636 /* existing bucket, keep its placement */
2637 bci
.info
.bucket
.explicit_placement
= old_bci
->info
.bucket
.explicit_placement
;
2638 bci
.info
.placement_rule
= old_bci
->info
.placement_rule
;
2641 /* record the read version (if any), store the new version */
2642 bci
.info
.objv_tracker
.read_version
= objv_tracker
.read_version
;
2643 bci
.info
.objv_tracker
.write_version
= objv_tracker
.write_version
;
2648 int RGWMetadataHandlerPut_BucketInstance::put_checked(const DoutPrefixProvider
*dpp
)
2650 RGWBucketInstanceMetadataObject
*orig_obj
= static_cast<RGWBucketInstanceMetadataObject
*>(old_obj
);
2652 RGWBucketInfo
*orig_info
= (orig_obj
? &orig_obj
->get_bucket_info() : nullptr);
2654 auto& info
= obj
->get_bucket_info();
2655 auto mtime
= obj
->get_mtime();
2656 auto pattrs
= obj
->get_pattrs();
2658 RGWSI_Bucket_BI_Ctx
ctx(op
->ctx());
2660 return bihandler
->svc
.bucket
->store_bucket_instance_info(ctx
,
2671 int RGWMetadataHandlerPut_BucketInstance::put_post(const DoutPrefixProvider
*dpp
)
2673 RGWBucketCompleteInfo
& bci
= obj
->get_bci();
2675 objv_tracker
= bci
.info
.objv_tracker
;
2677 int ret
= bihandler
->svc
.bi
->init_index(dpp
, bci
.info
);
2682 return STATUS_APPLIED
;
2685 class RGWArchiveBucketInstanceMetadataHandler
: public RGWBucketInstanceMetadataHandler
{
2687 RGWArchiveBucketInstanceMetadataHandler() {}
2689 int do_remove(RGWSI_MetaBackend_Handler::Op
*op
, string
& entry
, RGWObjVersionTracker
& objv_tracker
, optional_yield y
, const DoutPrefixProvider
*dpp
) override
{
2690 ldpp_dout(dpp
, 0) << "SKIP: bucket instance removal is not allowed on archive zone: bucket.instance:" << entry
<< dendl
;
2695 RGWBucketCtl::RGWBucketCtl(RGWSI_Zone
*zone_svc
,
2696 RGWSI_Bucket
*bucket_svc
,
2697 RGWSI_Bucket_Sync
*bucket_sync_svc
,
2698 RGWSI_BucketIndex
*bi_svc
) : cct(zone_svc
->ctx())
2700 svc
.zone
= zone_svc
;
2701 svc
.bucket
= bucket_svc
;
2702 svc
.bucket_sync
= bucket_sync_svc
;
2706 void RGWBucketCtl::init(RGWUserCtl
*user_ctl
,
2707 RGWBucketMetadataHandler
*_bm_handler
,
2708 RGWBucketInstanceMetadataHandler
*_bmi_handler
,
2709 RGWDataChangesLog
*datalog
,
2710 const DoutPrefixProvider
*dpp
)
2712 ctl
.user
= user_ctl
;
2714 bm_handler
= _bm_handler
;
2715 bmi_handler
= _bmi_handler
;
2717 bucket_be_handler
= bm_handler
->get_be_handler();
2718 bi_be_handler
= bmi_handler
->get_be_handler();
2720 datalog
->set_bucket_filter(
2721 [this](const rgw_bucket
& bucket
, optional_yield y
, const DoutPrefixProvider
*dpp
) {
2722 return bucket_exports_data(bucket
, y
, dpp
);
2726 int RGWBucketCtl::call(std::function
<int(RGWSI_Bucket_X_Ctx
& ctx
)> f
) {
2727 return bm_handler
->call([&](RGWSI_Bucket_EP_Ctx
& ep_ctx
) {
2728 return bmi_handler
->call([&](RGWSI_Bucket_BI_Ctx
& bi_ctx
) {
2729 RGWSI_Bucket_X_Ctx ctx
{ep_ctx
, bi_ctx
};
2735 int RGWBucketCtl::read_bucket_entrypoint_info(const rgw_bucket
& bucket
,
2736 RGWBucketEntryPoint
*info
,
2737 optional_yield y
, const DoutPrefixProvider
*dpp
,
2738 const Bucket::GetParams
& params
)
2740 return bm_handler
->call(params
.bectx_params
, [&](RGWSI_Bucket_EP_Ctx
& ctx
) {
2741 return svc
.bucket
->read_bucket_entrypoint_info(ctx
,
2742 RGWSI_Bucket::get_entrypoint_meta_key(bucket
),
2744 params
.objv_tracker
,
2750 params
.refresh_version
);
2754 int RGWBucketCtl::store_bucket_entrypoint_info(const rgw_bucket
& bucket
,
2755 RGWBucketEntryPoint
& info
,
2757 const DoutPrefixProvider
*dpp
,
2758 const Bucket::PutParams
& params
)
2760 return bm_handler
->call([&](RGWSI_Bucket_EP_Ctx
& ctx
) {
2761 return svc
.bucket
->store_bucket_entrypoint_info(ctx
,
2762 RGWSI_Bucket::get_entrypoint_meta_key(bucket
),
2767 params
.objv_tracker
,
2773 int RGWBucketCtl::remove_bucket_entrypoint_info(const rgw_bucket
& bucket
,
2775 const DoutPrefixProvider
*dpp
,
2776 const Bucket::RemoveParams
& params
)
2778 return bm_handler
->call([&](RGWSI_Bucket_EP_Ctx
& ctx
) {
2779 return svc
.bucket
->remove_bucket_entrypoint_info(ctx
,
2780 RGWSI_Bucket::get_entrypoint_meta_key(bucket
),
2781 params
.objv_tracker
,
2787 int RGWBucketCtl::read_bucket_instance_info(const rgw_bucket
& bucket
,
2788 RGWBucketInfo
*info
,
2790 const DoutPrefixProvider
*dpp
,
2791 const BucketInstance::GetParams
& params
)
2793 int ret
= bmi_handler
->call(params
.bectx_params
, [&](RGWSI_Bucket_BI_Ctx
& ctx
) {
2794 return svc
.bucket
->read_bucket_instance_info(ctx
,
2795 RGWSI_Bucket::get_bi_meta_key(bucket
),
2802 params
.refresh_version
);
2809 if (params
.objv_tracker
) {
2810 *params
.objv_tracker
= info
->objv_tracker
;
2816 int RGWBucketCtl::read_bucket_info(const rgw_bucket
& bucket
,
2817 RGWBucketInfo
*info
,
2819 const DoutPrefixProvider
*dpp
,
2820 const BucketInstance::GetParams
& params
,
2821 RGWObjVersionTracker
*ep_objv_tracker
)
2823 const rgw_bucket
*b
= &bucket
;
2825 std::optional
<RGWBucketEntryPoint
> ep
;
2827 if (b
->bucket_id
.empty()) {
2830 int r
= read_bucket_entrypoint_info(*b
, &(*ep
), y
, dpp
, RGWBucketCtl::Bucket::GetParams()
2831 .set_bectx_params(params
.bectx_params
)
2832 .set_objv_tracker(ep_objv_tracker
));
2840 int ret
= bmi_handler
->call(params
.bectx_params
, [&](RGWSI_Bucket_BI_Ctx
& ctx
) {
2841 return svc
.bucket
->read_bucket_instance_info(ctx
,
2842 RGWSI_Bucket::get_bi_meta_key(*b
),
2848 params
.refresh_version
);
2855 if (params
.objv_tracker
) {
2856 *params
.objv_tracker
= info
->objv_tracker
;
2862 int RGWBucketCtl::do_store_bucket_instance_info(RGWSI_Bucket_BI_Ctx
& ctx
,
2863 const rgw_bucket
& bucket
,
2864 RGWBucketInfo
& info
,
2866 const DoutPrefixProvider
*dpp
,
2867 const BucketInstance::PutParams
& params
)
2869 if (params
.objv_tracker
) {
2870 info
.objv_tracker
= *params
.objv_tracker
;
2873 return svc
.bucket
->store_bucket_instance_info(ctx
,
2874 RGWSI_Bucket::get_bi_meta_key(bucket
),
2884 int RGWBucketCtl::store_bucket_instance_info(const rgw_bucket
& bucket
,
2885 RGWBucketInfo
& info
,
2887 const DoutPrefixProvider
*dpp
,
2888 const BucketInstance::PutParams
& params
)
2890 return bmi_handler
->call([&](RGWSI_Bucket_BI_Ctx
& ctx
) {
2891 return do_store_bucket_instance_info(ctx
, bucket
, info
, y
, dpp
, params
);
2895 int RGWBucketCtl::remove_bucket_instance_info(const rgw_bucket
& bucket
,
2896 RGWBucketInfo
& info
,
2898 const DoutPrefixProvider
*dpp
,
2899 const BucketInstance::RemoveParams
& params
)
2901 if (params
.objv_tracker
) {
2902 info
.objv_tracker
= *params
.objv_tracker
;
2905 return bmi_handler
->call([&](RGWSI_Bucket_BI_Ctx
& ctx
) {
2906 return svc
.bucket
->remove_bucket_instance_info(ctx
,
2907 RGWSI_Bucket::get_bi_meta_key(bucket
),
2915 int RGWBucketCtl::do_store_linked_bucket_info(RGWSI_Bucket_X_Ctx
& ctx
,
2916 RGWBucketInfo
& info
,
2917 RGWBucketInfo
*orig_info
,
2918 bool exclusive
, real_time mtime
,
2919 obj_version
*pep_objv
,
2920 map
<string
, bufferlist
> *pattrs
,
2921 bool create_entry_point
,
2922 optional_yield y
, const DoutPrefixProvider
*dpp
)
2924 bool create_head
= !info
.has_instance_obj
|| create_entry_point
;
2926 int ret
= svc
.bucket
->store_bucket_instance_info(ctx
.bi
,
2927 RGWSI_Bucket::get_bi_meta_key(info
.bucket
),
2938 return 0; /* done! */
2940 RGWBucketEntryPoint entry_point
;
2941 entry_point
.bucket
= info
.bucket
;
2942 entry_point
.owner
= info
.owner
;
2943 entry_point
.creation_time
= info
.creation_time
;
2944 entry_point
.linked
= true;
2945 RGWObjVersionTracker ot
;
2946 if (pep_objv
&& !pep_objv
->tag
.empty()) {
2947 ot
.write_version
= *pep_objv
;
2949 ot
.generate_new_write_ver(cct
);
2951 *pep_objv
= ot
.write_version
;
2954 ret
= svc
.bucket
->store_bucket_entrypoint_info(ctx
.ep
,
2955 RGWSI_Bucket::get_entrypoint_meta_key(info
.bucket
),
2968 int RGWBucketCtl::convert_old_bucket_info(RGWSI_Bucket_X_Ctx
& ctx
,
2969 const rgw_bucket
& bucket
,
2971 const DoutPrefixProvider
*dpp
)
2973 RGWBucketEntryPoint entry_point
;
2975 RGWObjVersionTracker ot
;
2976 map
<string
, bufferlist
> attrs
;
2978 auto cct
= svc
.bucket
->ctx();
2980 ldpp_dout(dpp
, 10) << "RGWRados::convert_old_bucket_info(): bucket=" << bucket
<< dendl
;
2982 int ret
= svc
.bucket
->read_bucket_entrypoint_info(ctx
.ep
,
2983 RGWSI_Bucket::get_entrypoint_meta_key(bucket
),
2984 &entry_point
, &ot
, &ep_mtime
, &attrs
, y
, dpp
);
2986 ldpp_dout(dpp
, 0) << "ERROR: get_bucket_entrypoint_info() returned " << ret
<< " bucket=" << bucket
<< dendl
;
2990 if (!entry_point
.has_bucket_info
) {
2991 /* already converted! */
2995 info
= entry_point
.old_bucket_info
;
2997 ot
.generate_new_write_ver(cct
);
2999 ret
= do_store_linked_bucket_info(ctx
, info
, nullptr, false, ep_mtime
, &ot
.write_version
, &attrs
, true, y
, dpp
);
3001 ldpp_dout(dpp
, 0) << "ERROR: failed to put_linked_bucket_info(): " << ret
<< dendl
;
3008 int RGWBucketCtl::set_bucket_instance_attrs(RGWBucketInfo
& bucket_info
,
3009 map
<string
, bufferlist
>& attrs
,
3010 RGWObjVersionTracker
*objv_tracker
,
3012 const DoutPrefixProvider
*dpp
)
3014 return call([&](RGWSI_Bucket_X_Ctx
& ctx
) {
3015 rgw_bucket
& bucket
= bucket_info
.bucket
;
3017 if (!bucket_info
.has_instance_obj
) {
3018 /* an old bucket object, need to convert it */
3019 int ret
= convert_old_bucket_info(ctx
, bucket
, y
, dpp
);
3021 ldpp_dout(dpp
, 0) << "ERROR: failed converting old bucket info: " << ret
<< dendl
;
3026 return do_store_bucket_instance_info(ctx
.bi
,
3031 BucketInstance::PutParams().set_attrs(&attrs
)
3032 .set_objv_tracker(objv_tracker
)
3033 .set_orig_info(&bucket_info
));
3038 int RGWBucketCtl::link_bucket(const rgw_user
& user_id
,
3039 const rgw_bucket
& bucket
,
3040 ceph::real_time creation_time
,
3042 const DoutPrefixProvider
*dpp
,
3043 bool update_entrypoint
,
3046 return bm_handler
->call([&](RGWSI_Bucket_EP_Ctx
& ctx
) {
3047 return do_link_bucket(ctx
, user_id
, bucket
, creation_time
,
3048 update_entrypoint
, pinfo
, y
, dpp
);
3052 int RGWBucketCtl::do_link_bucket(RGWSI_Bucket_EP_Ctx
& ctx
,
3053 const rgw_user
& user_id
,
3054 const rgw_bucket
& bucket
,
3055 ceph::real_time creation_time
,
3056 bool update_entrypoint
,
3059 const DoutPrefixProvider
*dpp
)
3063 RGWBucketEntryPoint ep
;
3064 RGWObjVersionTracker ot
;
3065 RGWObjVersionTracker
& rot
= (pinfo
) ? pinfo
->ep_objv
: ot
;
3066 map
<string
, bufferlist
> attrs
, *pattrs
= nullptr;
3069 if (update_entrypoint
) {
3070 meta_key
= RGWSI_Bucket::get_entrypoint_meta_key(bucket
);
3073 pattrs
= &pinfo
->attrs
;
3075 ret
= svc
.bucket
->read_bucket_entrypoint_info(ctx
,
3080 if (ret
< 0 && ret
!= -ENOENT
) {
3081 ldpp_dout(dpp
, 0) << "ERROR: store->get_bucket_entrypoint_info() returned: "
3082 << cpp_strerror(-ret
) << dendl
;
3088 ret
= ctl
.user
->add_bucket(dpp
, user_id
, bucket
, creation_time
, y
);
3090 ldpp_dout(dpp
, 0) << "ERROR: error adding bucket to user directory:"
3091 << " user=" << user_id
3092 << " bucket=" << bucket
3093 << " err=" << cpp_strerror(-ret
)
3098 if (!update_entrypoint
)
3104 ret
= svc
.bucket
->store_bucket_entrypoint_info(
3105 ctx
, meta_key
, ep
, false, real_time(), pattrs
, &rot
, y
, dpp
);
3112 int r
= do_unlink_bucket(ctx
, user_id
, bucket
, true, y
, dpp
);
3114 ldpp_dout(dpp
, 0) << "ERROR: failed unlinking bucket on error cleanup: "
3115 << cpp_strerror(-r
) << dendl
;
3120 int RGWBucketCtl::unlink_bucket(const rgw_user
& user_id
, const rgw_bucket
& bucket
, optional_yield y
, const DoutPrefixProvider
*dpp
, bool update_entrypoint
)
3122 return bm_handler
->call([&](RGWSI_Bucket_EP_Ctx
& ctx
) {
3123 return do_unlink_bucket(ctx
, user_id
, bucket
, update_entrypoint
, y
, dpp
);
3127 int RGWBucketCtl::do_unlink_bucket(RGWSI_Bucket_EP_Ctx
& ctx
,
3128 const rgw_user
& user_id
,
3129 const rgw_bucket
& bucket
,
3130 bool update_entrypoint
,
3132 const DoutPrefixProvider
*dpp
)
3134 int ret
= ctl
.user
->remove_bucket(dpp
, user_id
, bucket
, y
);
3136 ldpp_dout(dpp
, 0) << "ERROR: error removing bucket from directory: "
3137 << cpp_strerror(-ret
)<< dendl
;
3140 if (!update_entrypoint
)
3143 RGWBucketEntryPoint ep
;
3144 RGWObjVersionTracker ot
;
3145 map
<string
, bufferlist
> attrs
;
3146 string meta_key
= RGWSI_Bucket::get_entrypoint_meta_key(bucket
);
3147 ret
= svc
.bucket
->read_bucket_entrypoint_info(ctx
, meta_key
, &ep
, &ot
, nullptr, &attrs
, y
, dpp
);
3156 if (ep
.owner
!= user_id
) {
3157 ldpp_dout(dpp
, 0) << "bucket entry point user mismatch, can't unlink bucket: " << ep
.owner
<< " != " << user_id
<< dendl
;
3162 return svc
.bucket
->store_bucket_entrypoint_info(ctx
, meta_key
, ep
, false, real_time(), &attrs
, &ot
, y
, dpp
);
3165 int RGWBucketCtl::set_acl(ACLOwner
& owner
, rgw_bucket
& bucket
,
3166 RGWBucketInfo
& bucket_info
, bufferlist
& bl
,
3168 const DoutPrefixProvider
*dpp
)
3170 // set owner and acl
3171 bucket_info
.owner
= owner
.get_id();
3172 std::map
<std::string
, bufferlist
> attrs
{{RGW_ATTR_ACL
, bl
}};
3174 int r
= store_bucket_instance_info(bucket
, bucket_info
, y
, dpp
,
3175 BucketInstance::PutParams().set_attrs(&attrs
));
3177 cerr
<< "ERROR: failed to set bucket owner: " << cpp_strerror(-r
) << std::endl
;
3184 // TODO: remove RGWRados dependency for bucket listing
3185 int RGWBucketCtl::chown(rgw::sal::RGWRadosStore
*store
, RGWBucketInfo
& bucket_info
,
3186 const rgw_user
& user_id
, const std::string
& display_name
,
3187 const std::string
& marker
, optional_yield y
, const DoutPrefixProvider
*dpp
)
3189 RGWObjectCtx
obj_ctx(store
);
3190 std::vector
<rgw_bucket_dir_entry
> objs
;
3191 map
<string
, bool> common_prefixes
;
3193 RGWRados::Bucket
target(store
->getRados(), bucket_info
);
3194 RGWRados::Bucket::List
list_op(&target
);
3196 list_op
.params
.list_versions
= true;
3197 list_op
.params
.allow_unordered
= true;
3198 list_op
.params
.marker
= marker
;
3200 bool is_truncated
= false;
3202 int max_entries
= 1000;
3204 //Loop through objects and update object acls to point to bucket owner
3208 int ret
= list_op
.list_objects(dpp
, max_entries
, &objs
, &common_prefixes
, &is_truncated
, y
);
3210 ldpp_dout(dpp
, 0) << "ERROR: list objects failed: " << cpp_strerror(-ret
) << dendl
;
3214 list_op
.params
.marker
= list_op
.get_next_marker();
3215 count
+= objs
.size();
3217 for (const auto& obj
: objs
) {
3219 rgw_obj
r_obj(bucket_info
.bucket
, obj
.key
);
3220 RGWRados::Object
op_target(store
->getRados(), bucket_info
, obj_ctx
, r_obj
);
3221 RGWRados::Object::Read
read_op(&op_target
);
3223 map
<string
, bufferlist
> attrs
;
3224 read_op
.params
.attrs
= &attrs
;
3225 ret
= read_op
.prepare(y
, dpp
);
3227 ldpp_dout(dpp
, 0) << "ERROR: failed to read object " << obj
.key
.name
<< cpp_strerror(-ret
) << dendl
;
3230 const auto& aiter
= attrs
.find(RGW_ATTR_ACL
);
3231 if (aiter
== attrs
.end()) {
3232 ldpp_dout(dpp
, 0) << "ERROR: no acls found for object " << obj
.key
.name
<< " .Continuing with next object." << dendl
;
3235 bufferlist
& bl
= aiter
->second
;
3236 RGWAccessControlPolicy
policy(store
->ctx());
3240 owner
= policy
.get_owner();
3241 } catch (buffer::error
& err
) {
3242 ldpp_dout(dpp
, 0) << "ERROR: decode policy failed" << err
.what()
3247 //Get the ACL from the policy
3248 RGWAccessControlList
& acl
= policy
.get_acl();
3250 //Remove grant that is set to old owner
3251 acl
.remove_canon_user_grant(owner
.get_id());
3253 //Create a grant and add grant
3255 grant
.set_canon(user_id
, display_name
, RGW_PERM_FULL_CONTROL
);
3256 acl
.add_grant(&grant
);
3258 //Update the ACL owner to the new user
3259 owner
.set_id(user_id
);
3260 owner
.set_name(display_name
);
3261 policy
.set_owner(owner
);
3266 obj_ctx
.set_atomic(r_obj
);
3267 ret
= store
->getRados()->set_attr(dpp
, &obj_ctx
, bucket_info
, r_obj
, RGW_ATTR_ACL
, bl
);
3269 ldpp_dout(dpp
, 0) << "ERROR: modify attr failed " << cpp_strerror(-ret
) << dendl
;
3274 cerr
<< count
<< " objects processed in " << bucket_info
.bucket
.name
3275 << ". Next marker " << list_op
.params
.marker
.name
<< std::endl
;
3276 } while(is_truncated
);
3280 int RGWBucketCtl::read_bucket_stats(const rgw_bucket
& bucket
,
3281 RGWBucketEnt
*result
,
3283 const DoutPrefixProvider
*dpp
)
3285 return call([&](RGWSI_Bucket_X_Ctx
& ctx
) {
3286 return svc
.bucket
->read_bucket_stats(ctx
, bucket
, result
, y
, dpp
);
3290 int RGWBucketCtl::read_buckets_stats(map
<string
, RGWBucketEnt
>& m
,
3291 optional_yield y
, const DoutPrefixProvider
*dpp
)
3293 return call([&](RGWSI_Bucket_X_Ctx
& ctx
) {
3294 return svc
.bucket
->read_buckets_stats(ctx
, m
, y
, dpp
);
3298 int RGWBucketCtl::sync_user_stats(const DoutPrefixProvider
*dpp
,
3299 const rgw_user
& user_id
,
3300 const RGWBucketInfo
& bucket_info
,
3308 int r
= svc
.bi
->read_stats(dpp
, bucket_info
, pent
, null_yield
);
3310 ldpp_dout(dpp
, 20) << __func__
<< "(): failed to read bucket stats (r=" << r
<< ")" << dendl
;
3314 return ctl
.user
->flush_bucket_stats(dpp
, user_id
, *pent
, y
);
3317 int RGWBucketCtl::get_sync_policy_handler(std::optional
<rgw_zone_id
> zone
,
3318 std::optional
<rgw_bucket
> bucket
,
3319 RGWBucketSyncPolicyHandlerRef
*phandler
,
3321 const DoutPrefixProvider
*dpp
)
3323 int r
= call([&](RGWSI_Bucket_X_Ctx
& ctx
) {
3324 return svc
.bucket_sync
->get_policy_handler(ctx
, zone
, bucket
, phandler
, y
, dpp
);
3327 ldpp_dout(dpp
, 20) << __func__
<< "(): failed to get policy handler for bucket=" << bucket
<< " (r=" << r
<< ")" << dendl
;
3333 int RGWBucketCtl::bucket_exports_data(const rgw_bucket
& bucket
,
3335 const DoutPrefixProvider
*dpp
)
3338 RGWBucketSyncPolicyHandlerRef handler
;
3340 int r
= get_sync_policy_handler(std::nullopt
, bucket
, &handler
, y
, dpp
);
3345 return handler
->bucket_exports_data();
3348 int RGWBucketCtl::bucket_imports_data(const rgw_bucket
& bucket
,
3349 optional_yield y
, const DoutPrefixProvider
*dpp
)
3352 RGWBucketSyncPolicyHandlerRef handler
;
3354 int r
= get_sync_policy_handler(std::nullopt
, bucket
, &handler
, y
, dpp
);
3359 return handler
->bucket_imports_data();
3362 RGWBucketMetadataHandlerBase
*RGWBucketMetaHandlerAllocator::alloc()
3364 return new RGWBucketMetadataHandler();
3367 RGWBucketInstanceMetadataHandlerBase
*RGWBucketInstanceMetaHandlerAllocator::alloc()
3369 return new RGWBucketInstanceMetadataHandler();
3372 RGWBucketMetadataHandlerBase
*RGWArchiveBucketMetaHandlerAllocator::alloc()
3374 return new RGWArchiveBucketMetadataHandler();
3377 RGWBucketInstanceMetadataHandlerBase
*RGWArchiveBucketInstanceMetaHandlerAllocator::alloc()
3379 return new RGWArchiveBucketInstanceMetadataHandler();