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