]> git.proxmox.com Git - ceph.git/blame - ceph/src/rgw/rgw_bucket.cc
import ceph quincy 17.2.6
[ceph.git] / ceph / src / rgw / rgw_bucket.cc
CommitLineData
7c673cae 1// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
9f95a23c 2// vim: ts=8 sw=2 smarttab ft=cpp
7c673cae 3
f67539c2 4#include <cerrno>
7c673cae
FG
5#include <map>
6#include <sstream>
f67539c2
TL
7#include <string>
8#include <string_view>
7c673cae 9
7c673cae
FG
10#include <boost/format.hpp>
11
12#include "common/errno.h"
13#include "common/ceph_json.h"
f64942e4 14#include "include/scope_guard.h"
9f95a23c 15
f67539c2 16#include "rgw_datalog.h"
7c673cae 17#include "rgw_rados.h"
11fdf7f2 18#include "rgw_zone.h"
7c673cae
FG
19#include "rgw_acl.h"
20#include "rgw_acl_s3.h"
9f95a23c 21#include "rgw_tag_s3.h"
7c673cae
FG
22
23#include "include/types.h"
24#include "rgw_bucket.h"
25#include "rgw_user.h"
26#include "rgw_string.h"
224ce89b 27#include "rgw_multi.h"
9f95a23c
TL
28#include "rgw_op.h"
29#include "rgw_bucket_sync.h"
7c673cae 30
11fdf7f2
TL
31#include "services/svc_zone.h"
32#include "services/svc_sys_obj.h"
9f95a23c
TL
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"
11fdf7f2 40
7c673cae
FG
41#include "include/rados/librados.hpp"
42// until everything is moved from rgw_common
43#include "rgw_common.h"
f64942e4 44#include "rgw_reshard.h"
11fdf7f2 45#include "rgw_lc.h"
f67539c2 46#include "rgw_bucket_layout.h"
39ae355f 47#include "common/errno.h"
9f95a23c
TL
48
49// stolen from src/cls/version/cls_version.cc
50#define VERSION_ATTR "ceph.objclass.version"
51
7c673cae
FG
52#include "cls/user/cls_user_types.h"
53
f67539c2 54#include "rgw_sal_rados.h"
9f95a23c 55
7c673cae
FG
56#define dout_context g_ceph_context
57#define dout_subsys ceph_subsys_rgw
58
59#define BUCKET_TAG_TIMEOUT 30
60
20effc67
TL
61using namespace std;
62
9f95a23c
TL
63// default number of entries to list with each bucket listing call
64// (use marker to bridge between calls)
65static constexpr size_t listing_max_entries = 1000;
7c673cae 66
20effc67
TL
67void init_bucket(rgw_bucket *b, const char *t, const char *n, const char *dp, const char *ip, const char *m, const char *id)
68{
69 b->tenant = t;
70 b->name = n;
71 b->marker = m;
72 b->bucket_id = id;
73 b->explicit_placement.data_pool = rgw_pool(dp);
74 b->explicit_placement.index_pool = rgw_pool(ip);
75}
7c673cae 76
9f95a23c
TL
77/*
78 * The tenant_name is always returned on purpose. May be empty, of course.
79 */
80static void parse_bucket(const string& bucket,
81 string *tenant_name,
82 string *bucket_name,
83 string *bucket_instance = nullptr /* optional */)
7c673cae 84{
9f95a23c
TL
85 /*
86 * expected format: [tenant/]bucket:bucket_instance
87 */
88 int pos = bucket.find('/');
89 if (pos >= 0) {
90 *tenant_name = bucket.substr(0, pos);
91 } else {
92 tenant_name->clear();
93 }
94 string bn = bucket.substr(pos + 1);
95 pos = bn.find (':');
96 if (pos < 0) {
97 *bucket_name = std::move(bn);
98 return;
99 }
100 *bucket_name = bn.substr(0, pos);
101 if (bucket_instance) {
102 *bucket_instance = bn.substr(pos + 1);
103 }
104
105 /*
106 * deal with the possible tenant:bucket:bucket_instance case
107 */
108 if (tenant_name->empty()) {
109 pos = bucket_instance->find(':');
110 if (pos >= 0) {
111 *tenant_name = *bucket_name;
112 *bucket_name = bucket_instance->substr(0, pos);
113 *bucket_instance = bucket_instance->substr(pos + 1);
114 }
115 }
7c673cae
FG
116}
117
118/*
119 * Note that this is not a reversal of parse_bucket(). That one deals
120 * with the syntax we need in metadata and such. This one deals with
121 * the representation in RADOS pools. We chose '/' because it's not
122 * acceptable in bucket names and thus qualified buckets cannot conflict
123 * with the legacy or S3 buckets.
124 */
125std::string rgw_make_bucket_entry_name(const std::string& tenant_name,
126 const std::string& bucket_name) {
127 std::string bucket_entry;
128
129 if (bucket_name.empty()) {
130 bucket_entry.clear();
131 } else if (tenant_name.empty()) {
132 bucket_entry = bucket_name;
133 } else {
134 bucket_entry = tenant_name + "/" + bucket_name;
135 }
136
137 return bucket_entry;
138}
139
140/*
141 * Tenants are separated from buckets in URLs by a colon in S3.
142 * This function is not to be used on Swift URLs, not even for COPY arguments.
143 */
39ae355f
TL
144int rgw_parse_url_bucket(const string &bucket, const string& auth_tenant,
145 string &tenant_name, string &bucket_name) {
7c673cae
FG
146
147 int pos = bucket.find(':');
148 if (pos >= 0) {
149 /*
150 * N.B.: We allow ":bucket" syntax with explicit empty tenant in order
151 * to refer to the legacy tenant, in case users in new named tenants
152 * want to access old global buckets.
153 */
154 tenant_name = bucket.substr(0, pos);
155 bucket_name = bucket.substr(pos + 1);
39ae355f
TL
156 if (bucket_name.empty()) {
157 return -ERR_INVALID_BUCKET_NAME;
158 }
7c673cae
FG
159 } else {
160 tenant_name = auth_tenant;
161 bucket_name = bucket;
162 }
39ae355f 163 return 0;
7c673cae
FG
164}
165
9f95a23c 166int rgw_bucket_parse_bucket_instance(const string& bucket_instance, string *bucket_name, string *bucket_id, int *shard_id)
7c673cae 167{
9f95a23c
TL
168 auto pos = bucket_instance.rfind(':');
169 if (pos == string::npos) {
7c673cae
FG
170 return -EINVAL;
171 }
172
173 string first = bucket_instance.substr(0, pos);
174 string second = bucket_instance.substr(pos + 1);
175
9f95a23c
TL
176 pos = first.find(':');
177
178 if (pos == string::npos) {
7c673cae 179 *shard_id = -1;
9f95a23c
TL
180 *bucket_name = first;
181 *bucket_id = second;
7c673cae
FG
182 return 0;
183 }
184
9f95a23c
TL
185 *bucket_name = first.substr(0, pos);
186 *bucket_id = first.substr(pos + 1);
187
7c673cae
FG
188 string err;
189 *shard_id = strict_strtol(second.c_str(), 10, &err);
190 if (!err.empty()) {
191 return -EINVAL;
192 }
193
194 return 0;
195}
196
197// parse key in format: [tenant/]name:instance[:shard_id]
198int rgw_bucket_parse_bucket_key(CephContext *cct, const string& key,
199 rgw_bucket *bucket, int *shard_id)
200{
f67539c2
TL
201 std::string_view name{key};
202 std::string_view instance;
7c673cae
FG
203
204 // split tenant/name
205 auto pos = name.find('/');
9f95a23c 206 if (pos != string::npos) {
7c673cae
FG
207 auto tenant = name.substr(0, pos);
208 bucket->tenant.assign(tenant.begin(), tenant.end());
209 name = name.substr(pos + 1);
1911f103
TL
210 } else {
211 bucket->tenant.clear();
7c673cae
FG
212 }
213
214 // split name:instance
215 pos = name.find(':');
9f95a23c 216 if (pos != string::npos) {
7c673cae
FG
217 instance = name.substr(pos + 1);
218 name = name.substr(0, pos);
219 }
220 bucket->name.assign(name.begin(), name.end());
221
222 // split instance:shard
223 pos = instance.find(':');
9f95a23c 224 if (pos == string::npos) {
7c673cae 225 bucket->bucket_id.assign(instance.begin(), instance.end());
9f95a23c
TL
226 if (shard_id) {
227 *shard_id = -1;
228 }
7c673cae
FG
229 return 0;
230 }
231
232 // parse shard id
233 auto shard = instance.substr(pos + 1);
234 string err;
235 auto id = strict_strtol(shard.data(), 10, &err);
236 if (!err.empty()) {
9f95a23c
TL
237 if (cct) {
238 ldout(cct, 0) << "ERROR: failed to parse bucket shard '"
239 << instance.data() << "': " << err << dendl;
240 }
7c673cae
FG
241 return -EINVAL;
242 }
243
9f95a23c
TL
244 if (shard_id) {
245 *shard_id = id;
246 }
7c673cae
FG
247 instance = instance.substr(0, pos);
248 bucket->bucket_id.assign(instance.begin(), instance.end());
249 return 0;
250}
251
7c673cae
FG
252static void dump_mulipart_index_results(list<rgw_obj_index_key>& objs_to_unlink,
253 Formatter *f)
254{
d2e6a577
FG
255 for (const auto& o : objs_to_unlink) {
256 f->dump_string("object", o.name);
7c673cae 257 }
7c673cae
FG
258}
259
39ae355f 260void check_bad_user_bucket_mapping(rgw::sal::Store* store, rgw::sal::User& user,
f67539c2 261 bool fix,
b3b6e05e
TL
262 optional_yield y,
263 const DoutPrefixProvider *dpp)
7c673cae 264{
20effc67 265 rgw::sal::BucketList user_buckets;
7c673cae
FG
266 string marker;
267
268 CephContext *cct = store->ctx();
269
270 size_t max_entries = cct->_conf->rgw_list_buckets_max_chunk;
271
272 do {
39ae355f 273 int ret = user.list_buckets(dpp, marker, string(), max_entries, false, user_buckets, y);
7c673cae
FG
274 if (ret < 0) {
275 ldout(store->ctx(), 0) << "failed to read user buckets: "
276 << cpp_strerror(-ret) << dendl;
277 return;
278 }
279
20effc67 280 map<string, std::unique_ptr<rgw::sal::Bucket>>& buckets = user_buckets.get_buckets();
f67539c2 281 for (auto i = buckets.begin();
7c673cae
FG
282 i != buckets.end();
283 ++i) {
284 marker = i->first;
285
f67539c2 286 auto& bucket = i->second;
7c673cae 287
20effc67 288 std::unique_ptr<rgw::sal::Bucket> actual_bucket;
39ae355f 289 int r = store->get_bucket(dpp, &user, user.get_tenant(), bucket->get_name(), &actual_bucket, null_yield);
7c673cae
FG
290 if (r < 0) {
291 ldout(store->ctx(), 0) << "could not get bucket info for bucket=" << bucket << dendl;
292 continue;
293 }
294
20effc67
TL
295 if (actual_bucket->get_name().compare(bucket->get_name()) != 0 ||
296 actual_bucket->get_tenant().compare(bucket->get_tenant()) != 0 ||
297 actual_bucket->get_marker().compare(bucket->get_marker()) != 0 ||
298 actual_bucket->get_bucket_id().compare(bucket->get_bucket_id()) != 0) {
7c673cae
FG
299 cout << "bucket info mismatch: expected " << actual_bucket << " got " << bucket << std::endl;
300 if (fix) {
301 cout << "fixing" << std::endl;
39ae355f 302 r = actual_bucket->chown(dpp, user, null_yield);
7c673cae
FG
303 if (r < 0) {
304 cerr << "failed to fix bucket: " << cpp_strerror(-r) << std::endl;
305 }
306 }
307 }
308 }
9f95a23c 309 } while (user_buckets.is_truncated());
7c673cae
FG
310}
311
20effc67
TL
312// returns true if entry is in the empty namespace. note: function
313// type conforms to type RGWBucketListNameFilter
314bool rgw_bucket_object_check_filter(const std::string& oid)
7c673cae 315{
20effc67
TL
316 const static std::string empty_ns;
317 rgw_obj_key key; // thrown away but needed for parsing
318 return rgw_obj_key::oid_to_key_in_ns(oid, &key, empty_ns);
7c673cae
FG
319}
320
20effc67 321int rgw_remove_object(const DoutPrefixProvider *dpp, rgw::sal::Store* store, rgw::sal::Bucket* bucket, rgw_obj_key& key)
7c673cae
FG
322{
323 RGWObjectCtx rctx(store);
324
325 if (key.instance.empty()) {
326 key.instance = "null";
327 }
328
20effc67 329 std::unique_ptr<rgw::sal::Object> object = bucket->get_object(key);
7c673cae 330
20effc67 331 return object->delete_object(dpp, &rctx, null_yield);
7c673cae
FG
332}
333
7c673cae
FG
334static void set_err_msg(std::string *sink, std::string msg)
335{
336 if (sink && !msg.empty())
337 *sink = msg;
338}
339
20effc67
TL
340int RGWBucket::init(rgw::sal::Store* _store, RGWBucketAdminOpState& op_state,
341 optional_yield y, const DoutPrefixProvider *dpp, std::string *err_msg)
7c673cae 342{
20effc67 343 if (!_store) {
9f95a23c 344 set_err_msg(err_msg, "no storage!");
7c673cae 345 return -EINVAL;
9f95a23c 346 }
7c673cae 347
20effc67 348 store = _store;
7c673cae 349
20effc67 350 std::string bucket_name = op_state.get_bucket_name();
7c673cae 351
20effc67 352 if (bucket_name.empty() && op_state.get_user_id().empty())
7c673cae 353 return -EINVAL;
20effc67
TL
354
355 user = store->get_user(op_state.get_user_id());
356 std::string tenant = user->get_tenant();
357
9f95a23c 358 // split possible tenant/name
20effc67 359 auto pos = bucket_name.find('/');
9f95a23c 360 if (pos != string::npos) {
20effc67
TL
361 tenant = bucket_name.substr(0, pos);
362 bucket_name = bucket_name.substr(pos + 1);
9f95a23c
TL
363 }
364
20effc67
TL
365 int r = store->get_bucket(dpp, user.get(), tenant, bucket_name, &bucket, y);
366 if (r < 0) {
367 set_err_msg(err_msg, "failed to fetch bucket info for bucket=" + bucket_name);
7c673cae 368 return r;
7c673cae
FG
369 }
370
20effc67
TL
371 op_state.set_bucket(bucket->clone());
372
373 if (!rgw::sal::User::empty(user.get())) {
374 r = user->load_user(dpp, y);
9f95a23c
TL
375 if (r < 0) {
376 set_err_msg(err_msg, "failed to fetch user info");
7c673cae 377 return r;
9f95a23c 378 }
7c673cae
FG
379 }
380
20effc67
TL
381 op_state.display_name = user->get_display_name();
382
7c673cae
FG
383 clear_failure();
384 return 0;
385}
386
20effc67 387bool rgw_find_bucket_by_id(const DoutPrefixProvider *dpp, CephContext *cct, rgw::sal::Store* store,
1911f103
TL
388 const string& marker, const string& bucket_id, rgw_bucket* bucket_out)
389{
390 void *handle = NULL;
391 bool truncated = false;
392 string s;
393
20effc67 394 int ret = store->meta_list_keys_init(dpp, "bucket.instance", marker, &handle);
1911f103
TL
395 if (ret < 0) {
396 cerr << "ERROR: can't get key: " << cpp_strerror(-ret) << std::endl;
20effc67 397 store->meta_list_keys_complete(handle);
1911f103
TL
398 return -ret;
399 }
400 do {
401 list<string> keys;
20effc67 402 ret = store->meta_list_keys_next(dpp, handle, 1000, keys, &truncated);
1911f103
TL
403 if (ret < 0) {
404 cerr << "ERROR: lists_keys_next(): " << cpp_strerror(-ret) << std::endl;
20effc67 405 store->meta_list_keys_complete(handle);
1911f103
TL
406 return -ret;
407 }
408 for (list<string>::iterator iter = keys.begin(); iter != keys.end(); ++iter) {
409 s = *iter;
410 ret = rgw_bucket_parse_bucket_key(cct, s, bucket_out, nullptr);
411 if (ret < 0) {
412 continue;
413 }
414 if (bucket_id == bucket_out->bucket_id) {
20effc67 415 store->meta_list_keys_complete(handle);
1911f103
TL
416 return true;
417 }
418 }
419 } while (truncated);
20effc67 420 store->meta_list_keys_complete(handle);
1911f103
TL
421 return false;
422}
423
9f95a23c 424int RGWBucket::chown(RGWBucketAdminOpState& op_state, const string& marker,
b3b6e05e 425 optional_yield y, const DoutPrefixProvider *dpp, std::string *err_msg)
9f95a23c 426{
39ae355f
TL
427 /* User passed in by rgw_admin is the new user; get the current user and set it in
428 * the bucket */
429 std::unique_ptr<rgw::sal::User> old_user = store->get_user(bucket->get_info().owner);
430 bucket->set_owner(old_user.get());
431
432 return rgw_chown_bucket_and_objects(store, bucket.get(), user.get(), marker, err_msg, dpp, y);
9f95a23c
TL
433}
434
b3b6e05e 435int RGWBucket::set_quota(RGWBucketAdminOpState& op_state, const DoutPrefixProvider *dpp, std::string *err_msg)
94b18763 436{
20effc67 437 bucket = op_state.get_bucket()->clone();
94b18763 438
20effc67
TL
439 bucket->get_info().quota = op_state.quota;
440 int r = bucket->put_info(dpp, false, real_time());
94b18763
FG
441 if (r < 0) {
442 set_err_msg(err_msg, "ERROR: failed writing bucket instance info: " + cpp_strerror(-r));
443 return r;
444 }
445 return r;
446}
447
b3b6e05e 448int RGWBucket::remove_object(const DoutPrefixProvider *dpp, RGWBucketAdminOpState& op_state, std::string *err_msg)
7c673cae 449{
7c673cae
FG
450 std::string object_name = op_state.get_object_name();
451
452 rgw_obj_key key(object_name);
453
20effc67
TL
454 bucket = op_state.get_bucket()->clone();
455
456 int ret = rgw_remove_object(dpp, store, bucket.get(), key);
7c673cae
FG
457 if (ret < 0) {
458 set_err_msg(err_msg, "unable to remove object" + cpp_strerror(-ret));
459 return ret;
460 }
461
462 return 0;
463}
464
20effc67 465static void dump_bucket_index(const vector<rgw_bucket_dir_entry>& objs, Formatter *f)
7c673cae 466{
20effc67
TL
467 for (auto iter = objs.begin(); iter != objs.end(); ++iter) {
468 f->dump_string("object", iter->key.name);
9f95a23c 469 }
7c673cae
FG
470}
471
472static void dump_bucket_usage(map<RGWObjCategory, RGWStorageStats>& stats, Formatter *formatter)
473{
474 map<RGWObjCategory, RGWStorageStats>::iterator iter;
475
476 formatter->open_object_section("usage");
477 for (iter = stats.begin(); iter != stats.end(); ++iter) {
478 RGWStorageStats& s = iter->second;
20effc67 479 formatter->open_object_section(to_string(iter->first));
7c673cae
FG
480 s.dump(formatter);
481 formatter->close_section();
482 }
483 formatter->close_section();
484}
485
486static void dump_index_check(map<RGWObjCategory, RGWStorageStats> existing_stats,
487 map<RGWObjCategory, RGWStorageStats> calculated_stats,
488 Formatter *formatter)
489{
490 formatter->open_object_section("check_result");
491 formatter->open_object_section("existing_header");
492 dump_bucket_usage(existing_stats, formatter);
493 formatter->close_section();
494 formatter->open_object_section("calculated_header");
495 dump_bucket_usage(calculated_stats, formatter);
496 formatter->close_section();
497 formatter->close_section();
498}
499
500int RGWBucket::check_bad_index_multipart(RGWBucketAdminOpState& op_state,
b3b6e05e
TL
501 RGWFormatterFlusher& flusher,
502 const DoutPrefixProvider *dpp, std::string *err_msg)
7c673cae
FG
503{
504 bool fix_index = op_state.will_fix_index();
7c673cae
FG
505
506 bool is_truncated;
507 map<string, bool> meta_objs;
508 map<rgw_obj_index_key, string> all_objs;
509
20effc67 510 bucket = op_state.get_bucket()->clone();
7c673cae 511
20effc67 512 rgw::sal::Bucket::ListParams params;
7c673cae 513
20effc67
TL
514 params.list_versions = true;
515 params.ns = RGW_OBJ_NS_MULTIPART;
7c673cae
FG
516
517 do {
20effc67
TL
518 rgw::sal::Bucket::ListResults results;
519 int r = bucket->list(dpp, params, listing_max_entries, results, null_yield);
7c673cae 520 if (r < 0) {
20effc67 521 set_err_msg(err_msg, "failed to list objects in bucket=" + bucket->get_name() +
7c673cae
FG
522 " err=" + cpp_strerror(-r));
523
524 return r;
525 }
20effc67 526 is_truncated = results.is_truncated;
7c673cae
FG
527
528 vector<rgw_bucket_dir_entry>::iterator iter;
20effc67 529 for (iter = results.objs.begin(); iter != results.objs.end(); ++iter) {
31f18b77 530 rgw_obj_index_key key = iter->key;
20effc67 531 rgw_obj obj(bucket->get_key(), key);
31f18b77 532 string oid = obj.get_oid();
7c673cae
FG
533
534 int pos = oid.find_last_of('.');
535 if (pos < 0) {
536 /* obj has no suffix */
537 all_objs[key] = oid;
538 } else {
539 /* obj has suffix */
540 string name = oid.substr(0, pos);
541 string suffix = oid.substr(pos + 1);
542
543 if (suffix.compare("meta") == 0) {
544 meta_objs[name] = true;
545 } else {
546 all_objs[key] = name;
547 }
548 }
549 }
7c673cae
FG
550 } while (is_truncated);
551
d2e6a577
FG
552 list<rgw_obj_index_key> objs_to_unlink;
553 Formatter *f = flusher.get_formatter();
554
555 f->open_array_section("invalid_multipart_entries");
556
7c673cae
FG
557 for (auto aiter = all_objs.begin(); aiter != all_objs.end(); ++aiter) {
558 string& name = aiter->second;
559
7c673cae
FG
560 if (meta_objs.find(name) == meta_objs.end()) {
561 objs_to_unlink.push_back(aiter->first);
562 }
7c673cae 563
9f95a23c 564 if (objs_to_unlink.size() > listing_max_entries) {
d2e6a577 565 if (fix_index) {
20effc67 566 int r = bucket->remove_objs_from_index(dpp, objs_to_unlink);
d2e6a577
FG
567 if (r < 0) {
568 set_err_msg(err_msg, "ERROR: remove_obj_from_index() returned error: " +
569 cpp_strerror(-r));
570 return r;
571 }
572 }
573
574 dump_mulipart_index_results(objs_to_unlink, flusher.get_formatter());
575 flusher.flush();
576 objs_to_unlink.clear();
577 }
578 }
7c673cae
FG
579
580 if (fix_index) {
20effc67 581 int r = bucket->remove_objs_from_index(dpp, objs_to_unlink);
7c673cae
FG
582 if (r < 0) {
583 set_err_msg(err_msg, "ERROR: remove_obj_from_index() returned error: " +
584 cpp_strerror(-r));
585
586 return r;
587 }
588 }
589
d2e6a577
FG
590 dump_mulipart_index_results(objs_to_unlink, f);
591 f->close_section();
592 flusher.flush();
593
7c673cae
FG
594 return 0;
595}
596
b3b6e05e
TL
597int RGWBucket::check_object_index(const DoutPrefixProvider *dpp,
598 RGWBucketAdminOpState& op_state,
7c673cae 599 RGWFormatterFlusher& flusher,
9f95a23c 600 optional_yield y,
7c673cae
FG
601 std::string *err_msg)
602{
603
604 bool fix_index = op_state.will_fix_index();
605
7c673cae
FG
606 if (!fix_index) {
607 set_err_msg(err_msg, "check-objects flag requires fix index enabled");
608 return -EINVAL;
609 }
610
20effc67 611 bucket->set_tag_timeout(dpp, BUCKET_TAG_TIMEOUT);
7c673cae 612
20effc67
TL
613 rgw::sal::Bucket::ListResults results;
614 results.is_truncated = true;
7c673cae
FG
615
616 Formatter *formatter = flusher.get_formatter();
617 formatter->open_object_section("objects");
20effc67
TL
618 while (results.is_truncated) {
619 rgw::sal::Bucket::ListParams params;
620 params.marker = results.next_marker;
621 params.force_check_filter = rgw_bucket_object_check_filter;
622
623 int r = bucket->list(dpp, params, listing_max_entries, results, y);
624
7c673cae
FG
625 if (r == -ENOENT) {
626 break;
20effc67 627 } else if (r < 0) {
7c673cae
FG
628 set_err_msg(err_msg, "ERROR: failed operation r=" + cpp_strerror(-r));
629 }
630
20effc67 631 dump_bucket_index(results.objs, formatter);
7c673cae 632 flusher.flush();
7c673cae
FG
633 }
634
635 formatter->close_section();
636
20effc67 637 bucket->set_tag_timeout(dpp, 0);
7c673cae
FG
638
639 return 0;
640}
641
642
b3b6e05e
TL
643int RGWBucket::check_index(const DoutPrefixProvider *dpp,
644 RGWBucketAdminOpState& op_state,
7c673cae
FG
645 map<RGWObjCategory, RGWStorageStats>& existing_stats,
646 map<RGWObjCategory, RGWStorageStats>& calculated_stats,
647 std::string *err_msg)
648{
7c673cae
FG
649 bool fix_index = op_state.will_fix_index();
650
20effc67 651 int r = bucket->check_index(dpp, existing_stats, calculated_stats);
7c673cae
FG
652 if (r < 0) {
653 set_err_msg(err_msg, "failed to check index error=" + cpp_strerror(-r));
654 return r;
655 }
656
657 if (fix_index) {
20effc67 658 r = bucket->rebuild_index(dpp);
7c673cae
FG
659 if (r < 0) {
660 set_err_msg(err_msg, "failed to rebuild index err=" + cpp_strerror(-r));
661 return r;
662 }
663 }
664
665 return 0;
666}
667
20effc67 668int RGWBucket::sync(RGWBucketAdminOpState& op_state, const DoutPrefixProvider *dpp, std::string *err_msg)
7c673cae 669{
20effc67 670 if (!store->is_meta_master()) {
9f95a23c 671 set_err_msg(err_msg, "ERROR: failed to update bucket sync: only allowed on meta master zone");
522d829b 672 return -EINVAL;
9f95a23c
TL
673 }
674 bool sync = op_state.will_sync_bucket();
675 if (sync) {
20effc67 676 bucket->get_info().flags &= ~BUCKET_DATASYNC_DISABLED;
9f95a23c 677 } else {
20effc67 678 bucket->get_info().flags |= BUCKET_DATASYNC_DISABLED;
7c673cae 679 }
81eedcae 680
20effc67 681 int r = bucket->put_info(dpp, false, real_time());
9f95a23c
TL
682 if (r < 0) {
683 set_err_msg(err_msg, "ERROR: failed writing bucket instance info:" + cpp_strerror(-r));
684 return r;
685 }
7c673cae 686
20effc67
TL
687 int shards_num = bucket->get_info().layout.current_index.layout.normal.num_shards? bucket->get_info().layout.current_index.layout.normal.num_shards : 1;
688 int shard_id = bucket->get_info().layout.current_index.layout.normal.num_shards? 0 : -1;
7c673cae 689
9f95a23c 690 if (!sync) {
20effc67 691 r = static_cast<rgw::sal::RadosStore*>(store)->svc()->bilog_rados->log_stop(dpp, bucket->get_info(), -1);
9f95a23c
TL
692 if (r < 0) {
693 set_err_msg(err_msg, "ERROR: failed writing stop bilog:" + cpp_strerror(-r));
694 return r;
695 }
696 } else {
20effc67 697 r = static_cast<rgw::sal::RadosStore*>(store)->svc()->bilog_rados->log_start(dpp, bucket->get_info(), -1);
9f95a23c
TL
698 if (r < 0) {
699 set_err_msg(err_msg, "ERROR: failed writing resync bilog:" + cpp_strerror(-r));
700 return r;
701 }
702 }
703
704 for (int i = 0; i < shards_num; ++i, ++shard_id) {
20effc67 705 r = static_cast<rgw::sal::RadosStore*>(store)->svc()->datalog_rados->add_entry(dpp, bucket->get_info(), shard_id);
9f95a23c
TL
706 if (r < 0) {
707 set_err_msg(err_msg, "ERROR: failed writing data log:" + cpp_strerror(-r));
708 return r;
709 }
710 }
711
712 return 0;
713}
714
715
716int RGWBucket::policy_bl_to_stream(bufferlist& bl, ostream& o)
717{
718 RGWAccessControlPolicy_S3 policy(g_ceph_context);
719 int ret = decode_bl(bl, policy);
720 if (ret < 0) {
721 ldout(store->ctx(),0) << "failed to decode RGWAccessControlPolicy" << dendl;
722 }
723 policy.to_xml(o);
724 return 0;
725}
726
20effc67
TL
727int rgw_object_get_attr(const DoutPrefixProvider *dpp,
728 rgw::sal::Store* store, rgw::sal::Object* obj,
729 const char* attr_name, bufferlist& out_bl, optional_yield y)
9f95a23c
TL
730{
731 RGWObjectCtx obj_ctx(store);
20effc67 732 std::unique_ptr<rgw::sal::Object::ReadOp> rop = obj->get_read_op(&obj_ctx);
9f95a23c 733
20effc67 734 return rop->get_attr(dpp, attr_name, out_bl, y);
9f95a23c
TL
735}
736
b3b6e05e 737int RGWBucket::get_policy(RGWBucketAdminOpState& op_state, RGWAccessControlPolicy& policy, optional_yield y, const DoutPrefixProvider *dpp)
9f95a23c 738{
20effc67 739 int ret;
9f95a23c 740 std::string object_name = op_state.get_object_name();
9f95a23c 741
20effc67 742 bucket = op_state.get_bucket()->clone();
7c673cae
FG
743
744 if (!object_name.empty()) {
745 bufferlist bl;
20effc67 746 std::unique_ptr<rgw::sal::Object> obj = bucket->get_object(rgw_obj_key(object_name));
7c673cae 747
20effc67 748 ret = rgw_object_get_attr(dpp, store, obj.get(), RGW_ATTR_ACL, bl, y);
81eedcae 749 if (ret < 0){
7c673cae 750 return ret;
81eedcae 751 }
7c673cae 752
81eedcae
TL
753 ret = decode_bl(bl, policy);
754 if (ret < 0) {
755 ldout(store->ctx(),0) << "failed to decode RGWAccessControlPolicy" << dendl;
756 }
757 return ret;
7c673cae
FG
758 }
759
20effc67
TL
760 map<string, bufferlist>::iterator aiter = bucket->get_attrs().find(RGW_ATTR_ACL);
761 if (aiter == bucket->get_attrs().end()) {
7c673cae
FG
762 return -ENOENT;
763 }
764
81eedcae
TL
765 ret = decode_bl(aiter->second, policy);
766 if (ret < 0) {
767 ldout(store->ctx(),0) << "failed to decode RGWAccessControlPolicy" << dendl;
768 }
769
770 return ret;
7c673cae
FG
771}
772
773
20effc67 774int RGWBucketAdminOp::get_policy(rgw::sal::Store* store, RGWBucketAdminOpState& op_state,
b3b6e05e 775 RGWAccessControlPolicy& policy, const DoutPrefixProvider *dpp)
7c673cae
FG
776{
777 RGWBucket bucket;
778
b3b6e05e 779 int ret = bucket.init(store, op_state, null_yield, dpp);
7c673cae
FG
780 if (ret < 0)
781 return ret;
782
b3b6e05e 783 ret = bucket.get_policy(op_state, policy, null_yield, dpp);
7c673cae
FG
784 if (ret < 0)
785 return ret;
786
787 return 0;
788}
789
790/* Wrappers to facilitate RESTful interface */
791
792
20effc67 793int RGWBucketAdminOp::get_policy(rgw::sal::Store* store, RGWBucketAdminOpState& op_state,
b3b6e05e 794 RGWFormatterFlusher& flusher, const DoutPrefixProvider *dpp)
7c673cae
FG
795{
796 RGWAccessControlPolicy policy(store->ctx());
797
b3b6e05e 798 int ret = get_policy(store, op_state, policy, dpp);
7c673cae
FG
799 if (ret < 0)
800 return ret;
801
802 Formatter *formatter = flusher.get_formatter();
803
804 flusher.start(0);
805
806 formatter->open_object_section("policy");
807 policy.dump(formatter);
808 formatter->close_section();
809
810 flusher.flush();
811
812 return 0;
813}
814
20effc67 815int RGWBucketAdminOp::dump_s3_policy(rgw::sal::Store* store, RGWBucketAdminOpState& op_state,
b3b6e05e 816 ostream& os, const DoutPrefixProvider *dpp)
7c673cae
FG
817{
818 RGWAccessControlPolicy_S3 policy(store->ctx());
819
b3b6e05e 820 int ret = get_policy(store, op_state, policy, dpp);
7c673cae
FG
821 if (ret < 0)
822 return ret;
823
824 policy.to_xml(os);
825
826 return 0;
827}
828
20effc67 829int RGWBucketAdminOp::unlink(rgw::sal::Store* store, RGWBucketAdminOpState& op_state, const DoutPrefixProvider *dpp)
7c673cae
FG
830{
831 RGWBucket bucket;
832
b3b6e05e 833 int ret = bucket.init(store, op_state, null_yield, dpp);
7c673cae
FG
834 if (ret < 0)
835 return ret;
836
20effc67 837 return static_cast<rgw::sal::RadosStore*>(store)->ctl()->bucket->unlink_bucket(op_state.get_user_id(), op_state.get_bucket()->get_info().bucket, null_yield, dpp, true);
7c673cae
FG
838}
839
20effc67 840int RGWBucketAdminOp::link(rgw::sal::Store* store, RGWBucketAdminOpState& op_state, const DoutPrefixProvider *dpp, string *err)
7c673cae 841{
20effc67
TL
842 if (!op_state.is_user_op()) {
843 set_err_msg(err, "empty user id");
844 return -EINVAL;
845 }
7c673cae 846
20effc67
TL
847 RGWBucket bucket;
848 int ret = bucket.init(store, op_state, null_yield, dpp, err);
7c673cae
FG
849 if (ret < 0)
850 return ret;
851
20effc67
TL
852 string bucket_id = op_state.get_bucket_id();
853 std::string display_name = op_state.get_user_display_name();
854 std::unique_ptr<rgw::sal::Bucket> loc_bucket;
855 std::unique_ptr<rgw::sal::Bucket> old_bucket;
856
857 loc_bucket = op_state.get_bucket()->clone();
858
859 if (!bucket_id.empty() && bucket_id != loc_bucket->get_bucket_id()) {
860 set_err_msg(err,
861 "specified bucket id does not match " + loc_bucket->get_bucket_id());
862 return -EINVAL;
863 }
864
865 old_bucket = loc_bucket->clone();
866
867 loc_bucket->get_key().tenant = op_state.get_user_id().tenant;
868
869 if (!op_state.new_bucket_name.empty()) {
870 auto pos = op_state.new_bucket_name.find('/');
871 if (pos != string::npos) {
872 loc_bucket->get_key().tenant = op_state.new_bucket_name.substr(0, pos);
873 loc_bucket->get_key().name = op_state.new_bucket_name.substr(pos + 1);
874 } else {
875 loc_bucket->get_key().name = op_state.new_bucket_name;
876 }
877 }
878
879 RGWObjVersionTracker objv_tracker;
880 RGWObjVersionTracker old_version = loc_bucket->get_info().objv_tracker;
881
882 map<string, bufferlist>::iterator aiter = loc_bucket->get_attrs().find(RGW_ATTR_ACL);
883 if (aiter == loc_bucket->get_attrs().end()) {
884 // should never happen; only pre-argonaut buckets lacked this.
885 ldpp_dout(dpp, 0) << "WARNING: can't bucket link because no acl on bucket=" << old_bucket << dendl;
886 set_err_msg(err,
887 "While crossing the Anavros you have displeased the goddess Hera."
888 " You must sacrifice your ancient bucket " + loc_bucket->get_bucket_id());
889 return -EINVAL;
890 }
891 bufferlist& aclbl = aiter->second;
892 RGWAccessControlPolicy policy;
893 ACLOwner owner;
894 try {
895 auto iter = aclbl.cbegin();
896 decode(policy, iter);
897 owner = policy.get_owner();
898 } catch (buffer::error& e) {
899 set_err_msg(err, "couldn't decode policy");
900 return -EIO;
901 }
902
903 int r = static_cast<rgw::sal::RadosStore*>(store)->ctl()->bucket->unlink_bucket(owner.get_id(), old_bucket->get_info().bucket, null_yield, dpp, false);
904 if (r < 0) {
905 set_err_msg(err, "could not unlink policy from user " + owner.get_id().to_str());
906 return r;
907 }
908
909 // now update the user for the bucket...
910 if (display_name.empty()) {
911 ldpp_dout(dpp, 0) << "WARNING: user " << op_state.get_user_id() << " has no display name set" << dendl;
912 }
913
914 RGWAccessControlPolicy policy_instance;
915 policy_instance.create_default(op_state.get_user_id(), display_name);
916 owner = policy_instance.get_owner();
917
918 aclbl.clear();
919 policy_instance.encode(aclbl);
920
921 bool exclusive = false;
922 loc_bucket->get_info().owner = op_state.get_user_id();
923 if (*loc_bucket != *old_bucket) {
924 loc_bucket->get_info().bucket = loc_bucket->get_key();
925 loc_bucket->get_info().objv_tracker.version_for_read()->ver = 0;
926 exclusive = true;
927 }
928
929 r = loc_bucket->put_info(dpp, exclusive, ceph::real_time());
930 if (r < 0) {
931 set_err_msg(err, "ERROR: failed writing bucket instance info: " + cpp_strerror(-r));
932 return r;
933 }
934
935 /* link to user */
936 RGWBucketEntryPoint ep;
937 ep.bucket = loc_bucket->get_info().bucket;
938 ep.owner = op_state.get_user_id();
939 ep.creation_time = loc_bucket->get_info().creation_time;
940 ep.linked = true;
941 rgw::sal::Attrs ep_attrs;
942 rgw_ep_info ep_data{ep, ep_attrs};
943
944 r = static_cast<rgw::sal::RadosStore*>(store)->ctl()->bucket->link_bucket(op_state.get_user_id(), loc_bucket->get_info().bucket, loc_bucket->get_info().creation_time, null_yield, dpp, true, &ep_data);
945 if (r < 0) {
946 set_err_msg(err, "failed to relink bucket");
947 return r;
948 }
949
950 if (*loc_bucket != *old_bucket) {
951 // like RGWRados::delete_bucket -- excepting no bucket_index work.
952 r = static_cast<rgw::sal::RadosStore*>(store)->ctl()->bucket->remove_bucket_entrypoint_info(
953 old_bucket->get_key(), null_yield, dpp,
954 RGWBucketCtl::Bucket::RemoveParams()
955 .set_objv_tracker(&ep_data.ep_objv));
956 if (r < 0) {
957 set_err_msg(err, "failed to unlink old bucket " + old_bucket->get_tenant() + "/" + old_bucket->get_name());
958 return r;
959 }
960 r = static_cast<rgw::sal::RadosStore*>(store)->ctl()->bucket->remove_bucket_instance_info(
961 old_bucket->get_key(), old_bucket->get_info(),
962 null_yield, dpp,
963 RGWBucketCtl::BucketInstance::RemoveParams()
964 .set_objv_tracker(&ep_data.ep_objv));
965 if (r < 0) {
966 set_err_msg(err, "failed to unlink old bucket " + old_bucket->get_tenant() + "/" + old_bucket->get_name());
967 return r;
968 }
969 }
7c673cae 970
20effc67 971 return 0;
7c673cae
FG
972}
973
20effc67 974int RGWBucketAdminOp::chown(rgw::sal::Store* store, RGWBucketAdminOpState& op_state, const string& marker, const DoutPrefixProvider *dpp, string *err)
9f95a23c
TL
975{
976 RGWBucket bucket;
9f95a23c 977
20effc67 978 int ret = bucket.init(store, op_state, null_yield, dpp, err);
9f95a23c
TL
979 if (ret < 0)
980 return ret;
981
b3b6e05e 982 return bucket.chown(op_state, marker, null_yield, dpp, err);
9f95a23c
TL
983
984}
985
20effc67 986int RGWBucketAdminOp::check_index(rgw::sal::Store* store, RGWBucketAdminOpState& op_state,
b3b6e05e 987 RGWFormatterFlusher& flusher, optional_yield y, const DoutPrefixProvider *dpp)
7c673cae
FG
988{
989 int ret;
7c673cae
FG
990 map<RGWObjCategory, RGWStorageStats> existing_stats;
991 map<RGWObjCategory, RGWStorageStats> calculated_stats;
d2e6a577 992
7c673cae
FG
993
994 RGWBucket bucket;
995
b3b6e05e 996 ret = bucket.init(store, op_state, null_yield, dpp);
7c673cae
FG
997 if (ret < 0)
998 return ret;
999
1000 Formatter *formatter = flusher.get_formatter();
1001 flusher.start(0);
1002
b3b6e05e 1003 ret = bucket.check_bad_index_multipart(op_state, flusher, dpp);
7c673cae
FG
1004 if (ret < 0)
1005 return ret;
1006
b3b6e05e 1007 ret = bucket.check_object_index(dpp, op_state, flusher, y);
7c673cae
FG
1008 if (ret < 0)
1009 return ret;
1010
b3b6e05e 1011 ret = bucket.check_index(dpp, op_state, existing_stats, calculated_stats);
7c673cae
FG
1012 if (ret < 0)
1013 return ret;
1014
1015 dump_index_check(existing_stats, calculated_stats, formatter);
1016 flusher.flush();
1017
1018 return 0;
1019}
1020
20effc67 1021int RGWBucketAdminOp::remove_bucket(rgw::sal::Store* store, RGWBucketAdminOpState& op_state,
b3b6e05e
TL
1022 optional_yield y, const DoutPrefixProvider *dpp,
1023 bool bypass_gc, bool keep_index_consistent)
7c673cae 1024{
20effc67
TL
1025 std::unique_ptr<rgw::sal::Bucket> bucket;
1026 std::unique_ptr<rgw::sal::User> user = store->get_user(op_state.get_user_id());
7c673cae 1027
b3b6e05e 1028 int ret = store->get_bucket(dpp, user.get(), user->get_tenant(), op_state.get_bucket_name(),
f67539c2 1029 &bucket, y);
7c673cae
FG
1030 if (ret < 0)
1031 return ret;
1032
f67539c2 1033 if (bypass_gc)
20effc67 1034 ret = bucket->remove_bucket_bypass_gc(op_state.get_max_aio(), keep_index_consistent, y, dpp);
f67539c2 1035 else
a4b75251 1036 ret = bucket->remove_bucket(dpp, op_state.will_delete_children(),
f67539c2
TL
1037 false, nullptr, y);
1038
c07f9fc5 1039 return ret;
7c673cae
FG
1040}
1041
20effc67 1042int RGWBucketAdminOp::remove_object(rgw::sal::Store* store, RGWBucketAdminOpState& op_state, const DoutPrefixProvider *dpp)
7c673cae
FG
1043{
1044 RGWBucket bucket;
1045
b3b6e05e 1046 int ret = bucket.init(store, op_state, null_yield, dpp);
7c673cae
FG
1047 if (ret < 0)
1048 return ret;
1049
b3b6e05e 1050 return bucket.remove_object(dpp, op_state);
7c673cae
FG
1051}
1052
20effc67 1053int RGWBucketAdminOp::sync_bucket(rgw::sal::Store* store, RGWBucketAdminOpState& op_state, const DoutPrefixProvider *dpp, string *err_msg)
9f95a23c
TL
1054{
1055 RGWBucket bucket;
20effc67 1056 int ret = bucket.init(store, op_state, null_yield, dpp, err_msg);
9f95a23c
TL
1057 if (ret < 0)
1058 {
1059 return ret;
1060 }
20effc67 1061 return bucket.sync(op_state, dpp, err_msg);
9f95a23c
TL
1062}
1063
20effc67 1064static int bucket_stats(rgw::sal::Store* store,
f6b5b4d7
TL
1065 const std::string& tenant_name,
1066 const std::string& bucket_name,
b3b6e05e
TL
1067 Formatter *formatter,
1068 const DoutPrefixProvider *dpp)
7c673cae 1069{
20effc67 1070 std::unique_ptr<rgw::sal::Bucket> bucket;
7c673cae
FG
1071 map<RGWObjCategory, RGWStorageStats> stats;
1072
1073 real_time mtime;
20effc67
TL
1074 int ret = store->get_bucket(dpp, nullptr, tenant_name, bucket_name, &bucket, null_yield);
1075 if (ret < 0) {
1076 return ret;
f6b5b4d7 1077 }
7c673cae 1078
7c673cae
FG
1079 string bucket_ver, master_ver;
1080 string max_marker;
20effc67 1081 ret = bucket->read_stats(dpp, RGW_NO_SHARD, &bucket_ver, &master_ver, stats, &max_marker);
7c673cae 1082 if (ret < 0) {
20effc67 1083 cerr << "error getting bucket stats bucket=" << bucket->get_name() << " ret=" << ret << std::endl;
7c673cae
FG
1084 return ret;
1085 }
1086
1087 utime_t ut(mtime);
20effc67 1088 utime_t ctime_ut(bucket->get_creation_time());
7c673cae
FG
1089
1090 formatter->open_object_section("stats");
20effc67 1091 formatter->dump_string("bucket", bucket->get_name());
f67539c2 1092 formatter->dump_int("num_shards",
20effc67
TL
1093 bucket->get_info().layout.current_index.layout.normal.num_shards);
1094 formatter->dump_string("tenant", bucket->get_tenant());
1095 formatter->dump_string("zonegroup", bucket->get_info().zonegroup);
1096 formatter->dump_string("placement_rule", bucket->get_info().placement_rule.to_str());
1097 ::encode_json("explicit_placement", bucket->get_key().explicit_placement, formatter);
1098 formatter->dump_string("id", bucket->get_bucket_id());
1099 formatter->dump_string("marker", bucket->get_marker());
1100 formatter->dump_stream("index_type") << bucket->get_info().layout.current_index.layout.type;
1101 ::encode_json("owner", bucket->get_info().owner, formatter);
7c673cae
FG
1102 formatter->dump_string("ver", bucket_ver);
1103 formatter->dump_string("master_ver", master_ver);
81eedcae 1104 ut.gmtime(formatter->dump_stream("mtime"));
9f95a23c 1105 ctime_ut.gmtime(formatter->dump_stream("creation_time"));
7c673cae
FG
1106 formatter->dump_string("max_marker", max_marker);
1107 dump_bucket_usage(stats, formatter);
20effc67 1108 encode_json("bucket_quota", bucket->get_info().quota, formatter);
9f95a23c
TL
1109
1110 // bucket tags
20effc67
TL
1111 auto iter = bucket->get_attrs().find(RGW_ATTR_TAGS);
1112 if (iter != bucket->get_attrs().end()) {
9f95a23c
TL
1113 RGWObjTagSet_S3 tagset;
1114 bufferlist::const_iterator piter{&iter->second};
1115 try {
1116 tagset.decode(piter);
1117 tagset.dump(formatter);
1118 } catch (buffer::error& err) {
1119 cerr << "ERROR: caught buffer:error, couldn't decode TagSet" << std::endl;
1120 }
1121 }
1122
1123 // TODO: bucket CORS
1124 // TODO: bucket LC
7c673cae
FG
1125 formatter->close_section();
1126
1127 return 0;
1128}
1129
20effc67 1130int RGWBucketAdminOp::limit_check(rgw::sal::Store* store,
7c673cae
FG
1131 RGWBucketAdminOpState& op_state,
1132 const std::list<std::string>& user_ids,
f67539c2 1133 RGWFormatterFlusher& flusher, optional_yield y,
b3b6e05e 1134 const DoutPrefixProvider *dpp,
7c673cae
FG
1135 bool warnings_only)
1136{
1137 int ret = 0;
1138 const size_t max_entries =
1139 store->ctx()->_conf->rgw_list_buckets_max_chunk;
1140
1141 const size_t safe_max_objs_per_shard =
1142 store->ctx()->_conf->rgw_safe_max_objects_per_shard;
1143
1144 uint16_t shard_warn_pct =
1145 store->ctx()->_conf->rgw_shard_warning_threshold;
1146 if (shard_warn_pct > 100)
1147 shard_warn_pct = 90;
1148
1149 Formatter *formatter = flusher.get_formatter();
1150 flusher.start(0);
1151
1152 formatter->open_array_section("users");
1153
1154 for (const auto& user_id : user_ids) {
a8e16298 1155
7c673cae
FG
1156 formatter->open_object_section("user");
1157 formatter->dump_string("user_id", user_id);
7c673cae 1158 formatter->open_array_section("buckets");
a8e16298
TL
1159
1160 string marker;
20effc67 1161 rgw::sal::BucketList buckets;
7c673cae 1162 do {
20effc67 1163 std::unique_ptr<rgw::sal::User> user = store->get_user(rgw_user(user_id));
9f95a23c 1164
20effc67 1165 ret = user->list_buckets(dpp, marker, string(), max_entries, false, buckets, y);
7c673cae 1166
7c673cae
FG
1167 if (ret < 0)
1168 return ret;
1169
20effc67 1170 map<string, std::unique_ptr<rgw::sal::Bucket>>& m_buckets = buckets.get_buckets();
7c673cae
FG
1171
1172 for (const auto& iter : m_buckets) {
f67539c2 1173 auto& bucket = iter.second;
7c673cae
FG
1174 uint32_t num_shards = 1;
1175 uint64_t num_objects = 0;
1176
9f95a23c
TL
1177 marker = bucket->get_name(); /* Casey's location for marker update,
1178 * as we may now not reach the end of
1179 * the loop body */
7c673cae 1180
20effc67 1181 ret = bucket->load_bucket(dpp, null_yield);
7c673cae
FG
1182 if (ret < 0)
1183 continue;
1184
1185 /* need stats for num_entries */
1186 string bucket_ver, master_ver;
1187 std::map<RGWObjCategory, RGWStorageStats> stats;
20effc67 1188 ret = bucket->read_stats(dpp, RGW_NO_SHARD, &bucket_ver, &master_ver, stats, nullptr);
7c673cae
FG
1189
1190 if (ret < 0)
1191 continue;
1192
1193 for (const auto& s : stats) {
adb31ebb 1194 num_objects += s.second.num_objects;
7c673cae
FG
1195 }
1196
20effc67 1197 num_shards = bucket->get_info().layout.current_index.layout.normal.num_shards;
31f18b77
FG
1198 uint64_t objs_per_shard =
1199 (num_shards) ? num_objects/num_shards : num_objects;
7c673cae 1200 {
adb31ebb 1201 bool warn;
7c673cae 1202 stringstream ss;
adb31ebb
TL
1203 uint64_t fill_pct = objs_per_shard * 100 / safe_max_objs_per_shard;
1204 if (fill_pct > 100) {
1205 ss << "OVER " << fill_pct << "%";
1206 warn = true;
1207 } else if (fill_pct >= shard_warn_pct) {
1208 ss << "WARN " << fill_pct << "%";
1209 warn = true;
7c673cae 1210 } else {
adb31ebb
TL
1211 ss << "OK";
1212 warn = false;
7c673cae
FG
1213 }
1214
adb31ebb 1215 if (warn || !warnings_only) {
7c673cae 1216 formatter->open_object_section("bucket");
9f95a23c
TL
1217 formatter->dump_string("bucket", bucket->get_name());
1218 formatter->dump_string("tenant", bucket->get_tenant());
7c673cae
FG
1219 formatter->dump_int("num_objects", num_objects);
1220 formatter->dump_int("num_shards", num_shards);
1221 formatter->dump_int("objects_per_shard", objs_per_shard);
1222 formatter->dump_string("fill_status", ss.str());
1223 formatter->close_section();
1224 }
1225 }
1226 }
a8e16298 1227 formatter->flush(cout);
9f95a23c 1228 } while (buckets.is_truncated()); /* foreach: bucket */
7c673cae
FG
1229
1230 formatter->close_section();
1231 formatter->close_section();
1232 formatter->flush(cout);
1233
1234 } /* foreach: user_id */
1235
1236 formatter->close_section();
1237 formatter->flush(cout);
1238
1239 return ret;
1240} /* RGWBucketAdminOp::limit_check */
1241
20effc67 1242int RGWBucketAdminOp::info(rgw::sal::Store* store,
f6b5b4d7 1243 RGWBucketAdminOpState& op_state,
f67539c2 1244 RGWFormatterFlusher& flusher,
b3b6e05e
TL
1245 optional_yield y,
1246 const DoutPrefixProvider *dpp)
7c673cae 1247{
adb31ebb 1248 RGWBucket bucket;
11fdf7f2 1249 int ret = 0;
f6b5b4d7 1250 const std::string& bucket_name = op_state.get_bucket_name();
adb31ebb 1251 if (!bucket_name.empty()) {
b3b6e05e 1252 ret = bucket.init(store, op_state, null_yield, dpp);
adb31ebb
TL
1253 if (-ENOENT == ret)
1254 return -ERR_NO_SUCH_BUCKET;
1255 else if (ret < 0)
1256 return ret;
1257 }
1258
7c673cae
FG
1259 Formatter *formatter = flusher.get_formatter();
1260 flusher.start(0);
1261
1262 CephContext *cct = store->ctx();
1263
1264 const size_t max_entries = cct->_conf->rgw_list_buckets_max_chunk;
1265
f6b5b4d7
TL
1266 const bool show_stats = op_state.will_fetch_stats();
1267 const rgw_user& user_id = op_state.get_user_id();
7c673cae
FG
1268 if (op_state.is_user_op()) {
1269 formatter->open_array_section("buckets");
1270
20effc67
TL
1271 rgw::sal::BucketList buckets;
1272 std::unique_ptr<rgw::sal::User> user = store->get_user(op_state.get_user_id());
f6b5b4d7
TL
1273 std::string marker;
1274 const std::string empty_end_marker;
1275 constexpr bool no_need_stats = false; // set need_stats to false
7c673cae
FG
1276
1277 do {
20effc67 1278 ret = user->list_buckets(dpp, marker, empty_end_marker, max_entries,
f67539c2 1279 no_need_stats, buckets, y);
f6b5b4d7 1280 if (ret < 0) {
7c673cae 1281 return ret;
f6b5b4d7 1282 }
7c673cae 1283
f6b5b4d7 1284 const std::string* marker_cursor = nullptr;
20effc67 1285 map<string, std::unique_ptr<rgw::sal::Bucket>>& m = buckets.get_buckets();
7c673cae 1286
f6b5b4d7
TL
1287 for (const auto& i : m) {
1288 const std::string& obj_name = i.first;
11fdf7f2
TL
1289 if (!bucket_name.empty() && bucket_name != obj_name) {
1290 continue;
1291 }
1292
f6b5b4d7 1293 if (show_stats) {
b3b6e05e 1294 bucket_stats(store, user_id.tenant, obj_name, formatter, dpp);
f6b5b4d7 1295 } else {
7c673cae 1296 formatter->dump_string("bucket", obj_name);
f6b5b4d7 1297 }
7c673cae 1298
f6b5b4d7
TL
1299 marker_cursor = &obj_name;
1300 } // for loop
1301 if (marker_cursor) {
1302 marker = *marker_cursor;
7c673cae
FG
1303 }
1304
1305 flusher.flush();
f6b5b4d7 1306 } while (buckets.is_truncated());
7c673cae
FG
1307
1308 formatter->close_section();
1309 } else if (!bucket_name.empty()) {
b3b6e05e 1310 ret = bucket_stats(store, user_id.tenant, bucket_name, formatter, dpp);
11fdf7f2
TL
1311 if (ret < 0) {
1312 return ret;
1313 }
7c673cae 1314 } else {
11fdf7f2
TL
1315 void *handle = nullptr;
1316 bool truncated = true;
7c673cae
FG
1317
1318 formatter->open_array_section("buckets");
20effc67 1319 ret = store->meta_list_keys_init(dpp, "bucket", string(), &handle);
11fdf7f2
TL
1320 while (ret == 0 && truncated) {
1321 std::list<std::string> buckets;
f6b5b4d7 1322 constexpr int max_keys = 1000;
20effc67 1323 ret = store->meta_list_keys_next(dpp, handle, max_keys, buckets,
f6b5b4d7 1324 &truncated);
11fdf7f2 1325 for (auto& bucket_name : buckets) {
f6b5b4d7 1326 if (show_stats) {
b3b6e05e 1327 bucket_stats(store, user_id.tenant, bucket_name, formatter, dpp);
f6b5b4d7 1328 } else {
11fdf7f2 1329 formatter->dump_string("bucket", bucket_name);
f6b5b4d7 1330 }
7c673cae
FG
1331 }
1332 }
20effc67 1333 store->meta_list_keys_complete(handle);
7c673cae
FG
1334
1335 formatter->close_section();
1336 }
1337
1338 flusher.flush();
1339
1340 return 0;
1341}
1342
20effc67 1343int RGWBucketAdminOp::set_quota(rgw::sal::Store* store, RGWBucketAdminOpState& op_state, const DoutPrefixProvider *dpp)
94b18763
FG
1344{
1345 RGWBucket bucket;
1346
b3b6e05e 1347 int ret = bucket.init(store, op_state, null_yield, dpp);
94b18763
FG
1348 if (ret < 0)
1349 return ret;
b3b6e05e 1350 return bucket.set_quota(op_state, dpp);
94b18763 1351}
7c673cae 1352
20effc67 1353static int purge_bucket_instance(rgw::sal::Store* store, const RGWBucketInfo& bucket_info, const DoutPrefixProvider *dpp)
f64942e4 1354{
20effc67
TL
1355 std::unique_ptr<rgw::sal::Bucket> bucket;
1356 int ret = store->get_bucket(nullptr, bucket_info, &bucket);
1357 if (ret < 0)
1358 return ret;
1359
1360 return bucket->purge_instance(dpp);
f64942e4
AA
1361}
1362
11fdf7f2 1363inline auto split_tenant(const std::string& bucket_name){
f64942e4
AA
1364 auto p = bucket_name.find('/');
1365 if(p != std::string::npos) {
1366 return std::make_pair(bucket_name.substr(0,p), bucket_name.substr(p+1));
1367 }
1368 return std::make_pair(std::string(), bucket_name);
1369}
1370
1371using bucket_instance_ls = std::vector<RGWBucketInfo>;
20effc67 1372void get_stale_instances(rgw::sal::Store* store, const std::string& bucket_name,
f64942e4 1373 const vector<std::string>& lst,
b3b6e05e
TL
1374 bucket_instance_ls& stale_instances,
1375 const DoutPrefixProvider *dpp)
f64942e4
AA
1376{
1377
f64942e4
AA
1378 bucket_instance_ls other_instances;
1379// first iterate over the entries, and pick up the done buckets; these
1380// are guaranteed to be stale
1381 for (const auto& bucket_instance : lst){
1382 RGWBucketInfo binfo;
20effc67
TL
1383 std::unique_ptr<rgw::sal::Bucket> bucket;
1384 rgw_bucket rbucket;
1385 rgw_bucket_parse_bucket_key(store->ctx(), bucket_instance, &rbucket, nullptr);
1386 int r = store->get_bucket(dpp, nullptr, rbucket, &bucket, null_yield);
f64942e4
AA
1387 if (r < 0){
1388 // this can only happen if someone deletes us right when we're processing
b3b6e05e 1389 ldpp_dout(dpp, -1) << "Bucket instance is invalid: " << bucket_instance
f64942e4
AA
1390 << cpp_strerror(-r) << dendl;
1391 continue;
1392 }
20effc67 1393 binfo = bucket->get_info();
9f95a23c 1394 if (binfo.reshard_status == cls_rgw_reshard_status::DONE)
f64942e4
AA
1395 stale_instances.emplace_back(std::move(binfo));
1396 else {
1397 other_instances.emplace_back(std::move(binfo));
1398 }
1399 }
1400
1401 // Read the cur bucket info, if the bucket doesn't exist we can simply return
1402 // all the instances
20effc67 1403 auto [tenant, bname] = split_tenant(bucket_name);
f64942e4 1404 RGWBucketInfo cur_bucket_info;
20effc67
TL
1405 std::unique_ptr<rgw::sal::Bucket> cur_bucket;
1406 int r = store->get_bucket(dpp, nullptr, tenant, bname, &cur_bucket, null_yield);
f64942e4
AA
1407 if (r < 0) {
1408 if (r == -ENOENT) {
1409 // bucket doesn't exist, everything is stale then
1410 stale_instances.insert(std::end(stale_instances),
1411 std::make_move_iterator(other_instances.begin()),
1412 std::make_move_iterator(other_instances.end()));
1413 } else {
1414 // all bets are off if we can't read the bucket, just return the sureshot stale instances
b3b6e05e 1415 ldpp_dout(dpp, -1) << "error: reading bucket info for bucket: "
20effc67 1416 << bname << cpp_strerror(-r) << dendl;
f64942e4
AA
1417 }
1418 return;
1419 }
1420
1421 // Don't process further in this round if bucket is resharding
20effc67 1422 cur_bucket_info = cur_bucket->get_info();
9f95a23c 1423 if (cur_bucket_info.reshard_status == cls_rgw_reshard_status::IN_PROGRESS)
f64942e4
AA
1424 return;
1425
1426 other_instances.erase(std::remove_if(other_instances.begin(), other_instances.end(),
1427 [&cur_bucket_info](const RGWBucketInfo& b){
1428 return (b.bucket.bucket_id == cur_bucket_info.bucket.bucket_id ||
1429 b.bucket.bucket_id == cur_bucket_info.new_bucket_instance_id);
1430 }),
1431 other_instances.end());
1432
1433 // check if there are still instances left
1434 if (other_instances.empty()) {
1435 return;
1436 }
1437
1438 // Now we have a bucket with instances where the reshard status is none, this
1439 // usually happens when the reshard process couldn't complete, lockdown the
1440 // bucket and walk through these instances to make sure no one else interferes
1441 // with these
1442 {
20effc67
TL
1443 RGWBucketReshardLock reshard_lock(static_cast<rgw::sal::RadosStore*>(store), cur_bucket->get_info(), true);
1444 r = reshard_lock.lock(dpp);
f64942e4
AA
1445 if (r < 0) {
1446 // most likely bucket is under reshard, return the sureshot stale instances
b3b6e05e 1447 ldpp_dout(dpp, 5) << __func__
f64942e4
AA
1448 << "failed to take reshard lock; reshard underway likey" << dendl;
1449 return;
1450 }
1451 auto sg = make_scope_guard([&reshard_lock](){ reshard_lock.unlock();} );
1452 // this should be fast enough that we may not need to renew locks and check
1453 // exit status?, should we read the values of the instances again?
1454 stale_instances.insert(std::end(stale_instances),
1455 std::make_move_iterator(other_instances.begin()),
1456 std::make_move_iterator(other_instances.end()));
1457 }
1458
1459 return;
1460}
1461
20effc67 1462static int process_stale_instances(rgw::sal::Store* store, RGWBucketAdminOpState& op_state,
f64942e4 1463 RGWFormatterFlusher& flusher,
b3b6e05e 1464 const DoutPrefixProvider *dpp,
f64942e4
AA
1465 std::function<void(const bucket_instance_ls&,
1466 Formatter *,
20effc67 1467 rgw::sal::Store*)> process_f)
f64942e4
AA
1468{
1469 std::string marker;
1470 void *handle;
1471 Formatter *formatter = flusher.get_formatter();
1472 static constexpr auto default_max_keys = 1000;
1473
20effc67 1474 int ret = store->meta_list_keys_init(dpp, "bucket.instance", marker, &handle);
f64942e4
AA
1475 if (ret < 0) {
1476 cerr << "ERROR: can't get key: " << cpp_strerror(-ret) << std::endl;
1477 return ret;
1478 }
1479
1480 bool truncated;
1481
1482 formatter->open_array_section("keys");
e306af50 1483 auto g = make_scope_guard([&store, &handle, &formatter]() {
20effc67 1484 store->meta_list_keys_complete(handle);
e306af50
TL
1485 formatter->close_section(); // keys
1486 formatter->flush(cout);
1487 });
f64942e4
AA
1488
1489 do {
1490 list<std::string> keys;
1491
20effc67 1492 ret = store->meta_list_keys_next(dpp, handle, default_max_keys, keys, &truncated);
f64942e4
AA
1493 if (ret < 0 && ret != -ENOENT) {
1494 cerr << "ERROR: lists_keys_next(): " << cpp_strerror(-ret) << std::endl;
1495 return ret;
1496 } if (ret != -ENOENT) {
1497 // partition the list of buckets by buckets as the listing is un sorted,
1498 // since it would minimize the reads to bucket_info
1499 std::unordered_map<std::string, std::vector<std::string>> bucket_instance_map;
1500 for (auto &key: keys) {
1501 auto pos = key.find(':');
1502 if(pos != std::string::npos)
1503 bucket_instance_map[key.substr(0,pos)].emplace_back(std::move(key));
1504 }
1505 for (const auto& kv: bucket_instance_map) {
1506 bucket_instance_ls stale_lst;
b3b6e05e 1507 get_stale_instances(store, kv.first, kv.second, stale_lst, dpp);
f64942e4
AA
1508 process_f(stale_lst, formatter, store);
1509 }
1510 }
1511 } while (truncated);
1512
f64942e4
AA
1513 return 0;
1514}
1515
20effc67 1516int RGWBucketAdminOp::list_stale_instances(rgw::sal::Store* store,
f64942e4 1517 RGWBucketAdminOpState& op_state,
b3b6e05e
TL
1518 RGWFormatterFlusher& flusher,
1519 const DoutPrefixProvider *dpp)
f64942e4
AA
1520{
1521 auto process_f = [](const bucket_instance_ls& lst,
1522 Formatter *formatter,
20effc67 1523 rgw::sal::Store*){
f64942e4
AA
1524 for (const auto& binfo: lst)
1525 formatter->dump_string("key", binfo.bucket.get_key());
1526 };
b3b6e05e 1527 return process_stale_instances(store, op_state, flusher, dpp, process_f);
f64942e4
AA
1528}
1529
1530
20effc67 1531int RGWBucketAdminOp::clear_stale_instances(rgw::sal::Store* store,
f64942e4 1532 RGWBucketAdminOpState& op_state,
b3b6e05e
TL
1533 RGWFormatterFlusher& flusher,
1534 const DoutPrefixProvider *dpp)
f64942e4 1535{
b3b6e05e 1536 auto process_f = [dpp](const bucket_instance_ls& lst,
f64942e4 1537 Formatter *formatter,
20effc67 1538 rgw::sal::Store* store){
f64942e4 1539 for (const auto &binfo: lst) {
b3b6e05e 1540 int ret = purge_bucket_instance(store, binfo, dpp);
f64942e4
AA
1541 if (ret == 0){
1542 auto md_key = "bucket.instance:" + binfo.bucket.get_key();
20effc67 1543 ret = store->meta_remove(dpp, md_key, null_yield);
f64942e4
AA
1544 }
1545 formatter->open_object_section("delete_status");
1546 formatter->dump_string("bucket_instance", binfo.bucket.get_key());
1547 formatter->dump_int("status", -ret);
1548 formatter->close_section();
1549 }
1550 };
1551
b3b6e05e 1552 return process_stale_instances(store, op_state, flusher, dpp, process_f);
f64942e4
AA
1553}
1554
20effc67 1555static int fix_single_bucket_lc(rgw::sal::Store* store,
11fdf7f2 1556 const std::string& tenant_name,
b3b6e05e
TL
1557 const std::string& bucket_name,
1558 const DoutPrefixProvider *dpp)
11fdf7f2 1559{
20effc67
TL
1560 std::unique_ptr<rgw::sal::Bucket> bucket;
1561 int ret = store->get_bucket(dpp, nullptr, tenant_name, bucket_name, &bucket, null_yield);
11fdf7f2
TL
1562 if (ret < 0) {
1563 // TODO: Should we handle the case where the bucket could've been removed between
1564 // listing and fetching?
1565 return ret;
1566 }
1567
20effc67 1568 return rgw::lc::fix_lc_shard_entry(dpp, store, store->get_rgwlc()->get_lc(), bucket.get());
11fdf7f2
TL
1569}
1570
1571static void format_lc_status(Formatter* formatter,
1572 const std::string& tenant_name,
1573 const std::string& bucket_name,
1574 int status)
1575{
1576 formatter->open_object_section("bucket_entry");
1577 std::string entry = tenant_name.empty() ? bucket_name : tenant_name + "/" + bucket_name;
1578 formatter->dump_string("bucket", entry);
1579 formatter->dump_int("status", status);
1580 formatter->close_section(); // bucket_entry
1581}
1582
20effc67 1583static void process_single_lc_entry(rgw::sal::Store* store,
9f95a23c 1584 Formatter *formatter,
11fdf7f2 1585 const std::string& tenant_name,
b3b6e05e
TL
1586 const std::string& bucket_name,
1587 const DoutPrefixProvider *dpp)
11fdf7f2 1588{
b3b6e05e 1589 int ret = fix_single_bucket_lc(store, tenant_name, bucket_name, dpp);
11fdf7f2
TL
1590 format_lc_status(formatter, tenant_name, bucket_name, -ret);
1591}
1592
20effc67 1593int RGWBucketAdminOp::fix_lc_shards(rgw::sal::Store* store,
11fdf7f2 1594 RGWBucketAdminOpState& op_state,
b3b6e05e
TL
1595 RGWFormatterFlusher& flusher,
1596 const DoutPrefixProvider *dpp)
11fdf7f2
TL
1597{
1598 std::string marker;
1599 void *handle;
1600 Formatter *formatter = flusher.get_formatter();
1601 static constexpr auto default_max_keys = 1000;
1602
1603 bool truncated;
1604 if (const std::string& bucket_name = op_state.get_bucket_name();
1605 ! bucket_name.empty()) {
1606 const rgw_user user_id = op_state.get_user_id();
b3b6e05e 1607 process_single_lc_entry(store, formatter, user_id.tenant, bucket_name, dpp);
11fdf7f2
TL
1608 formatter->flush(cout);
1609 } else {
20effc67 1610 int ret = store->meta_list_keys_init(dpp, "bucket", marker, &handle);
11fdf7f2
TL
1611 if (ret < 0) {
1612 std::cerr << "ERROR: can't get key: " << cpp_strerror(-ret) << std::endl;
1613 return ret;
1614 }
1615
1616 {
1617 formatter->open_array_section("lc_fix_status");
1618 auto sg = make_scope_guard([&store, &handle, &formatter](){
20effc67 1619 store->meta_list_keys_complete(handle);
11fdf7f2
TL
1620 formatter->close_section(); // lc_fix_status
1621 formatter->flush(cout);
1622 });
1623 do {
1624 list<std::string> keys;
20effc67 1625 ret = store->meta_list_keys_next(dpp, handle, default_max_keys, keys, &truncated);
11fdf7f2
TL
1626 if (ret < 0 && ret != -ENOENT) {
1627 std::cerr << "ERROR: lists_keys_next(): " << cpp_strerror(-ret) << std::endl;
1628 return ret;
1629 } if (ret != -ENOENT) {
1630 for (const auto &key:keys) {
1631 auto [tenant_name, bucket_name] = split_tenant(key);
b3b6e05e 1632 process_single_lc_entry(store, formatter, tenant_name, bucket_name, dpp);
11fdf7f2
TL
1633 }
1634 }
1635 formatter->flush(cout); // regularly flush every 1k entries
1636 } while (truncated);
1637 }
1638
1639 }
1640 return 0;
1641
1642}
1643
20effc67
TL
1644static bool has_object_expired(const DoutPrefixProvider *dpp,
1645 rgw::sal::Store* store,
1646 rgw::sal::Bucket* bucket,
81eedcae
TL
1647 const rgw_obj_key& key, utime_t& delete_at)
1648{
20effc67 1649 std::unique_ptr<rgw::sal::Object> obj = bucket->get_object(key);
81eedcae
TL
1650 bufferlist delete_at_bl;
1651
20effc67 1652 int ret = rgw_object_get_attr(dpp, store, obj.get(), RGW_ATTR_DELETE_AT, delete_at_bl, null_yield);
81eedcae
TL
1653 if (ret < 0) {
1654 return false; // no delete at attr, proceed
1655 }
1656
1657 ret = decode_bl(delete_at_bl, delete_at);
1658 if (ret < 0) {
1659 return false; // failed to parse
1660 }
1661
1662 if (delete_at <= ceph_clock_now() && !delete_at.is_zero()) {
1663 return true;
1664 }
1665
1666 return false;
1667}
1668
20effc67
TL
1669static int fix_bucket_obj_expiry(const DoutPrefixProvider *dpp,
1670 rgw::sal::Store* store,
1671 rgw::sal::Bucket* bucket,
81eedcae
TL
1672 RGWFormatterFlusher& flusher, bool dry_run)
1673{
20effc67 1674 if (bucket->get_key().bucket_id == bucket->get_key().marker) {
b3b6e05e 1675 ldpp_dout(dpp, -1) << "Not a resharded bucket skipping" << dendl;
81eedcae
TL
1676 return 0; // not a resharded bucket, move along
1677 }
1678
1679 Formatter *formatter = flusher.get_formatter();
1680 formatter->open_array_section("expired_deletion_status");
1681 auto sg = make_scope_guard([&formatter] {
1682 formatter->close_section();
1683 formatter->flush(std::cout);
1684 });
1685
20effc67
TL
1686 rgw::sal::Bucket::ListParams params;
1687 rgw::sal::Bucket::ListResults results;
81eedcae 1688
20effc67
TL
1689 params.list_versions = bucket->versioned();
1690 params.allow_unordered = true;
81eedcae 1691
81eedcae 1692 do {
20effc67 1693 int ret = bucket->list(dpp, params, listing_max_entries, results, null_yield);
81eedcae 1694 if (ret < 0) {
b3b6e05e 1695 ldpp_dout(dpp, -1) << "ERROR failed to list objects in the bucket" << dendl;
81eedcae
TL
1696 return ret;
1697 }
20effc67 1698 for (const auto& obj : results.objs) {
81eedcae
TL
1699 rgw_obj_key key(obj.key);
1700 utime_t delete_at;
20effc67 1701 if (has_object_expired(dpp, store, bucket, key, delete_at)) {
81eedcae
TL
1702 formatter->open_object_section("object_status");
1703 formatter->dump_string("object", key.name);
1704 formatter->dump_stream("delete_at") << delete_at;
1705
1706 if (!dry_run) {
20effc67 1707 ret = rgw_remove_object(dpp, store, bucket, key);
81eedcae
TL
1708 formatter->dump_int("status", ret);
1709 }
1710
1711 formatter->close_section(); // object_status
1712 }
1713 }
1714 formatter->flush(cout); // regularly flush every 1k entries
20effc67 1715 } while (results.is_truncated);
81eedcae
TL
1716
1717 return 0;
1718}
1719
20effc67 1720int RGWBucketAdminOp::fix_obj_expiry(rgw::sal::Store* store,
9f95a23c 1721 RGWBucketAdminOpState& op_state,
b3b6e05e
TL
1722 RGWFormatterFlusher& flusher,
1723 const DoutPrefixProvider *dpp, bool dry_run)
81eedcae
TL
1724{
1725 RGWBucket admin_bucket;
b3b6e05e 1726 int ret = admin_bucket.init(store, op_state, null_yield, dpp);
81eedcae 1727 if (ret < 0) {
b3b6e05e 1728 ldpp_dout(dpp, -1) << "failed to initialize bucket" << dendl;
81eedcae
TL
1729 return ret;
1730 }
20effc67
TL
1731 std::unique_ptr<rgw::sal::Bucket> bucket;
1732 ret = store->get_bucket(nullptr, admin_bucket.get_bucket_info(), &bucket);
1733 if (ret < 0) {
1734 return ret;
1735 }
81eedcae 1736
20effc67 1737 return fix_bucket_obj_expiry(dpp, store, bucket.get(), flusher, dry_run);
81eedcae
TL
1738}
1739
7c673cae
FG
1740void RGWBucketCompleteInfo::dump(Formatter *f) const {
1741 encode_json("bucket_info", info, f);
1742 encode_json("attrs", attrs, f);
1743}
1744
1745void RGWBucketCompleteInfo::decode_json(JSONObj *obj) {
1746 JSONDecoder::decode_json("bucket_info", info, obj);
1747 JSONDecoder::decode_json("attrs", attrs, obj);
1748}
1749
9f95a23c 1750class RGWBucketMetadataHandler : public RGWBucketMetadataHandlerBase {
7c673cae 1751public:
9f95a23c
TL
1752 struct Svc {
1753 RGWSI_Bucket *bucket{nullptr};
1754 } svc;
7c673cae 1755
9f95a23c
TL
1756 struct Ctl {
1757 RGWBucketCtl *bucket{nullptr};
1758 } ctl;
7c673cae 1759
9f95a23c 1760 RGWBucketMetadataHandler() {}
7c673cae 1761
9f95a23c
TL
1762 void init(RGWSI_Bucket *bucket_svc,
1763 RGWBucketCtl *bucket_ctl) override {
1764 base_init(bucket_svc->ctx(),
1765 bucket_svc->get_ep_be_handler().get());
1766 svc.bucket = bucket_svc;
1767 ctl.bucket = bucket_ctl;
1768 }
7c673cae 1769
9f95a23c 1770 string get_type() override { return "bucket"; }
7c673cae 1771
9f95a23c
TL
1772 RGWMetadataObject *get_meta_obj(JSONObj *jo, const obj_version& objv, const ceph::real_time& mtime) override {
1773 RGWBucketEntryPoint be;
7c673cae 1774
7c673cae 1775 try {
9f95a23c 1776 decode_json_obj(be, jo);
7c673cae 1777 } catch (JSONDecoder::err& e) {
9f95a23c 1778 return nullptr;
7c673cae
FG
1779 }
1780
9f95a23c
TL
1781 return new RGWBucketEntryMetadataObject(be, objv, mtime);
1782 }
7c673cae 1783
b3b6e05e 1784 int do_get(RGWSI_MetaBackend_Handler::Op *op, string& entry, RGWMetadataObject **obj, optional_yield y, const DoutPrefixProvider *dpp) override {
9f95a23c
TL
1785 RGWObjVersionTracker ot;
1786 RGWBucketEntryPoint be;
7c673cae 1787
9f95a23c
TL
1788 real_time mtime;
1789 map<string, bufferlist> attrs;
7c673cae 1790
9f95a23c 1791 RGWSI_Bucket_EP_Ctx ctx(op->ctx());
7c673cae 1792
b3b6e05e 1793 int ret = svc.bucket->read_bucket_entrypoint_info(ctx, entry, &be, &ot, &mtime, &attrs, y, dpp);
7c673cae
FG
1794 if (ret < 0)
1795 return ret;
1796
9f95a23c 1797 RGWBucketEntryMetadataObject *mdo = new RGWBucketEntryMetadataObject(be, ot.read_version, mtime, std::move(attrs));
7c673cae 1798
9f95a23c
TL
1799 *obj = mdo;
1800
1801 return 0;
7c673cae
FG
1802 }
1803
9f95a23c
TL
1804 int do_put(RGWSI_MetaBackend_Handler::Op *op, string& entry,
1805 RGWMetadataObject *obj,
1806 RGWObjVersionTracker& objv_tracker,
1807 optional_yield y,
b3b6e05e 1808 const DoutPrefixProvider *dpp,
f67539c2 1809 RGWMDLogSyncType type, bool from_remote_zone) override;
7c673cae 1810
9f95a23c 1811 int do_remove(RGWSI_MetaBackend_Handler::Op *op, string& entry, RGWObjVersionTracker& objv_tracker,
b3b6e05e 1812 optional_yield y, const DoutPrefixProvider *dpp) override {
7c673cae 1813 RGWBucketEntryPoint be;
7c673cae 1814
9f95a23c
TL
1815 real_time orig_mtime;
1816
1817 RGWSI_Bucket_EP_Ctx ctx(op->ctx());
1818
b3b6e05e 1819 int ret = svc.bucket->read_bucket_entrypoint_info(ctx, entry, &be, &objv_tracker, &orig_mtime, nullptr, y, dpp);
7c673cae
FG
1820 if (ret < 0)
1821 return ret;
1822
1823 /*
31f18b77 1824 * We're unlinking the bucket but we don't want to update the entrypoint here - we're removing
7c673cae
FG
1825 * it immediately and don't want to invalidate our cached objv_version or the bucket obj removal
1826 * will incorrectly fail.
1827 */
b3b6e05e 1828 ret = ctl.bucket->unlink_bucket(be.owner, be.bucket, y, dpp, false);
7c673cae 1829 if (ret < 0) {
b3b6e05e 1830 ldpp_dout(dpp, -1) << "could not unlink bucket=" << entry << " owner=" << be.owner << dendl;
7c673cae
FG
1831 }
1832
b3b6e05e 1833 ret = svc.bucket->remove_bucket_entrypoint_info(ctx, entry, &objv_tracker, y, dpp);
7c673cae 1834 if (ret < 0) {
b3b6e05e 1835 ldpp_dout(dpp, -1) << "could not delete bucket=" << entry << dendl;
7c673cae
FG
1836 }
1837 /* idempotent */
1838 return 0;
1839 }
1840
9f95a23c
TL
1841 int call(std::function<int(RGWSI_Bucket_EP_Ctx& ctx)> f) {
1842 return call(nullopt, f);
7c673cae
FG
1843 }
1844
9f95a23c
TL
1845 int call(std::optional<RGWSI_MetaBackend_CtxParams> bectx_params,
1846 std::function<int(RGWSI_Bucket_EP_Ctx& ctx)> f) {
1847 return be_handler->call(bectx_params, [&](RGWSI_MetaBackend_Handler::Op *op) {
1848 RGWSI_Bucket_EP_Ctx ctx(op->ctx());
1849 return f(ctx);
1850 });
1851 }
1852};
7c673cae 1853
9f95a23c
TL
1854class RGWMetadataHandlerPut_Bucket : public RGWMetadataHandlerPut_SObj
1855{
1856 RGWBucketMetadataHandler *bhandler;
1857 RGWBucketEntryMetadataObject *obj;
1858public:
1859 RGWMetadataHandlerPut_Bucket(RGWBucketMetadataHandler *_handler,
1860 RGWSI_MetaBackend_Handler::Op *op, string& entry,
1861 RGWMetadataObject *_obj, RGWObjVersionTracker& objv_tracker,
1862 optional_yield y,
f67539c2 1863 RGWMDLogSyncType type, bool from_remote_zone) : RGWMetadataHandlerPut_SObj(_handler, op, entry, obj, objv_tracker, y, type, from_remote_zone),
9f95a23c
TL
1864 bhandler(_handler) {
1865 obj = static_cast<RGWBucketEntryMetadataObject *>(_obj);
1866 }
1867 ~RGWMetadataHandlerPut_Bucket() {}
7c673cae 1868
9f95a23c
TL
1869 void encode_obj(bufferlist *bl) override {
1870 obj->get_ep().encode(*bl);
7c673cae
FG
1871 }
1872
b3b6e05e
TL
1873 int put_checked(const DoutPrefixProvider *dpp) override;
1874 int put_post(const DoutPrefixProvider *dpp) override;
9f95a23c 1875};
7c673cae 1876
9f95a23c
TL
1877int RGWBucketMetadataHandler::do_put(RGWSI_MetaBackend_Handler::Op *op, string& entry,
1878 RGWMetadataObject *obj,
1879 RGWObjVersionTracker& objv_tracker,
1880 optional_yield y,
b3b6e05e 1881 const DoutPrefixProvider *dpp,
f67539c2 1882 RGWMDLogSyncType type, bool from_remote_zone)
9f95a23c 1883{
f67539c2 1884 RGWMetadataHandlerPut_Bucket put_op(this, op, entry, obj, objv_tracker, y, type, from_remote_zone);
b3b6e05e 1885 return do_put_operate(&put_op, dpp);
9f95a23c 1886}
7c673cae 1887
b3b6e05e 1888int RGWMetadataHandlerPut_Bucket::put_checked(const DoutPrefixProvider *dpp)
9f95a23c
TL
1889{
1890 RGWBucketEntryMetadataObject *orig_obj = static_cast<RGWBucketEntryMetadataObject *>(old_obj);
7c673cae 1891
9f95a23c
TL
1892 if (orig_obj) {
1893 obj->set_pattrs(&orig_obj->get_attrs());
1894 }
7c673cae 1895
9f95a23c
TL
1896 auto& be = obj->get_ep();
1897 auto mtime = obj->get_mtime();
1898 auto pattrs = obj->get_pattrs();
7c673cae 1899
9f95a23c 1900 RGWSI_Bucket_EP_Ctx ctx(op->ctx());
7c673cae 1901
9f95a23c
TL
1902 return bhandler->svc.bucket->store_bucket_entrypoint_info(ctx, entry,
1903 be,
1904 false,
1905 mtime,
1906 pattrs,
1907 &objv_tracker,
b3b6e05e
TL
1908 y,
1909 dpp);
9f95a23c 1910}
7c673cae 1911
b3b6e05e 1912int RGWMetadataHandlerPut_Bucket::put_post(const DoutPrefixProvider *dpp)
9f95a23c
TL
1913{
1914 auto& be = obj->get_ep();
7c673cae 1915
9f95a23c 1916 int ret;
7c673cae 1917
9f95a23c
TL
1918 /* link bucket */
1919 if (be.linked) {
b3b6e05e 1920 ret = bhandler->ctl.bucket->link_bucket(be.owner, be.bucket, be.creation_time, y, dpp, false);
9f95a23c 1921 } else {
b3b6e05e 1922 ret = bhandler->ctl.bucket->unlink_bucket(be.owner, be.bucket, y, dpp, false);
7c673cae 1923 }
181888fb 1924
9f95a23c
TL
1925 return ret;
1926}
7c673cae 1927
9f95a23c 1928static void get_md5_digest(const RGWBucketEntryPoint *be, string& md5_digest) {
11fdf7f2
TL
1929
1930 char md5[CEPH_CRYPTO_MD5_DIGESTSIZE * 2 + 1];
1931 unsigned char m[CEPH_CRYPTO_MD5_DIGESTSIZE];
1932 bufferlist bl;
1933
1934 Formatter *f = new JSONFormatter(false);
1935 be->dump(f);
1936 f->flush(bl);
1937
1938 MD5 hash;
20effc67
TL
1939 // Allow use of MD5 digest in FIPS mode for non-cryptographic purposes
1940 hash.SetFlags(EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);
11fdf7f2
TL
1941 hash.Update((const unsigned char *)bl.c_str(), bl.length());
1942 hash.Final(m);
1943
1944 buf_to_hex(m, CEPH_CRYPTO_MD5_DIGESTSIZE, md5);
1945
1946 delete f;
1947
1948 md5_digest = md5;
1949}
1950
1951#define ARCHIVE_META_ATTR RGW_ATTR_PREFIX "zone.archive.info"
1952
1953struct archive_meta_info {
1954 rgw_bucket orig_bucket;
1955
1956 bool from_attrs(CephContext *cct, map<string, bufferlist>& attrs) {
1957 auto iter = attrs.find(ARCHIVE_META_ATTR);
1958 if (iter == attrs.end()) {
1959 return false;
1960 }
1961
1962 auto bliter = iter->second.cbegin();
1963 try {
1964 decode(bliter);
1965 } catch (buffer::error& err) {
1966 ldout(cct, 0) << "ERROR: failed to decode archive meta info" << dendl;
1967 return false;
1968 }
1969
1970 return true;
1971 }
1972
1973 void store_in_attrs(map<string, bufferlist>& attrs) const {
1974 encode(attrs[ARCHIVE_META_ATTR]);
1975 }
1976
1977 void encode(bufferlist& bl) const {
1978 ENCODE_START(1, 1, bl);
1979 encode(orig_bucket, bl);
1980 ENCODE_FINISH(bl);
1981 }
1982
1983 void decode(bufferlist::const_iterator& bl) {
1984 DECODE_START(1, bl);
1985 decode(orig_bucket, bl);
1986 DECODE_FINISH(bl);
1987 }
1988};
1989WRITE_CLASS_ENCODER(archive_meta_info)
1990
1991class RGWArchiveBucketMetadataHandler : public RGWBucketMetadataHandler {
1992public:
9f95a23c
TL
1993 RGWArchiveBucketMetadataHandler() {}
1994
1995 int do_remove(RGWSI_MetaBackend_Handler::Op *op, string& entry, RGWObjVersionTracker& objv_tracker,
b3b6e05e 1996 optional_yield y, const DoutPrefixProvider *dpp) override {
9f95a23c
TL
1997 auto cct = svc.bucket->ctx();
1998
1999 RGWSI_Bucket_EP_Ctx ctx(op->ctx());
2000
b3b6e05e 2001 ldpp_dout(dpp, 5) << "SKIP: bucket removal is not allowed on archive zone: bucket:" << entry << " ... proceeding to rename" << dendl;
11fdf7f2
TL
2002
2003 string tenant_name, bucket_name;
2004 parse_bucket(entry, &tenant_name, &bucket_name);
9f95a23c
TL
2005 rgw_bucket entry_bucket;
2006 entry_bucket.tenant = tenant_name;
2007 entry_bucket.name = bucket_name;
11fdf7f2
TL
2008
2009 real_time mtime;
2010
2011 /* read original entrypoint */
2012
2013 RGWBucketEntryPoint be;
11fdf7f2 2014 map<string, bufferlist> attrs;
b3b6e05e 2015 int ret = svc.bucket->read_bucket_entrypoint_info(ctx, entry, &be, &objv_tracker, &mtime, &attrs, y, dpp);
11fdf7f2
TL
2016 if (ret < 0) {
2017 return ret;
2018 }
2019
9f95a23c 2020 string bi_meta_name = RGWSI_Bucket::get_bi_meta_key(be.bucket);
11fdf7f2
TL
2021
2022 /* read original bucket instance info */
2023
2024 map<string, bufferlist> attrs_m;
2025 ceph::real_time orig_mtime;
2026 RGWBucketInfo old_bi;
2027
b3b6e05e 2028 ret = ctl.bucket->read_bucket_instance_info(be.bucket, &old_bi, y, dpp, RGWBucketCtl::BucketInstance::GetParams()
9f95a23c
TL
2029 .set_mtime(&orig_mtime)
2030 .set_attrs(&attrs_m));
11fdf7f2
TL
2031 if (ret < 0) {
2032 return ret;
2033 }
2034
2035 archive_meta_info ami;
2036
9f95a23c 2037 if (!ami.from_attrs(svc.bucket->ctx(), attrs_m)) {
11fdf7f2
TL
2038 ami.orig_bucket = old_bi.bucket;
2039 ami.store_in_attrs(attrs_m);
2040 }
2041
2042 /* generate a new bucket instance. We could have avoided this if we could just point a new
2043 * bucket entry point to the old bucket instance, however, due to limitation in the way
2044 * we index buckets under the user, bucket entrypoint and bucket instance of the same
2045 * bucket need to have the same name, so we need to copy the old bucket instance into
2046 * to a new entry with the new name
2047 */
2048
2049 string new_bucket_name;
2050
2051 RGWBucketInfo new_bi = old_bi;
2052 RGWBucketEntryPoint new_be = be;
2053
2054 string md5_digest;
2055
2056 get_md5_digest(&new_be, md5_digest);
2057 new_bucket_name = ami.orig_bucket.name + "-deleted-" + md5_digest;
2058
2059 new_bi.bucket.name = new_bucket_name;
2060 new_bi.objv_tracker.clear();
2061
2062 new_be.bucket.name = new_bucket_name;
2063
b3b6e05e 2064 ret = ctl.bucket->store_bucket_instance_info(be.bucket, new_bi, y, dpp, RGWBucketCtl::BucketInstance::PutParams()
9f95a23c
TL
2065 .set_exclusive(false)
2066 .set_mtime(orig_mtime)
2067 .set_attrs(&attrs_m)
2068 .set_orig_info(&old_bi));
11fdf7f2 2069 if (ret < 0) {
b3b6e05e 2070 ldpp_dout(dpp, 0) << "ERROR: failed to put new bucket instance info for bucket=" << new_bi.bucket << " ret=" << ret << dendl;
11fdf7f2
TL
2071 return ret;
2072 }
2073
2074 /* store a new entrypoint */
2075
2076 RGWObjVersionTracker ot;
9f95a23c 2077 ot.generate_new_write_ver(cct);
11fdf7f2 2078
9f95a23c 2079 ret = svc.bucket->store_bucket_entrypoint_info(ctx, RGWSI_Bucket::get_entrypoint_meta_key(new_be.bucket),
b3b6e05e 2080 new_be, true, mtime, &attrs, nullptr, y, dpp);
11fdf7f2 2081 if (ret < 0) {
b3b6e05e 2082 ldpp_dout(dpp, 0) << "ERROR: failed to put new bucket entrypoint for bucket=" << new_be.bucket << " ret=" << ret << dendl;
11fdf7f2
TL
2083 return ret;
2084 }
2085
2086 /* link new bucket */
2087
b3b6e05e 2088 ret = ctl.bucket->link_bucket(new_be.owner, new_be.bucket, new_be.creation_time, y, dpp, false);
11fdf7f2 2089 if (ret < 0) {
b3b6e05e 2090 ldpp_dout(dpp, 0) << "ERROR: failed to link new bucket for bucket=" << new_be.bucket << " ret=" << ret << dendl;
11fdf7f2
TL
2091 return ret;
2092 }
2093
2094 /* clean up old stuff */
2095
b3b6e05e 2096 ret = ctl.bucket->unlink_bucket(be.owner, entry_bucket, y, dpp, false);
11fdf7f2 2097 if (ret < 0) {
b3b6e05e 2098 ldpp_dout(dpp, -1) << "could not unlink bucket=" << entry << " owner=" << be.owner << dendl;
11fdf7f2
TL
2099 }
2100
2101 // if (ret == -ECANCELED) it means that there was a race here, and someone
2102 // wrote to the bucket entrypoint just before we removed it. The question is
2103 // whether it was a newly created bucket entrypoint ... in which case we
2104 // should ignore the error and move forward, or whether it is a higher version
2105 // of the same bucket instance ... in which we should retry
9f95a23c
TL
2106 ret = svc.bucket->remove_bucket_entrypoint_info(ctx,
2107 RGWSI_Bucket::get_entrypoint_meta_key(be.bucket),
2108 &objv_tracker,
b3b6e05e
TL
2109 y,
2110 dpp);
9f95a23c 2111 if (ret < 0) {
b3b6e05e 2112 ldpp_dout(dpp, 0) << "ERROR: failed to put new bucket entrypoint for bucket=" << new_be.bucket << " ret=" << ret << dendl;
9f95a23c
TL
2113 return ret;
2114 }
2115
b3b6e05e 2116 ret = ctl.bucket->remove_bucket_instance_info(be.bucket, old_bi, y, dpp);
11fdf7f2 2117 if (ret < 0) {
b3b6e05e 2118 ldpp_dout(dpp, -1) << "could not delete bucket=" << entry << dendl;
11fdf7f2
TL
2119 }
2120
11fdf7f2
TL
2121
2122 /* idempotent */
2123
2124 return 0;
2125 }
2126
9f95a23c
TL
2127 int do_put(RGWSI_MetaBackend_Handler::Op *op, string& entry,
2128 RGWMetadataObject *obj,
2129 RGWObjVersionTracker& objv_tracker,
b3b6e05e 2130 optional_yield y, const DoutPrefixProvider *dpp,
f67539c2 2131 RGWMDLogSyncType type, bool from_remote_zone) override {
11fdf7f2
TL
2132 if (entry.find("-deleted-") != string::npos) {
2133 RGWObjVersionTracker ot;
2134 RGWMetadataObject *robj;
b3b6e05e 2135 int ret = do_get(op, entry, &robj, y, dpp);
11fdf7f2
TL
2136 if (ret != -ENOENT) {
2137 if (ret < 0) {
2138 return ret;
2139 }
2140 ot.read_version = robj->get_version();
2141 delete robj;
2142
b3b6e05e 2143 ret = do_remove(op, entry, ot, y, dpp);
11fdf7f2
TL
2144 if (ret < 0) {
2145 return ret;
2146 }
2147 }
2148 }
2149
9f95a23c 2150 return RGWBucketMetadataHandler::do_put(op, entry, obj,
b3b6e05e 2151 objv_tracker, y, dpp, type, from_remote_zone);
11fdf7f2
TL
2152 }
2153
2154};
2155
9f95a23c
TL
2156class RGWBucketInstanceMetadataHandler : public RGWBucketInstanceMetadataHandlerBase {
2157 int read_bucket_instance_entry(RGWSI_Bucket_BI_Ctx& ctx,
2158 const string& entry,
2159 RGWBucketCompleteInfo *bi,
2160 ceph::real_time *pmtime,
b3b6e05e
TL
2161 optional_yield y,
2162 const DoutPrefixProvider *dpp) {
9f95a23c
TL
2163 return svc.bucket->read_bucket_instance_info(ctx,
2164 entry,
2165 &bi->info,
2166 pmtime, &bi->attrs,
b3b6e05e
TL
2167 y,
2168 dpp);
9f95a23c 2169 }
7c673cae
FG
2170
2171public:
9f95a23c
TL
2172 struct Svc {
2173 RGWSI_Zone *zone{nullptr};
2174 RGWSI_Bucket *bucket{nullptr};
2175 RGWSI_BucketIndex *bi{nullptr};
2176 } svc;
2177
2178 RGWBucketInstanceMetadataHandler() {}
2179
2180 void init(RGWSI_Zone *zone_svc,
f67539c2
TL
2181 RGWSI_Bucket *bucket_svc,
2182 RGWSI_BucketIndex *bi_svc) override {
9f95a23c
TL
2183 base_init(bucket_svc->ctx(),
2184 bucket_svc->get_bi_be_handler().get());
2185 svc.zone = zone_svc;
2186 svc.bucket = bucket_svc;
2187 svc.bi = bi_svc;
2188 }
2189
7c673cae
FG
2190 string get_type() override { return "bucket.instance"; }
2191
9f95a23c 2192 RGWMetadataObject *get_meta_obj(JSONObj *jo, const obj_version& objv, const ceph::real_time& mtime) override {
7c673cae
FG
2193 RGWBucketCompleteInfo bci;
2194
9f95a23c
TL
2195 try {
2196 decode_json_obj(bci, jo);
2197 } catch (JSONDecoder::err& e) {
2198 return nullptr;
2199 }
2200
2201 return new RGWBucketInstanceMetadataObject(bci, objv, mtime);
2202 }
2203
b3b6e05e 2204 int do_get(RGWSI_MetaBackend_Handler::Op *op, string& entry, RGWMetadataObject **obj, optional_yield y, const DoutPrefixProvider *dpp) override {
9f95a23c 2205 RGWBucketCompleteInfo bci;
7c673cae 2206 real_time mtime;
7c673cae 2207
9f95a23c
TL
2208 RGWSI_Bucket_BI_Ctx ctx(op->ctx());
2209
b3b6e05e 2210 int ret = svc.bucket->read_bucket_instance_info(ctx, entry, &bci.info, &mtime, &bci.attrs, y, dpp);
7c673cae
FG
2211 if (ret < 0)
2212 return ret;
2213
2214 RGWBucketInstanceMetadataObject *mdo = new RGWBucketInstanceMetadataObject(bci, bci.info.objv_tracker.read_version, mtime);
2215
2216 *obj = mdo;
2217
2218 return 0;
2219 }
2220
9f95a23c
TL
2221 int do_put(RGWSI_MetaBackend_Handler::Op *op, string& entry,
2222 RGWMetadataObject *_obj, RGWObjVersionTracker& objv_tracker,
b3b6e05e 2223 optional_yield y, const DoutPrefixProvider *dpp,
f67539c2 2224 RGWMDLogSyncType sync_type, bool from_remote_zone) override;
7c673cae 2225
9f95a23c 2226 int do_remove(RGWSI_MetaBackend_Handler::Op *op, string& entry, RGWObjVersionTracker& objv_tracker,
b3b6e05e 2227 optional_yield y, const DoutPrefixProvider *dpp) override {
9f95a23c 2228 RGWBucketCompleteInfo bci;
7c673cae 2229
9f95a23c
TL
2230 RGWSI_Bucket_BI_Ctx ctx(op->ctx());
2231
b3b6e05e 2232 int ret = read_bucket_instance_entry(ctx, entry, &bci, nullptr, y, dpp);
9f95a23c 2233 if (ret < 0 && ret != -ENOENT)
7c673cae
FG
2234 return ret;
2235
b3b6e05e 2236 return svc.bucket->remove_bucket_instance_info(ctx, entry, bci.info, &bci.info.objv_tracker, y, dpp);
9f95a23c 2237 }
7c673cae 2238
9f95a23c
TL
2239 int call(std::function<int(RGWSI_Bucket_BI_Ctx& ctx)> f) {
2240 return call(nullopt, f);
2241 }
c07f9fc5 2242
9f95a23c
TL
2243 int call(std::optional<RGWSI_MetaBackend_CtxParams> bectx_params,
2244 std::function<int(RGWSI_Bucket_BI_Ctx& ctx)> f) {
2245 return be_handler->call(bectx_params, [&](RGWSI_MetaBackend_Handler::Op *op) {
2246 RGWSI_Bucket_BI_Ctx ctx(op->ctx());
2247 return f(ctx);
2248 });
2249 }
2250};
c07f9fc5 2251
9f95a23c
TL
2252class RGWMetadataHandlerPut_BucketInstance : public RGWMetadataHandlerPut_SObj
2253{
2254 CephContext *cct;
2255 RGWBucketInstanceMetadataHandler *bihandler;
2256 RGWBucketInstanceMetadataObject *obj;
2257public:
f67539c2 2258 RGWMetadataHandlerPut_BucketInstance(CephContext *_cct,
9f95a23c
TL
2259 RGWBucketInstanceMetadataHandler *_handler,
2260 RGWSI_MetaBackend_Handler::Op *_op, string& entry,
2261 RGWMetadataObject *_obj, RGWObjVersionTracker& objv_tracker,
2262 optional_yield y,
f67539c2
TL
2263 RGWMDLogSyncType type, bool from_remote_zone) : RGWMetadataHandlerPut_SObj(_handler, _op, entry, obj, objv_tracker, y, type, from_remote_zone),
2264 cct(_cct), bihandler(_handler) {
9f95a23c 2265 obj = static_cast<RGWBucketInstanceMetadataObject *>(_obj);
c07f9fc5 2266
9f95a23c
TL
2267 auto& bci = obj->get_bci();
2268 obj->set_pattrs(&bci.attrs);
2269 }
7c673cae 2270
9f95a23c
TL
2271 void encode_obj(bufferlist *bl) override {
2272 obj->get_bucket_info().encode(*bl);
2273 }
7c673cae 2274
b3b6e05e
TL
2275 int put_check(const DoutPrefixProvider *dpp) override;
2276 int put_checked(const DoutPrefixProvider *dpp) override;
2277 int put_post(const DoutPrefixProvider *dpp) override;
9f95a23c 2278};
7c673cae 2279
9f95a23c
TL
2280int RGWBucketInstanceMetadataHandler::do_put(RGWSI_MetaBackend_Handler::Op *op,
2281 string& entry,
2282 RGWMetadataObject *obj,
2283 RGWObjVersionTracker& objv_tracker,
2284 optional_yield y,
b3b6e05e 2285 const DoutPrefixProvider *dpp,
f67539c2 2286 RGWMDLogSyncType type, bool from_remote_zone)
9f95a23c
TL
2287{
2288 RGWMetadataHandlerPut_BucketInstance put_op(svc.bucket->ctx(), this, op, entry, obj,
f67539c2 2289 objv_tracker, y, type, from_remote_zone);
b3b6e05e 2290 return do_put_operate(&put_op, dpp);
9f95a23c 2291}
7c673cae 2292
f67539c2
TL
2293void init_default_bucket_layout(CephContext *cct, rgw::BucketLayout& layout,
2294 const RGWZone& zone,
2295 std::optional<uint32_t> shards,
2296 std::optional<rgw::BucketIndexType> type) {
2297 layout.current_index.gen = 0;
2298 layout.current_index.layout.normal.hash_type = rgw::BucketHashType::Mod;
2299
2300 layout.current_index.layout.type =
2301 type.value_or(rgw::BucketIndexType::Normal);
2302
2303 if (shards) {
2304 layout.current_index.layout.normal.num_shards = *shards;
2305 } else if (cct->_conf->rgw_override_bucket_index_max_shards > 0) {
2306 layout.current_index.layout.normal.num_shards =
2307 cct->_conf->rgw_override_bucket_index_max_shards;
2308 } else {
2309 layout.current_index.layout.normal.num_shards =
2310 zone.bucket_index_max_shards;
2311 }
2312
2313 if (layout.current_index.layout.type == rgw::BucketIndexType::Normal) {
2314 layout.logs.push_back(log_layout_from_index(
2315 layout.current_index.gen,
2316 layout.current_index.layout.normal));
2317 }
2318}
2319
b3b6e05e 2320int RGWMetadataHandlerPut_BucketInstance::put_check(const DoutPrefixProvider *dpp)
9f95a23c
TL
2321{
2322 int ret;
7c673cae 2323
9f95a23c 2324 RGWBucketCompleteInfo& bci = obj->get_bci();
7c673cae 2325
9f95a23c 2326 RGWBucketInstanceMetadataObject *orig_obj = static_cast<RGWBucketInstanceMetadataObject *>(old_obj);
7c673cae 2327
9f95a23c 2328 RGWBucketCompleteInfo *old_bci = (orig_obj ? &orig_obj->get_bci() : nullptr);
7c673cae 2329
f67539c2
TL
2330 const bool exists = (!!orig_obj);
2331
2332 if (from_remote_zone) {
2333 // don't sync bucket layout changes
2334 if (!exists) {
2335 auto& bci_index = bci.info.layout.current_index.layout;
2336 auto index_type = bci_index.type;
2337 auto num_shards = bci_index.normal.num_shards;
2338 init_default_bucket_layout(cct, bci.info.layout,
2339 bihandler->svc.zone->get_zone(),
2340 num_shards, index_type);
2341 } else {
2342 bci.info.layout = old_bci->info.layout;
2343 }
2344 }
7c673cae 2345
9f95a23c
TL
2346 if (!exists || old_bci->info.bucket.bucket_id != bci.info.bucket.bucket_id) {
2347 /* a new bucket, we need to select a new bucket placement for it */
2348 string tenant_name;
2349 string bucket_name;
2350 string bucket_instance;
2351 parse_bucket(entry, &tenant_name, &bucket_name, &bucket_instance);
7c673cae 2352
9f95a23c
TL
2353 RGWZonePlacementInfo rule_info;
2354 bci.info.bucket.name = bucket_name;
2355 bci.info.bucket.bucket_id = bucket_instance;
2356 bci.info.bucket.tenant = tenant_name;
2357 // if the sync module never writes data, don't require the zone to specify all placement targets
2358 if (bihandler->svc.zone->sync_module_supports_writes()) {
b3b6e05e 2359 ret = bihandler->svc.zone->select_bucket_location_by_rule(dpp, bci.info.placement_rule, &rule_info, y);
9f95a23c 2360 if (ret < 0) {
b3b6e05e 2361 ldpp_dout(dpp, 0) << "ERROR: select_bucket_placement() returned " << ret << dendl;
9f95a23c
TL
2362 return ret;
2363 }
2364 }
f67539c2 2365 bci.info.layout.current_index.layout.type = rule_info.index_type;
9f95a23c
TL
2366 } else {
2367 /* existing bucket, keep its placement */
2368 bci.info.bucket.explicit_placement = old_bci->info.bucket.explicit_placement;
2369 bci.info.placement_rule = old_bci->info.placement_rule;
7c673cae
FG
2370 }
2371
9f95a23c
TL
2372 /* record the read version (if any), store the new version */
2373 bci.info.objv_tracker.read_version = objv_tracker.read_version;
2374 bci.info.objv_tracker.write_version = objv_tracker.write_version;
7c673cae 2375
9f95a23c
TL
2376 return 0;
2377}
7c673cae 2378
b3b6e05e 2379int RGWMetadataHandlerPut_BucketInstance::put_checked(const DoutPrefixProvider *dpp)
9f95a23c
TL
2380{
2381 RGWBucketInstanceMetadataObject *orig_obj = static_cast<RGWBucketInstanceMetadataObject *>(old_obj);
7c673cae 2382
9f95a23c 2383 RGWBucketInfo *orig_info = (orig_obj ? &orig_obj->get_bucket_info() : nullptr);
7c673cae 2384
9f95a23c
TL
2385 auto& info = obj->get_bucket_info();
2386 auto mtime = obj->get_mtime();
2387 auto pattrs = obj->get_pattrs();
7c673cae 2388
9f95a23c 2389 RGWSI_Bucket_BI_Ctx ctx(op->ctx());
7c673cae 2390
9f95a23c
TL
2391 return bihandler->svc.bucket->store_bucket_instance_info(ctx,
2392 entry,
2393 info,
2394 orig_info,
2395 false,
2396 mtime,
2397 pattrs,
b3b6e05e
TL
2398 y,
2399 dpp);
9f95a23c 2400}
7c673cae 2401
b3b6e05e 2402int RGWMetadataHandlerPut_BucketInstance::put_post(const DoutPrefixProvider *dpp)
9f95a23c
TL
2403{
2404 RGWBucketCompleteInfo& bci = obj->get_bci();
7c673cae 2405
9f95a23c 2406 objv_tracker = bci.info.objv_tracker;
7c673cae 2407
b3b6e05e 2408 int ret = bihandler->svc.bi->init_index(dpp, bci.info);
9f95a23c
TL
2409 if (ret < 0) {
2410 return ret;
2411 }
2412
2413 return STATUS_APPLIED;
2414}
7c673cae 2415
9f95a23c
TL
2416class RGWArchiveBucketInstanceMetadataHandler : public RGWBucketInstanceMetadataHandler {
2417public:
2418 RGWArchiveBucketInstanceMetadataHandler() {}
7c673cae 2419
b3b6e05e
TL
2420 int do_remove(RGWSI_MetaBackend_Handler::Op *op, string& entry, RGWObjVersionTracker& objv_tracker, optional_yield y, const DoutPrefixProvider *dpp) override {
2421 ldpp_dout(dpp, 0) << "SKIP: bucket instance removal is not allowed on archive zone: bucket.instance:" << entry << dendl;
9f95a23c
TL
2422 return 0;
2423 }
2424};
2425
9f95a23c
TL
2426RGWBucketCtl::RGWBucketCtl(RGWSI_Zone *zone_svc,
2427 RGWSI_Bucket *bucket_svc,
2428 RGWSI_Bucket_Sync *bucket_sync_svc,
f67539c2 2429 RGWSI_BucketIndex *bi_svc) : cct(zone_svc->ctx())
9f95a23c
TL
2430{
2431 svc.zone = zone_svc;
2432 svc.bucket = bucket_svc;
2433 svc.bucket_sync = bucket_sync_svc;
2434 svc.bi = bi_svc;
2435}
2436
2437void RGWBucketCtl::init(RGWUserCtl *user_ctl,
2438 RGWBucketMetadataHandler *_bm_handler,
2439 RGWBucketInstanceMetadataHandler *_bmi_handler,
b3b6e05e
TL
2440 RGWDataChangesLog *datalog,
2441 const DoutPrefixProvider *dpp)
9f95a23c
TL
2442{
2443 ctl.user = user_ctl;
2444
2445 bm_handler = _bm_handler;
2446 bmi_handler = _bmi_handler;
2447
2448 bucket_be_handler = bm_handler->get_be_handler();
2449 bi_be_handler = bmi_handler->get_be_handler();
2450
f67539c2 2451 datalog->set_bucket_filter(
b3b6e05e
TL
2452 [this](const rgw_bucket& bucket, optional_yield y, const DoutPrefixProvider *dpp) {
2453 return bucket_exports_data(bucket, y, dpp);
f67539c2 2454 });
9f95a23c
TL
2455}
2456
2457int RGWBucketCtl::call(std::function<int(RGWSI_Bucket_X_Ctx& ctx)> f) {
2458 return bm_handler->call([&](RGWSI_Bucket_EP_Ctx& ep_ctx) {
2459 return bmi_handler->call([&](RGWSI_Bucket_BI_Ctx& bi_ctx) {
2460 RGWSI_Bucket_X_Ctx ctx{ep_ctx, bi_ctx};
2461 return f(ctx);
2462 });
2463 });
2464}
2465
2466int RGWBucketCtl::read_bucket_entrypoint_info(const rgw_bucket& bucket,
2467 RGWBucketEntryPoint *info,
b3b6e05e 2468 optional_yield y, const DoutPrefixProvider *dpp,
9f95a23c
TL
2469 const Bucket::GetParams& params)
2470{
2471 return bm_handler->call(params.bectx_params, [&](RGWSI_Bucket_EP_Ctx& ctx) {
2472 return svc.bucket->read_bucket_entrypoint_info(ctx,
2473 RGWSI_Bucket::get_entrypoint_meta_key(bucket),
2474 info,
2475 params.objv_tracker,
2476 params.mtime,
2477 params.attrs,
2478 y,
b3b6e05e 2479 dpp,
9f95a23c
TL
2480 params.cache_info,
2481 params.refresh_version);
2482 });
2483}
2484
2485int RGWBucketCtl::store_bucket_entrypoint_info(const rgw_bucket& bucket,
2486 RGWBucketEntryPoint& info,
2487 optional_yield y,
b3b6e05e 2488 const DoutPrefixProvider *dpp,
9f95a23c
TL
2489 const Bucket::PutParams& params)
2490{
2491 return bm_handler->call([&](RGWSI_Bucket_EP_Ctx& ctx) {
2492 return svc.bucket->store_bucket_entrypoint_info(ctx,
2493 RGWSI_Bucket::get_entrypoint_meta_key(bucket),
2494 info,
2495 params.exclusive,
2496 params.mtime,
2497 params.attrs,
2498 params.objv_tracker,
b3b6e05e
TL
2499 y,
2500 dpp);
9f95a23c
TL
2501 });
2502}
2503
2504int RGWBucketCtl::remove_bucket_entrypoint_info(const rgw_bucket& bucket,
2505 optional_yield y,
b3b6e05e 2506 const DoutPrefixProvider *dpp,
9f95a23c
TL
2507 const Bucket::RemoveParams& params)
2508{
2509 return bm_handler->call([&](RGWSI_Bucket_EP_Ctx& ctx) {
2510 return svc.bucket->remove_bucket_entrypoint_info(ctx,
2511 RGWSI_Bucket::get_entrypoint_meta_key(bucket),
2512 params.objv_tracker,
b3b6e05e
TL
2513 y,
2514 dpp);
9f95a23c
TL
2515 });
2516}
2517
2518int RGWBucketCtl::read_bucket_instance_info(const rgw_bucket& bucket,
2519 RGWBucketInfo *info,
2520 optional_yield y,
b3b6e05e 2521 const DoutPrefixProvider *dpp,
9f95a23c
TL
2522 const BucketInstance::GetParams& params)
2523{
2524 int ret = bmi_handler->call(params.bectx_params, [&](RGWSI_Bucket_BI_Ctx& ctx) {
2525 return svc.bucket->read_bucket_instance_info(ctx,
2526 RGWSI_Bucket::get_bi_meta_key(bucket),
2527 info,
2528 params.mtime,
2529 params.attrs,
2530 y,
b3b6e05e 2531 dpp,
9f95a23c
TL
2532 params.cache_info,
2533 params.refresh_version);
2534 });
2535
2536 if (ret < 0) {
2537 return ret;
2538 }
2539
2540 if (params.objv_tracker) {
2541 *params.objv_tracker = info->objv_tracker;
2542 }
2543
2544 return 0;
2545}
2546
2547int RGWBucketCtl::read_bucket_info(const rgw_bucket& bucket,
2548 RGWBucketInfo *info,
2549 optional_yield y,
b3b6e05e 2550 const DoutPrefixProvider *dpp,
9f95a23c
TL
2551 const BucketInstance::GetParams& params,
2552 RGWObjVersionTracker *ep_objv_tracker)
2553{
2554 const rgw_bucket *b = &bucket;
2555
2556 std::optional<RGWBucketEntryPoint> ep;
2557
2558 if (b->bucket_id.empty()) {
2559 ep.emplace();
2560
b3b6e05e 2561 int r = read_bucket_entrypoint_info(*b, &(*ep), y, dpp, RGWBucketCtl::Bucket::GetParams()
9f95a23c
TL
2562 .set_bectx_params(params.bectx_params)
2563 .set_objv_tracker(ep_objv_tracker));
2564 if (r < 0) {
2565 return r;
7c673cae
FG
2566 }
2567
9f95a23c
TL
2568 b = &ep->bucket;
2569 }
2570
2571 int ret = bmi_handler->call(params.bectx_params, [&](RGWSI_Bucket_BI_Ctx& ctx) {
2572 return svc.bucket->read_bucket_instance_info(ctx,
2573 RGWSI_Bucket::get_bi_meta_key(*b),
2574 info,
2575 params.mtime,
2576 params.attrs,
b3b6e05e 2577 y, dpp,
9f95a23c
TL
2578 params.cache_info,
2579 params.refresh_version);
2580 });
2581
2582 if (ret < 0) {
2583 return ret;
2584 }
2585
2586 if (params.objv_tracker) {
2587 *params.objv_tracker = info->objv_tracker;
2588 }
2589
2590 return 0;
2591}
2592
2593int RGWBucketCtl::do_store_bucket_instance_info(RGWSI_Bucket_BI_Ctx& ctx,
2594 const rgw_bucket& bucket,
2595 RGWBucketInfo& info,
2596 optional_yield y,
b3b6e05e 2597 const DoutPrefixProvider *dpp,
9f95a23c
TL
2598 const BucketInstance::PutParams& params)
2599{
2600 if (params.objv_tracker) {
2601 info.objv_tracker = *params.objv_tracker;
2602 }
2603
2604 return svc.bucket->store_bucket_instance_info(ctx,
2605 RGWSI_Bucket::get_bi_meta_key(bucket),
2606 info,
2607 params.orig_info,
2608 params.exclusive,
2609 params.mtime,
2610 params.attrs,
b3b6e05e
TL
2611 y,
2612 dpp);
9f95a23c
TL
2613}
2614
2615int RGWBucketCtl::store_bucket_instance_info(const rgw_bucket& bucket,
2616 RGWBucketInfo& info,
2617 optional_yield y,
b3b6e05e 2618 const DoutPrefixProvider *dpp,
9f95a23c
TL
2619 const BucketInstance::PutParams& params)
2620{
2621 return bmi_handler->call([&](RGWSI_Bucket_BI_Ctx& ctx) {
b3b6e05e 2622 return do_store_bucket_instance_info(ctx, bucket, info, y, dpp, params);
9f95a23c
TL
2623 });
2624}
2625
2626int RGWBucketCtl::remove_bucket_instance_info(const rgw_bucket& bucket,
2627 RGWBucketInfo& info,
2628 optional_yield y,
b3b6e05e 2629 const DoutPrefixProvider *dpp,
9f95a23c
TL
2630 const BucketInstance::RemoveParams& params)
2631{
2632 if (params.objv_tracker) {
2633 info.objv_tracker = *params.objv_tracker;
2634 }
2635
2636 return bmi_handler->call([&](RGWSI_Bucket_BI_Ctx& ctx) {
2637 return svc.bucket->remove_bucket_instance_info(ctx,
2638 RGWSI_Bucket::get_bi_meta_key(bucket),
2639 info,
2640 &info.objv_tracker,
b3b6e05e
TL
2641 y,
2642 dpp);
9f95a23c
TL
2643 });
2644}
2645
2646int RGWBucketCtl::do_store_linked_bucket_info(RGWSI_Bucket_X_Ctx& ctx,
2647 RGWBucketInfo& info,
2648 RGWBucketInfo *orig_info,
2649 bool exclusive, real_time mtime,
2650 obj_version *pep_objv,
2651 map<string, bufferlist> *pattrs,
2652 bool create_entry_point,
b3b6e05e 2653 optional_yield y, const DoutPrefixProvider *dpp)
9f95a23c
TL
2654{
2655 bool create_head = !info.has_instance_obj || create_entry_point;
2656
2657 int ret = svc.bucket->store_bucket_instance_info(ctx.bi,
2658 RGWSI_Bucket::get_bi_meta_key(info.bucket),
2659 info,
2660 orig_info,
2661 exclusive,
2662 mtime, pattrs,
b3b6e05e 2663 y, dpp);
9f95a23c
TL
2664 if (ret < 0) {
2665 return ret;
2666 }
2667
2668 if (!create_head)
2669 return 0; /* done! */
2670
2671 RGWBucketEntryPoint entry_point;
2672 entry_point.bucket = info.bucket;
2673 entry_point.owner = info.owner;
2674 entry_point.creation_time = info.creation_time;
2675 entry_point.linked = true;
2676 RGWObjVersionTracker ot;
2677 if (pep_objv && !pep_objv->tag.empty()) {
2678 ot.write_version = *pep_objv;
2679 } else {
2680 ot.generate_new_write_ver(cct);
2681 if (pep_objv) {
2682 *pep_objv = ot.write_version;
2683 }
2684 }
2685 ret = svc.bucket->store_bucket_entrypoint_info(ctx.ep,
2686 RGWSI_Bucket::get_entrypoint_meta_key(info.bucket),
2687 entry_point,
2688 exclusive,
2689 mtime,
2690 pattrs,
2691 &ot,
b3b6e05e
TL
2692 y,
2693 dpp);
9f95a23c
TL
2694 if (ret < 0)
2695 return ret;
2696
2697 return 0;
2698}
2699int RGWBucketCtl::convert_old_bucket_info(RGWSI_Bucket_X_Ctx& ctx,
2700 const rgw_bucket& bucket,
b3b6e05e
TL
2701 optional_yield y,
2702 const DoutPrefixProvider *dpp)
9f95a23c
TL
2703{
2704 RGWBucketEntryPoint entry_point;
2705 real_time ep_mtime;
2706 RGWObjVersionTracker ot;
2707 map<string, bufferlist> attrs;
2708 RGWBucketInfo info;
2709 auto cct = svc.bucket->ctx();
2710
b3b6e05e 2711 ldpp_dout(dpp, 10) << "RGWRados::convert_old_bucket_info(): bucket=" << bucket << dendl;
9f95a23c
TL
2712
2713 int ret = svc.bucket->read_bucket_entrypoint_info(ctx.ep,
2714 RGWSI_Bucket::get_entrypoint_meta_key(bucket),
b3b6e05e 2715 &entry_point, &ot, &ep_mtime, &attrs, y, dpp);
9f95a23c 2716 if (ret < 0) {
b3b6e05e 2717 ldpp_dout(dpp, 0) << "ERROR: get_bucket_entrypoint_info() returned " << ret << " bucket=" << bucket << dendl;
9f95a23c
TL
2718 return ret;
2719 }
2720
2721 if (!entry_point.has_bucket_info) {
2722 /* already converted! */
7c673cae
FG
2723 return 0;
2724 }
2725
9f95a23c
TL
2726 info = entry_point.old_bucket_info;
2727
2728 ot.generate_new_write_ver(cct);
2729
b3b6e05e 2730 ret = do_store_linked_bucket_info(ctx, info, nullptr, false, ep_mtime, &ot.write_version, &attrs, true, y, dpp);
9f95a23c 2731 if (ret < 0) {
b3b6e05e 2732 ldpp_dout(dpp, 0) << "ERROR: failed to put_linked_bucket_info(): " << ret << dendl;
9f95a23c 2733 return ret;
7c673cae
FG
2734 }
2735
9f95a23c
TL
2736 return 0;
2737}
2738
2739int RGWBucketCtl::set_bucket_instance_attrs(RGWBucketInfo& bucket_info,
2740 map<string, bufferlist>& attrs,
2741 RGWObjVersionTracker *objv_tracker,
b3b6e05e
TL
2742 optional_yield y,
2743 const DoutPrefixProvider *dpp)
9f95a23c
TL
2744{
2745 return call([&](RGWSI_Bucket_X_Ctx& ctx) {
2746 rgw_bucket& bucket = bucket_info.bucket;
2747
2748 if (!bucket_info.has_instance_obj) {
2749 /* an old bucket object, need to convert it */
b3b6e05e 2750 int ret = convert_old_bucket_info(ctx, bucket, y, dpp);
9f95a23c 2751 if (ret < 0) {
b3b6e05e 2752 ldpp_dout(dpp, 0) << "ERROR: failed converting old bucket info: " << ret << dendl;
9f95a23c
TL
2753 return ret;
2754 }
2755 }
2756
2757 return do_store_bucket_instance_info(ctx.bi,
2758 bucket,
2759 bucket_info,
2760 y,
b3b6e05e 2761 dpp,
9f95a23c
TL
2762 BucketInstance::PutParams().set_attrs(&attrs)
2763 .set_objv_tracker(objv_tracker)
2764 .set_orig_info(&bucket_info));
2765 });
2766}
2767
2768
2769int RGWBucketCtl::link_bucket(const rgw_user& user_id,
2770 const rgw_bucket& bucket,
2771 ceph::real_time creation_time,
2772 optional_yield y,
b3b6e05e 2773 const DoutPrefixProvider *dpp,
9f95a23c
TL
2774 bool update_entrypoint,
2775 rgw_ep_info *pinfo)
2776{
2777 return bm_handler->call([&](RGWSI_Bucket_EP_Ctx& ctx) {
f67539c2 2778 return do_link_bucket(ctx, user_id, bucket, creation_time,
b3b6e05e 2779 update_entrypoint, pinfo, y, dpp);
9f95a23c
TL
2780 });
2781}
2782
2783int RGWBucketCtl::do_link_bucket(RGWSI_Bucket_EP_Ctx& ctx,
2784 const rgw_user& user_id,
2785 const rgw_bucket& bucket,
2786 ceph::real_time creation_time,
9f95a23c 2787 bool update_entrypoint,
f67539c2 2788 rgw_ep_info *pinfo,
b3b6e05e
TL
2789 optional_yield y,
2790 const DoutPrefixProvider *dpp)
9f95a23c
TL
2791{
2792 int ret;
2793
2794 RGWBucketEntryPoint ep;
2795 RGWObjVersionTracker ot;
2796 RGWObjVersionTracker& rot = (pinfo) ? pinfo->ep_objv : ot;
2797 map<string, bufferlist> attrs, *pattrs = nullptr;
2798 string meta_key;
2799
2800 if (update_entrypoint) {
2801 meta_key = RGWSI_Bucket::get_entrypoint_meta_key(bucket);
2802 if (pinfo) {
2803 ep = pinfo->ep;
2804 pattrs = &pinfo->attrs;
2805 } else {
2806 ret = svc.bucket->read_bucket_entrypoint_info(ctx,
2807 meta_key,
2808 &ep, &rot,
2809 nullptr, &attrs,
b3b6e05e 2810 y, dpp);
9f95a23c 2811 if (ret < 0 && ret != -ENOENT) {
b3b6e05e 2812 ldpp_dout(dpp, 0) << "ERROR: store->get_bucket_entrypoint_info() returned: "
9f95a23c
TL
2813 << cpp_strerror(-ret) << dendl;
2814 }
2815 pattrs = &attrs;
2816 }
181888fb
FG
2817 }
2818
b3b6e05e 2819 ret = ctl.user->add_bucket(dpp, user_id, bucket, creation_time, y);
9f95a23c 2820 if (ret < 0) {
b3b6e05e 2821 ldpp_dout(dpp, 0) << "ERROR: error adding bucket to user directory:"
9f95a23c
TL
2822 << " user=" << user_id
2823 << " bucket=" << bucket
2824 << " err=" << cpp_strerror(-ret)
2825 << dendl;
2826 goto done_err;
7c673cae 2827 }
7c673cae 2828
9f95a23c
TL
2829 if (!update_entrypoint)
2830 return 0;
2831
2832 ep.linked = true;
2833 ep.owner = user_id;
2834 ep.bucket = bucket;
2835 ret = svc.bucket->store_bucket_entrypoint_info(
b3b6e05e 2836 ctx, meta_key, ep, false, real_time(), pattrs, &rot, y, dpp);
9f95a23c
TL
2837 if (ret < 0)
2838 goto done_err;
2839
2840 return 0;
2841
2842done_err:
b3b6e05e 2843 int r = do_unlink_bucket(ctx, user_id, bucket, true, y, dpp);
9f95a23c 2844 if (r < 0) {
b3b6e05e 2845 ldpp_dout(dpp, 0) << "ERROR: failed unlinking bucket on error cleanup: "
9f95a23c
TL
2846 << cpp_strerror(-r) << dendl;
2847 }
2848 return ret;
2849}
2850
b3b6e05e 2851int RGWBucketCtl::unlink_bucket(const rgw_user& user_id, const rgw_bucket& bucket, optional_yield y, const DoutPrefixProvider *dpp, bool update_entrypoint)
9f95a23c
TL
2852{
2853 return bm_handler->call([&](RGWSI_Bucket_EP_Ctx& ctx) {
b3b6e05e 2854 return do_unlink_bucket(ctx, user_id, bucket, update_entrypoint, y, dpp);
9f95a23c
TL
2855 });
2856}
2857
2858int RGWBucketCtl::do_unlink_bucket(RGWSI_Bucket_EP_Ctx& ctx,
2859 const rgw_user& user_id,
2860 const rgw_bucket& bucket,
f67539c2 2861 bool update_entrypoint,
b3b6e05e
TL
2862 optional_yield y,
2863 const DoutPrefixProvider *dpp)
9f95a23c 2864{
b3b6e05e 2865 int ret = ctl.user->remove_bucket(dpp, user_id, bucket, y);
9f95a23c 2866 if (ret < 0) {
b3b6e05e 2867 ldpp_dout(dpp, 0) << "ERROR: error removing bucket from directory: "
9f95a23c
TL
2868 << cpp_strerror(-ret)<< dendl;
2869 }
2870
2871 if (!update_entrypoint)
2872 return 0;
2873
2874 RGWBucketEntryPoint ep;
2875 RGWObjVersionTracker ot;
2876 map<string, bufferlist> attrs;
2877 string meta_key = RGWSI_Bucket::get_entrypoint_meta_key(bucket);
b3b6e05e 2878 ret = svc.bucket->read_bucket_entrypoint_info(ctx, meta_key, &ep, &ot, nullptr, &attrs, y, dpp);
9f95a23c
TL
2879 if (ret == -ENOENT)
2880 return 0;
2881 if (ret < 0)
2882 return ret;
11fdf7f2 2883
9f95a23c 2884 if (!ep.linked)
11fdf7f2 2885 return 0;
9f95a23c
TL
2886
2887 if (ep.owner != user_id) {
b3b6e05e 2888 ldpp_dout(dpp, 0) << "bucket entry point user mismatch, can't unlink bucket: " << ep.owner << " != " << user_id << dendl;
9f95a23c 2889 return -EINVAL;
11fdf7f2 2890 }
11fdf7f2 2891
9f95a23c 2892 ep.linked = false;
b3b6e05e 2893 return svc.bucket->store_bucket_entrypoint_info(ctx, meta_key, ep, false, real_time(), &attrs, &ot, y, dpp);
9f95a23c
TL
2894}
2895
9f95a23c
TL
2896int RGWBucketCtl::read_bucket_stats(const rgw_bucket& bucket,
2897 RGWBucketEnt *result,
b3b6e05e
TL
2898 optional_yield y,
2899 const DoutPrefixProvider *dpp)
9f95a23c
TL
2900{
2901 return call([&](RGWSI_Bucket_X_Ctx& ctx) {
b3b6e05e 2902 return svc.bucket->read_bucket_stats(ctx, bucket, result, y, dpp);
9f95a23c
TL
2903 });
2904}
2905
2906int RGWBucketCtl::read_buckets_stats(map<string, RGWBucketEnt>& m,
b3b6e05e 2907 optional_yield y, const DoutPrefixProvider *dpp)
9f95a23c
TL
2908{
2909 return call([&](RGWSI_Bucket_X_Ctx& ctx) {
b3b6e05e 2910 return svc.bucket->read_buckets_stats(ctx, m, y, dpp);
9f95a23c
TL
2911 });
2912}
2913
b3b6e05e
TL
2914int RGWBucketCtl::sync_user_stats(const DoutPrefixProvider *dpp,
2915 const rgw_user& user_id,
9f95a23c 2916 const RGWBucketInfo& bucket_info,
f67539c2 2917 optional_yield y,
9f95a23c
TL
2918 RGWBucketEnt* pent)
2919{
2920 RGWBucketEnt ent;
2921 if (!pent) {
2922 pent = &ent;
2923 }
b3b6e05e 2924 int r = svc.bi->read_stats(dpp, bucket_info, pent, null_yield);
9f95a23c 2925 if (r < 0) {
b3b6e05e 2926 ldpp_dout(dpp, 20) << __func__ << "(): failed to read bucket stats (r=" << r << ")" << dendl;
9f95a23c
TL
2927 return r;
2928 }
2929
b3b6e05e 2930 return ctl.user->flush_bucket_stats(dpp, user_id, *pent, y);
11fdf7f2
TL
2931}
2932
9f95a23c
TL
2933int RGWBucketCtl::get_sync_policy_handler(std::optional<rgw_zone_id> zone,
2934 std::optional<rgw_bucket> bucket,
2935 RGWBucketSyncPolicyHandlerRef *phandler,
b3b6e05e
TL
2936 optional_yield y,
2937 const DoutPrefixProvider *dpp)
9f95a23c
TL
2938{
2939 int r = call([&](RGWSI_Bucket_X_Ctx& ctx) {
b3b6e05e 2940 return svc.bucket_sync->get_policy_handler(ctx, zone, bucket, phandler, y, dpp);
9f95a23c
TL
2941 });
2942 if (r < 0) {
b3b6e05e 2943 ldpp_dout(dpp, 20) << __func__ << "(): failed to get policy handler for bucket=" << bucket << " (r=" << r << ")" << dendl;
9f95a23c
TL
2944 return r;
2945 }
2946 return 0;
11fdf7f2
TL
2947}
2948
9f95a23c 2949int RGWBucketCtl::bucket_exports_data(const rgw_bucket& bucket,
b3b6e05e
TL
2950 optional_yield y,
2951 const DoutPrefixProvider *dpp)
9f95a23c
TL
2952{
2953
2954 RGWBucketSyncPolicyHandlerRef handler;
2955
b3b6e05e 2956 int r = get_sync_policy_handler(std::nullopt, bucket, &handler, y, dpp);
9f95a23c
TL
2957 if (r < 0) {
2958 return r;
2959 }
2960
2961 return handler->bucket_exports_data();
11fdf7f2
TL
2962}
2963
9f95a23c 2964int RGWBucketCtl::bucket_imports_data(const rgw_bucket& bucket,
b3b6e05e 2965 optional_yield y, const DoutPrefixProvider *dpp)
7c673cae 2966{
9f95a23c
TL
2967
2968 RGWBucketSyncPolicyHandlerRef handler;
2969
b3b6e05e 2970 int r = get_sync_policy_handler(std::nullopt, bucket, &handler, y, dpp);
9f95a23c
TL
2971 if (r < 0) {
2972 return r;
11fdf7f2 2973 }
9f95a23c
TL
2974
2975 return handler->bucket_imports_data();
7c673cae 2976}
9f95a23c
TL
2977
2978RGWBucketMetadataHandlerBase *RGWBucketMetaHandlerAllocator::alloc()
2979{
2980 return new RGWBucketMetadataHandler();
2981}
2982
2983RGWBucketInstanceMetadataHandlerBase *RGWBucketInstanceMetaHandlerAllocator::alloc()
2984{
2985 return new RGWBucketInstanceMetadataHandler();
2986}
2987
2988RGWBucketMetadataHandlerBase *RGWArchiveBucketMetaHandlerAllocator::alloc()
2989{
2990 return new RGWArchiveBucketMetadataHandler();
2991}
2992
2993RGWBucketInstanceMetadataHandlerBase *RGWArchiveBucketInstanceMetaHandlerAllocator::alloc()
2994{
2995 return new RGWArchiveBucketInstanceMetadataHandler();
2996}
2997
20effc67
TL
2998
2999void RGWBucketEntryPoint::generate_test_instances(list<RGWBucketEntryPoint*>& o)
3000{
3001 RGWBucketEntryPoint *bp = new RGWBucketEntryPoint();
3002 init_bucket(&bp->bucket, "tenant", "bucket", "pool", ".index.pool", "marker", "10");
3003 bp->owner = "owner";
3004 bp->creation_time = ceph::real_clock::from_ceph_timespec({ceph_le32(2), ceph_le32(3)});
3005
3006 o.push_back(bp);
3007 o.push_back(new RGWBucketEntryPoint);
3008}
3009
3010void RGWBucketEntryPoint::dump(Formatter *f) const
3011{
3012 encode_json("bucket", bucket, f);
3013 encode_json("owner", owner, f);
3014 utime_t ut(creation_time);
3015 encode_json("creation_time", ut, f);
3016 encode_json("linked", linked, f);
3017 encode_json("has_bucket_info", has_bucket_info, f);
3018 if (has_bucket_info) {
3019 encode_json("old_bucket_info", old_bucket_info, f);
3020 }
3021}
3022
3023void RGWBucketEntryPoint::decode_json(JSONObj *obj) {
3024 JSONDecoder::decode_json("bucket", bucket, obj);
3025 JSONDecoder::decode_json("owner", owner, obj);
3026 utime_t ut;
3027 JSONDecoder::decode_json("creation_time", ut, obj);
3028 creation_time = ut.to_real_time();
3029 JSONDecoder::decode_json("linked", linked, obj);
3030 JSONDecoder::decode_json("has_bucket_info", has_bucket_info, obj);
3031 if (has_bucket_info) {
3032 JSONDecoder::decode_json("old_bucket_info", old_bucket_info, obj);
3033 }
3034}
3035
39ae355f
TL
3036int rgw_chown_bucket_and_objects(rgw::sal::Store* store, rgw::sal::Bucket* bucket,
3037 rgw::sal::User* new_user,
3038 const std::string& marker, std::string *err_msg,
3039 const DoutPrefixProvider *dpp, optional_yield y)
3040{
3041 /* Chown on the bucket */
3042 int ret = bucket->chown(dpp, *new_user, y);
3043 if (ret < 0) {
3044 set_err_msg(err_msg, "Failed to change object ownership: " + cpp_strerror(-ret));
3045 }
3046
3047 /* Now chown on all the objects in the bucket */
3048 map<string, bool> common_prefixes;
3049
3050 rgw::sal::Bucket::ListParams params;
3051 rgw::sal::Bucket::ListResults results;
3052
3053 params.list_versions = true;
3054 params.allow_unordered = true;
3055 params.marker = marker;
3056
3057 int count = 0;
3058 int max_entries = 1000;
3059
3060 //Loop through objects and update object acls to point to bucket owner
3061
3062 do {
3063 results.objs.clear();
3064 ret = bucket->list(dpp, params, max_entries, results, y);
3065 if (ret < 0) {
3066 ldpp_dout(dpp, 0) << "ERROR: list objects failed: " << cpp_strerror(-ret) << dendl;
3067 return ret;
3068 }
3069
3070 params.marker = results.next_marker;
3071 count += results.objs.size();
3072
3073 for (const auto& obj : results.objs) {
3074 std::unique_ptr<rgw::sal::Object> r_obj = bucket->get_object(obj.key);
3075
3076 ret = r_obj->chown(*new_user, dpp, y);
3077 if (ret < 0) {
3078 ldpp_dout(dpp, 0) << "ERROR: chown failed on " << r_obj << " :" << cpp_strerror(-ret) << dendl;
3079 return ret;
3080 }
3081 }
3082 cerr << count << " objects processed in " << bucket
3083 << ". Next marker " << params.marker.name << std::endl;
3084 } while(results.is_truncated);
3085
3086 return ret;
3087}
3088