]> git.proxmox.com Git - ceph.git/blame - ceph/src/rgw/rgw_bucket.cc
update sources to v12.1.3
[ceph.git] / ceph / src / rgw / rgw_bucket.cc
CommitLineData
7c673cae
FG
1// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2// vim: ts=8 sw=2 smarttab
3
4#include <errno.h>
5
6#include <string>
7#include <map>
8#include <sstream>
9
10#include <boost/utility/string_ref.hpp>
11#include <boost/format.hpp>
12
13#include "common/errno.h"
14#include "common/ceph_json.h"
15#include "rgw_rados.h"
16#include "rgw_acl.h"
17#include "rgw_acl_s3.h"
18
19#include "include/types.h"
20#include "rgw_bucket.h"
21#include "rgw_user.h"
22#include "rgw_string.h"
224ce89b 23#include "rgw_multi.h"
7c673cae
FG
24
25#include "include/rados/librados.hpp"
26// until everything is moved from rgw_common
27#include "rgw_common.h"
28
29#include "cls/user/cls_user_types.h"
30
31#define dout_context g_ceph_context
32#define dout_subsys ceph_subsys_rgw
33
34#define BUCKET_TAG_TIMEOUT 30
35
36using namespace std;
37
38static RGWMetadataHandler *bucket_meta_handler = NULL;
39static RGWMetadataHandler *bucket_instance_meta_handler = NULL;
40
41// define as static when RGWBucket implementation compete
42void rgw_get_buckets_obj(const rgw_user& user_id, string& buckets_obj_id)
43{
44 buckets_obj_id = user_id.to_str();
45 buckets_obj_id += RGW_BUCKETS_OBJ_SUFFIX;
46}
47
48/*
49 * Note that this is not a reversal of parse_bucket(). That one deals
50 * with the syntax we need in metadata and such. This one deals with
51 * the representation in RADOS pools. We chose '/' because it's not
52 * acceptable in bucket names and thus qualified buckets cannot conflict
53 * with the legacy or S3 buckets.
54 */
55std::string rgw_make_bucket_entry_name(const std::string& tenant_name,
56 const std::string& bucket_name) {
57 std::string bucket_entry;
58
59 if (bucket_name.empty()) {
60 bucket_entry.clear();
61 } else if (tenant_name.empty()) {
62 bucket_entry = bucket_name;
63 } else {
64 bucket_entry = tenant_name + "/" + bucket_name;
65 }
66
67 return bucket_entry;
68}
69
70/*
71 * Tenants are separated from buckets in URLs by a colon in S3.
72 * This function is not to be used on Swift URLs, not even for COPY arguments.
73 */
74void rgw_parse_url_bucket(const string &bucket, const string& auth_tenant,
75 string &tenant_name, string &bucket_name) {
76
77 int pos = bucket.find(':');
78 if (pos >= 0) {
79 /*
80 * N.B.: We allow ":bucket" syntax with explicit empty tenant in order
81 * to refer to the legacy tenant, in case users in new named tenants
82 * want to access old global buckets.
83 */
84 tenant_name = bucket.substr(0, pos);
85 bucket_name = bucket.substr(pos + 1);
86 } else {
87 tenant_name = auth_tenant;
88 bucket_name = bucket;
89 }
90}
91
92/**
93 * Get all the buckets owned by a user and fill up an RGWUserBuckets with them.
94 * Returns: 0 on success, -ERR# on failure.
95 */
96int rgw_read_user_buckets(RGWRados * store,
97 const rgw_user& user_id,
98 RGWUserBuckets& buckets,
99 const string& marker,
100 const string& end_marker,
101 uint64_t max,
102 bool need_stats,
103 bool *is_truncated,
104 uint64_t default_amount)
105{
106 int ret;
107 buckets.clear();
108 string buckets_obj_id;
109 rgw_get_buckets_obj(user_id, buckets_obj_id);
110 rgw_raw_obj obj(store->get_zone_params().user_uid_pool, buckets_obj_id);
111 list<cls_user_bucket_entry> entries;
112
113 bool truncated = false;
114 string m = marker;
115
116 uint64_t total = 0;
117
118 if (!max) {
119 max = default_amount;
120 }
121
122 do {
123 ret = store->cls_user_list_buckets(obj, m, end_marker, max - total, entries, &m, &truncated);
124 if (ret == -ENOENT)
125 ret = 0;
126
127 if (ret < 0)
128 return ret;
129
130 for (const auto& entry : entries) {
131 buckets.add(RGWBucketEnt(user_id, entry));
132 total++;
133 }
134
135 } while (truncated && total < max);
136
137 if (is_truncated != nullptr) {
138 *is_truncated = truncated;
139 }
140
141 if (need_stats) {
142 map<string, RGWBucketEnt>& m = buckets.get_buckets();
143 ret = store->update_containers_stats(m);
144 if (ret < 0 && ret != -ENOENT) {
145 ldout(store->ctx(), 0) << "ERROR: could not get stats for buckets" << dendl;
146 return ret;
147 }
148 }
149 return 0;
150}
151
152int rgw_bucket_sync_user_stats(RGWRados *store, const rgw_user& user_id, const RGWBucketInfo& bucket_info)
153{
154 string buckets_obj_id;
155 rgw_get_buckets_obj(user_id, buckets_obj_id);
156 rgw_raw_obj obj(store->get_zone_params().user_uid_pool, buckets_obj_id);
157
158 return store->cls_user_sync_bucket_stats(obj, bucket_info);
159}
160
161int rgw_bucket_sync_user_stats(RGWRados *store, const string& tenant_name, const string& bucket_name)
162{
163 RGWBucketInfo bucket_info;
164 RGWObjectCtx obj_ctx(store);
165 int ret = store->get_bucket_info(obj_ctx, tenant_name, bucket_name, bucket_info, NULL);
166 if (ret < 0) {
167 ldout(store->ctx(), 0) << "ERROR: could not fetch bucket info: ret=" << ret << dendl;
168 return ret;
169 }
170
171 ret = rgw_bucket_sync_user_stats(store, bucket_info.owner, bucket_info);
172 if (ret < 0) {
173 ldout(store->ctx(), 0) << "ERROR: could not sync user stats for bucket " << bucket_name << ": ret=" << ret << dendl;
174 return ret;
175 }
176
177 return 0;
178}
179
180int rgw_link_bucket(RGWRados *store, const rgw_user& user_id, rgw_bucket& bucket, real_time creation_time, bool update_entrypoint)
181{
182 int ret;
183 string& tenant_name = bucket.tenant;
184 string& bucket_name = bucket.name;
185
186 cls_user_bucket_entry new_bucket;
187
188 RGWBucketEntryPoint ep;
189 RGWObjVersionTracker ot;
190
191 bucket.convert(&new_bucket.bucket);
192 new_bucket.size = 0;
193 if (real_clock::is_zero(creation_time))
194 new_bucket.creation_time = real_clock::now();
195 else
196 new_bucket.creation_time = creation_time;
197
198 map<string, bufferlist> attrs;
199 RGWObjectCtx obj_ctx(store);
200
201 if (update_entrypoint) {
202 ret = store->get_bucket_entrypoint_info(obj_ctx, tenant_name, bucket_name, ep, &ot, NULL, &attrs);
203 if (ret < 0 && ret != -ENOENT) {
204 ldout(store->ctx(), 0) << "ERROR: store->get_bucket_entrypoint_info() returned: "
205 << cpp_strerror(-ret) << dendl;
206 }
207 }
208
209 string buckets_obj_id;
210 rgw_get_buckets_obj(user_id, buckets_obj_id);
211
212 rgw_raw_obj obj(store->get_zone_params().user_uid_pool, buckets_obj_id);
213 ret = store->cls_user_add_bucket(obj, new_bucket);
214 if (ret < 0) {
215 ldout(store->ctx(), 0) << "ERROR: error adding bucket to directory: "
216 << cpp_strerror(-ret) << dendl;
217 goto done_err;
218 }
219
220 if (!update_entrypoint)
221 return 0;
222
223 ep.linked = true;
224 ep.owner = user_id;
225 ep.bucket = bucket;
226 ret = store->put_bucket_entrypoint_info(tenant_name, bucket_name, ep, false, ot, real_time(), &attrs);
227 if (ret < 0)
228 goto done_err;
229
230 return 0;
231done_err:
232 int r = rgw_unlink_bucket(store, user_id, bucket.tenant, bucket.name);
233 if (r < 0) {
234 ldout(store->ctx(), 0) << "ERROR: failed unlinking bucket on error cleanup: "
235 << cpp_strerror(-r) << dendl;
236 }
237 return ret;
238}
239
240int rgw_unlink_bucket(RGWRados *store, const rgw_user& user_id, const string& tenant_name, const string& bucket_name, bool update_entrypoint)
241{
242 int ret;
243
244 string buckets_obj_id;
245 rgw_get_buckets_obj(user_id, buckets_obj_id);
246
247 cls_user_bucket bucket;
248 bucket.name = bucket_name;
249 rgw_raw_obj obj(store->get_zone_params().user_uid_pool, buckets_obj_id);
250 ret = store->cls_user_remove_bucket(obj, bucket);
251 if (ret < 0) {
252 ldout(store->ctx(), 0) << "ERROR: error removing bucket from directory: "
253 << cpp_strerror(-ret)<< dendl;
254 }
255
256 if (!update_entrypoint)
257 return 0;
258
259 RGWBucketEntryPoint ep;
260 RGWObjVersionTracker ot;
261 map<string, bufferlist> attrs;
262 RGWObjectCtx obj_ctx(store);
263 ret = store->get_bucket_entrypoint_info(obj_ctx, tenant_name, bucket_name, ep, &ot, NULL, &attrs);
264 if (ret == -ENOENT)
265 return 0;
266 if (ret < 0)
267 return ret;
268
269 if (!ep.linked)
270 return 0;
271
272 if (ep.owner != user_id) {
273 ldout(store->ctx(), 0) << "bucket entry point user mismatch, can't unlink bucket: " << ep.owner << " != " << user_id << dendl;
274 return -EINVAL;
275 }
276
277 ep.linked = false;
278 return store->put_bucket_entrypoint_info(tenant_name, bucket_name, ep, false, ot, real_time(), &attrs);
279}
280
281int rgw_bucket_store_info(RGWRados *store, const string& bucket_name, bufferlist& bl, bool exclusive,
282 map<string, bufferlist> *pattrs, RGWObjVersionTracker *objv_tracker,
283 real_time mtime) {
284 return store->meta_mgr->put_entry(bucket_meta_handler, bucket_name, bl, exclusive, objv_tracker, mtime, pattrs);
285}
286
287int rgw_bucket_instance_store_info(RGWRados *store, string& entry, bufferlist& bl, bool exclusive,
288 map<string, bufferlist> *pattrs, RGWObjVersionTracker *objv_tracker,
289 real_time mtime) {
290 return store->meta_mgr->put_entry(bucket_instance_meta_handler, entry, bl, exclusive, objv_tracker, mtime, pattrs);
291}
292
293int rgw_bucket_instance_remove_entry(RGWRados *store, string& entry, RGWObjVersionTracker *objv_tracker) {
294 return store->meta_mgr->remove_entry(bucket_instance_meta_handler, entry, objv_tracker);
295}
296
297// 'tenant/' is used in bucket instance keys for sync to avoid parsing ambiguity
298// with the existing instance[:shard] format. once we parse the shard, the / is
299// replaced with a : to match the [tenant:]instance format
300void rgw_bucket_instance_key_to_oid(string& key)
301{
302 // replace tenant/ with tenant:
303 auto c = key.find('/');
304 if (c != string::npos) {
305 key[c] = ':';
306 }
307}
308
309// convert bucket instance oids back to the tenant/ format for metadata keys.
310// it's safe to parse 'tenant:' only for oids, because they won't contain the
311// optional :shard at the end
312void rgw_bucket_instance_oid_to_key(string& oid)
313{
314 // find first : (could be tenant:bucket or bucket:instance)
315 auto c = oid.find(':');
316 if (c != string::npos) {
317 // if we find another :, the first one was for tenant
318 if (oid.find(':', c + 1) != string::npos) {
319 oid[c] = '/';
320 }
321 }
322}
323
324int rgw_bucket_parse_bucket_instance(const string& bucket_instance, string *target_bucket_instance, int *shard_id)
325{
326 ssize_t pos = bucket_instance.rfind(':');
327 if (pos < 0) {
328 return -EINVAL;
329 }
330
331 string first = bucket_instance.substr(0, pos);
332 string second = bucket_instance.substr(pos + 1);
333
334 if (first.find(':') == string::npos) {
335 *shard_id = -1;
336 *target_bucket_instance = bucket_instance;
337 return 0;
338 }
339
340 *target_bucket_instance = first;
341 string err;
342 *shard_id = strict_strtol(second.c_str(), 10, &err);
343 if (!err.empty()) {
344 return -EINVAL;
345 }
346
347 return 0;
348}
349
350// parse key in format: [tenant/]name:instance[:shard_id]
351int rgw_bucket_parse_bucket_key(CephContext *cct, const string& key,
352 rgw_bucket *bucket, int *shard_id)
353{
354 boost::string_ref name{key};
355 boost::string_ref instance;
356
357 // split tenant/name
358 auto pos = name.find('/');
359 if (pos != boost::string_ref::npos) {
360 auto tenant = name.substr(0, pos);
361 bucket->tenant.assign(tenant.begin(), tenant.end());
362 name = name.substr(pos + 1);
363 }
364
365 // split name:instance
366 pos = name.find(':');
367 if (pos != boost::string_ref::npos) {
368 instance = name.substr(pos + 1);
369 name = name.substr(0, pos);
370 }
371 bucket->name.assign(name.begin(), name.end());
372
373 // split instance:shard
374 pos = instance.find(':');
375 if (pos == boost::string_ref::npos) {
376 bucket->bucket_id.assign(instance.begin(), instance.end());
377 *shard_id = -1;
378 return 0;
379 }
380
381 // parse shard id
382 auto shard = instance.substr(pos + 1);
383 string err;
384 auto id = strict_strtol(shard.data(), 10, &err);
385 if (!err.empty()) {
386 ldout(cct, 0) << "ERROR: failed to parse bucket shard '"
387 << instance.data() << "': " << err << dendl;
388 return -EINVAL;
389 }
390
391 *shard_id = id;
392 instance = instance.substr(0, pos);
393 bucket->bucket_id.assign(instance.begin(), instance.end());
394 return 0;
395}
396
397int rgw_bucket_set_attrs(RGWRados *store, RGWBucketInfo& bucket_info,
398 map<string, bufferlist>& attrs,
399 RGWObjVersionTracker *objv_tracker)
400{
401 rgw_bucket& bucket = bucket_info.bucket;
402
403 if (!bucket_info.has_instance_obj) {
404 /* an old bucket object, need to convert it */
405 RGWObjectCtx obj_ctx(store);
406 int ret = store->convert_old_bucket_info(obj_ctx, bucket.tenant, bucket.name);
407 if (ret < 0) {
408 ldout(store->ctx(), 0) << "ERROR: failed converting old bucket info: " << ret << dendl;
409 return ret;
410 }
411 }
412
413 /* we want the bucket instance name without the oid prefix cruft */
414 string key = bucket.get_key();
415 bufferlist bl;
416
417 ::encode(bucket_info, bl);
418
419 return rgw_bucket_instance_store_info(store, key, bl, false, &attrs, objv_tracker, real_time());
420}
421
422static void dump_mulipart_index_results(list<rgw_obj_index_key>& objs_to_unlink,
423 Formatter *f)
424{
d2e6a577
FG
425 for (const auto& o : objs_to_unlink) {
426 f->dump_string("object", o.name);
7c673cae 427 }
7c673cae
FG
428}
429
430void check_bad_user_bucket_mapping(RGWRados *store, const rgw_user& user_id,
431 bool fix)
432{
433 RGWUserBuckets user_buckets;
434 bool is_truncated = false;
435 string marker;
436
437 CephContext *cct = store->ctx();
438
439 size_t max_entries = cct->_conf->rgw_list_buckets_max_chunk;
440
441 do {
442 int ret = rgw_read_user_buckets(store, user_id, user_buckets, marker,
443 string(), max_entries, false,
444 &is_truncated);
445 if (ret < 0) {
446 ldout(store->ctx(), 0) << "failed to read user buckets: "
447 << cpp_strerror(-ret) << dendl;
448 return;
449 }
450
451 map<string, RGWBucketEnt>& buckets = user_buckets.get_buckets();
452 for (map<string, RGWBucketEnt>::iterator i = buckets.begin();
453 i != buckets.end();
454 ++i) {
455 marker = i->first;
456
457 RGWBucketEnt& bucket_ent = i->second;
458 rgw_bucket& bucket = bucket_ent.bucket;
459
460 RGWBucketInfo bucket_info;
461 real_time mtime;
462 RGWObjectCtx obj_ctx(store);
463 int r = store->get_bucket_info(obj_ctx, user_id.tenant, bucket.name, bucket_info, &mtime);
464 if (r < 0) {
465 ldout(store->ctx(), 0) << "could not get bucket info for bucket=" << bucket << dendl;
466 continue;
467 }
468
469 rgw_bucket& actual_bucket = bucket_info.bucket;
470
471 if (actual_bucket.name.compare(bucket.name) != 0 ||
472 actual_bucket.tenant.compare(bucket.tenant) != 0 ||
473 actual_bucket.marker.compare(bucket.marker) != 0 ||
474 actual_bucket.bucket_id.compare(bucket.bucket_id) != 0) {
475 cout << "bucket info mismatch: expected " << actual_bucket << " got " << bucket << std::endl;
476 if (fix) {
477 cout << "fixing" << std::endl;
478 r = rgw_link_bucket(store, user_id, actual_bucket, bucket_info.creation_time);
479 if (r < 0) {
480 cerr << "failed to fix bucket: " << cpp_strerror(-r) << std::endl;
481 }
482 }
483 }
484 }
485 } while (is_truncated);
486}
487
488static bool bucket_object_check_filter(const string& oid)
489{
490 rgw_obj_key key;
491 string ns;
492 return rgw_obj_key::oid_to_key_in_ns(oid, &key, ns);
493}
494
495int rgw_remove_object(RGWRados *store, RGWBucketInfo& bucket_info, rgw_bucket& bucket, rgw_obj_key& key)
496{
497 RGWObjectCtx rctx(store);
498
499 if (key.instance.empty()) {
500 key.instance = "null";
501 }
502
503 rgw_obj obj(bucket, key);
504
505 return store->delete_obj(rctx, bucket_info, obj, bucket_info.versioning_status());
506}
507
508int rgw_remove_bucket(RGWRados *store, rgw_bucket& bucket, bool delete_children)
509{
510 int ret;
511 map<RGWObjCategory, RGWStorageStats> stats;
512 std::vector<rgw_bucket_dir_entry> objs;
513 map<string, bool> common_prefixes;
514 RGWBucketInfo info;
515 RGWObjectCtx obj_ctx(store);
516
517 string bucket_ver, master_ver;
518
519 ret = store->get_bucket_info(obj_ctx, bucket.tenant, bucket.name, info, NULL);
520 if (ret < 0)
521 return ret;
522
523 ret = store->get_bucket_stats(info, RGW_NO_SHARD, &bucket_ver, &master_ver, stats, NULL);
524 if (ret < 0)
525 return ret;
526
7c673cae
FG
527 RGWRados::Bucket target(store, info);
528 RGWRados::Bucket::List list_op(&target);
224ce89b
WB
529 CephContext *cct = store->ctx();
530 int max = 1000;
7c673cae
FG
531
532 list_op.params.list_versions = true;
533
224ce89b
WB
534 do {
535 objs.clear();
7c673cae 536
224ce89b
WB
537 ret = list_op.list_objects(max, &objs, &common_prefixes, NULL);
538 if (ret < 0)
539 return ret;
7c673cae 540
224ce89b
WB
541 if (!objs.empty() && !delete_children) {
542 lderr(store->ctx()) << "ERROR: could not remove non-empty bucket " << bucket.name << dendl;
543 return -ENOTEMPTY;
544 }
545
546 for (const auto& obj : objs) {
547 rgw_obj_key key(obj.key);
548 ret = rgw_remove_object(store, info, bucket, key);
7c673cae
FG
549 if (ret < 0)
550 return ret;
224ce89b 551 }
7c673cae 552
224ce89b 553 } while (!objs.empty());
7c673cae 554
224ce89b 555 string prefix, delimiter;
7c673cae 556
224ce89b
WB
557 ret = abort_bucket_multiparts(store, cct, info, prefix, delimiter);
558 if (ret < 0) {
559 return ret;
7c673cae
FG
560 }
561
562 ret = rgw_bucket_sync_user_stats(store, bucket.tenant, info);
563 if ( ret < 0) {
564 dout(1) << "WARNING: failed sync user stats before bucket delete. ret=" << ret << dendl;
565 }
566
567 RGWObjVersionTracker objv_tracker;
568
569 ret = store->delete_bucket(info, objv_tracker);
570 if (ret < 0) {
571 lderr(store->ctx()) << "ERROR: could not remove bucket " << bucket.name << dendl;
572 return ret;
573 }
574
575 ret = rgw_unlink_bucket(store, info.owner, bucket.tenant, bucket.name, false);
576 if (ret < 0) {
577 lderr(store->ctx()) << "ERROR: unable to remove user bucket information" << dendl;
578 }
579
580 return ret;
581}
582
583static int aio_wait(librados::AioCompletion *handle)
584{
585 librados::AioCompletion *c = (librados::AioCompletion *)handle;
586 c->wait_for_safe();
587 int ret = c->get_return_value();
588 c->release();
589 return ret;
590}
591
592static int drain_handles(list<librados::AioCompletion *>& pending)
593{
594 int ret = 0;
595 while (!pending.empty()) {
596 librados::AioCompletion *handle = pending.front();
597 pending.pop_front();
598 int r = aio_wait(handle);
599 if (r < 0) {
600 ret = r;
601 }
602 }
603 return ret;
604}
605
606int rgw_remove_bucket_bypass_gc(RGWRados *store, rgw_bucket& bucket,
607 int concurrent_max, bool keep_index_consistent)
608{
609 int ret;
610 map<RGWObjCategory, RGWStorageStats> stats;
611 std::vector<rgw_bucket_dir_entry> objs;
612 map<string, bool> common_prefixes;
613 RGWBucketInfo info;
614 RGWObjectCtx obj_ctx(store);
224ce89b 615 CephContext *cct = store->ctx();
7c673cae
FG
616
617 string bucket_ver, master_ver;
618
619 ret = store->get_bucket_info(obj_ctx, bucket.tenant, bucket.name, info, NULL);
620 if (ret < 0)
621 return ret;
622
623 ret = store->get_bucket_stats(info, RGW_NO_SHARD, &bucket_ver, &master_ver, stats, NULL);
624 if (ret < 0)
625 return ret;
626
224ce89b
WB
627 string prefix, delimiter;
628
629 ret = abort_bucket_multiparts(store, cct, info, prefix, delimiter);
630 if (ret < 0) {
631 return ret;
632 }
7c673cae
FG
633
634 RGWRados::Bucket target(store, info);
635 RGWRados::Bucket::List list_op(&target);
636
637 list_op.params.list_versions = true;
638
639 std::list<librados::AioCompletion*> handles;
640
641 int max = 1000;
642 int max_aio = concurrent_max;
643 ret = list_op.list_objects(max, &objs, &common_prefixes, NULL);
644 if (ret < 0)
645 return ret;
646
647 while (!objs.empty()) {
648 std::vector<rgw_bucket_dir_entry>::iterator it = objs.begin();
649 for (; it != objs.end(); ++it) {
650 RGWObjState *astate = NULL;
651 rgw_obj obj(bucket, (*it).key);
652
653 ret = store->get_obj_state(&obj_ctx, info, obj, &astate, false);
654 if (ret == -ENOENT) {
655 dout(1) << "WARNING: cannot find obj state for obj " << obj.get_oid() << dendl;
656 continue;
657 }
658 if (ret < 0) {
659 lderr(store->ctx()) << "ERROR: get obj state returned with error " << ret << dendl;
660 return ret;
661 }
662
663 if (astate->has_manifest) {
664 RGWObjManifest& manifest = astate->manifest;
665 RGWObjManifest::obj_iterator miter = manifest.obj_begin();
666 rgw_obj head_obj = manifest.get_obj();
667 rgw_raw_obj raw_head_obj;
668 store->obj_to_raw(info.placement_rule, head_obj, &raw_head_obj);
669
670
671 for (; miter != manifest.obj_end() && max_aio--; ++miter) {
672 if (!max_aio) {
673 ret = drain_handles(handles);
674 if (ret < 0) {
675 lderr(store->ctx()) << "ERROR: could not drain handles as aio completion returned with " << ret << dendl;
676 return ret;
677 }
678 max_aio = concurrent_max;
679 }
680
681 rgw_raw_obj last_obj = miter.get_location().get_raw_obj(store);
682 if (last_obj == raw_head_obj) {
683 // have the head obj deleted at the end
684 continue;
685 }
686
687 ret = store->delete_raw_obj_aio(last_obj, handles);
688 if (ret < 0) {
689 lderr(store->ctx()) << "ERROR: delete obj aio failed with " << ret << dendl;
690 return ret;
691 }
692 } // for all shadow objs
693
694 ret = store->delete_obj_aio(head_obj, info, astate, handles, keep_index_consistent);
695 if (ret < 0) {
696 lderr(store->ctx()) << "ERROR: delete obj aio failed with " << ret << dendl;
697 return ret;
698 }
699 }
700
701 if (!max_aio) {
702 ret = drain_handles(handles);
703 if (ret < 0) {
704 lderr(store->ctx()) << "ERROR: could not drain handles as aio completion returned with " << ret << dendl;
705 return ret;
706 }
707 max_aio = concurrent_max;
708 }
709 } // for all RGW objects
710 objs.clear();
711
712 ret = list_op.list_objects(max, &objs, &common_prefixes, NULL);
713 if (ret < 0)
714 return ret;
715 }
716
717 ret = drain_handles(handles);
718 if (ret < 0) {
719 lderr(store->ctx()) << "ERROR: could not drain handles as aio completion returned with " << ret << dendl;
720 return ret;
721 }
722
723 ret = rgw_bucket_sync_user_stats(store, bucket.tenant, info);
724 if (ret < 0) {
725 dout(1) << "WARNING: failed sync user stats before bucket delete. ret=" << ret << dendl;
726 }
727
728 RGWObjVersionTracker objv_tracker;
729
730 ret = rgw_bucket_delete_bucket_obj(store, bucket.tenant, bucket.name, objv_tracker);
731 if (ret < 0) {
732 lderr(store->ctx()) << "ERROR: could not remove bucket " << bucket.name << "with ret as " << ret << dendl;
733 return ret;
734 }
735
736 if (!store->is_syncing_bucket_meta(bucket)) {
737 RGWObjVersionTracker objv_tracker;
738 string entry = bucket.get_key();
739 ret = rgw_bucket_instance_remove_entry(store, entry, &objv_tracker);
740 if (ret < 0) {
741 lderr(store->ctx()) << "ERROR: could not remove bucket instance entry" << bucket.name << "with ret as " << ret << dendl;
742 return ret;
743 }
744 }
745
746 ret = rgw_unlink_bucket(store, info.owner, bucket.tenant, bucket.name, false);
747 if (ret < 0) {
748 lderr(store->ctx()) << "ERROR: unable to remove user bucket information" << dendl;
749 }
750
751 return ret;
752}
753
754int rgw_bucket_delete_bucket_obj(RGWRados *store,
755 const string& tenant_name,
756 const string& bucket_name,
757 RGWObjVersionTracker& objv_tracker)
758{
759 string key;
760
761 rgw_make_bucket_entry_name(tenant_name, bucket_name, key);
762 return store->meta_mgr->remove_entry(bucket_meta_handler, key, &objv_tracker);
763}
764
765static void set_err_msg(std::string *sink, std::string msg)
766{
767 if (sink && !msg.empty())
768 *sink = msg;
769}
770
771int RGWBucket::init(RGWRados *storage, RGWBucketAdminOpState& op_state)
772{
773 if (!storage)
774 return -EINVAL;
775
776 store = storage;
777
778 rgw_user user_id = op_state.get_user_id();
779 tenant = user_id.tenant;
780 bucket_name = op_state.get_bucket_name();
781 RGWUserBuckets user_buckets;
782 RGWObjectCtx obj_ctx(store);
783
784 if (bucket_name.empty() && user_id.empty())
785 return -EINVAL;
786
787 if (!bucket_name.empty()) {
788 int r = store->get_bucket_info(obj_ctx, tenant, bucket_name, bucket_info, NULL);
789 if (r < 0) {
790 ldout(store->ctx(), 0) << "could not get bucket info for bucket=" << bucket_name << dendl;
791 return r;
792 }
793
794 op_state.set_bucket(bucket_info.bucket);
795 }
796
797 if (!user_id.empty()) {
798 int r = rgw_get_user_info_by_uid(store, user_id, user_info);
799 if (r < 0)
800 return r;
801
802 op_state.display_name = user_info.display_name;
803 }
804
805 clear_failure();
806 return 0;
807}
808
809int RGWBucket::link(RGWBucketAdminOpState& op_state, std::string *err_msg)
810{
811 if (!op_state.is_user_op()) {
812 set_err_msg(err_msg, "empty user id");
813 return -EINVAL;
814 }
815
816 string bucket_id = op_state.get_bucket_id();
817 if (bucket_id.empty()) {
818 set_err_msg(err_msg, "empty bucket instance id");
819 return -EINVAL;
820 }
821
822 std::string display_name = op_state.get_user_display_name();
823 rgw_bucket bucket = op_state.get_bucket();
824
825 const rgw_pool& root_pool = store->get_zone_params().domain_root;
826 rgw_raw_obj obj(root_pool, bucket.name);
827 RGWObjVersionTracker objv_tracker;
828
829 map<string, bufferlist> attrs;
830 RGWBucketInfo bucket_info;
831
832 string key = bucket.name + ":" + bucket_id;
833 RGWObjectCtx obj_ctx(store);
834 int r = store->get_bucket_instance_info(obj_ctx, key, bucket_info, NULL, &attrs);
835 if (r < 0) {
836 return r;
837 }
838
839 rgw_user user_id = op_state.get_user_id();
840
841 map<string, bufferlist>::iterator aiter = attrs.find(RGW_ATTR_ACL);
842 if (aiter != attrs.end()) {
843 bufferlist aclbl = aiter->second;
844 RGWAccessControlPolicy policy;
845 ACLOwner owner;
846 try {
847 bufferlist::iterator iter = aclbl.begin();
848 ::decode(policy, iter);
849 owner = policy.get_owner();
850 } catch (buffer::error& err) {
851 set_err_msg(err_msg, "couldn't decode policy");
852 return -EIO;
853 }
854
855 r = rgw_unlink_bucket(store, owner.get_id(), bucket.tenant, bucket.name, false);
856 if (r < 0) {
857 set_err_msg(err_msg, "could not unlink policy from user " + owner.get_id().to_str());
858 return r;
859 }
860
861 // now update the user for the bucket...
862 if (display_name.empty()) {
863 ldout(store->ctx(), 0) << "WARNING: user " << user_info.user_id << " has no display name set" << dendl;
864 }
865 policy.create_default(user_info.user_id, display_name);
866
867 owner = policy.get_owner();
31f18b77 868 r = store->set_bucket_owner(bucket_info.bucket, owner);
7c673cae
FG
869 if (r < 0) {
870 set_err_msg(err_msg, "failed to set bucket owner: " + cpp_strerror(-r));
871 return r;
872 }
873
874 // ...and encode the acl
875 aclbl.clear();
876 policy.encode(aclbl);
877
878 r = store->system_obj_set_attr(NULL, obj, RGW_ATTR_ACL, aclbl, &objv_tracker);
224ce89b 879 if (r < 0) {
7c673cae 880 return r;
224ce89b 881 }
7c673cae
FG
882
883 RGWAccessControlPolicy policy_instance;
884 policy_instance.create_default(user_info.user_id, display_name);
885 aclbl.clear();
886 policy_instance.encode(aclbl);
887
888 string oid_bucket_instance = RGW_BUCKET_INSTANCE_MD_PREFIX + key;
889 rgw_raw_obj obj_bucket_instance(root_pool, oid_bucket_instance);
890 r = store->system_obj_set_attr(NULL, obj_bucket_instance, RGW_ATTR_ACL, aclbl, &objv_tracker);
224ce89b
WB
891 if (r < 0) {
892 return r;
893 }
7c673cae
FG
894
895 r = rgw_link_bucket(store, user_info.user_id, bucket_info.bucket, real_time());
224ce89b 896 if (r < 0) {
7c673cae 897 return r;
224ce89b 898 }
7c673cae
FG
899 }
900
901 return 0;
902}
903
904int RGWBucket::unlink(RGWBucketAdminOpState& op_state, std::string *err_msg)
905{
906 rgw_bucket bucket = op_state.get_bucket();
907
908 if (!op_state.is_user_op()) {
909 set_err_msg(err_msg, "could not fetch user or user bucket info");
910 return -EINVAL;
911 }
912
913 int r = rgw_unlink_bucket(store, user_info.user_id, bucket.tenant, bucket.name);
914 if (r < 0) {
915 set_err_msg(err_msg, "error unlinking bucket" + cpp_strerror(-r));
916 }
917
918 return r;
919}
920
921int RGWBucket::remove(RGWBucketAdminOpState& op_state, bool bypass_gc,
922 bool keep_index_consistent, std::string *err_msg)
923{
924 bool delete_children = op_state.will_delete_children();
925 rgw_bucket bucket = op_state.get_bucket();
926 int ret;
927
928 if (bypass_gc) {
929 if (delete_children) {
930 ret = rgw_remove_bucket_bypass_gc(store, bucket, op_state.get_max_aio(), keep_index_consistent);
931 } else {
932 set_err_msg(err_msg, "purge objects should be set for gc to be bypassed");
933 return -EINVAL;
934 }
935 } else {
936 ret = rgw_remove_bucket(store, bucket, delete_children);
937 }
938
939 if (ret < 0) {
940 set_err_msg(err_msg, "unable to remove bucket" + cpp_strerror(-ret));
941 return ret;
942 }
943
944 return 0;
945}
946
947int RGWBucket::remove_object(RGWBucketAdminOpState& op_state, std::string *err_msg)
948{
949 rgw_bucket bucket = op_state.get_bucket();
950 std::string object_name = op_state.get_object_name();
951
952 rgw_obj_key key(object_name);
953
954 int ret = rgw_remove_object(store, bucket_info, bucket, key);
955 if (ret < 0) {
956 set_err_msg(err_msg, "unable to remove object" + cpp_strerror(-ret));
957 return ret;
958 }
959
960 return 0;
961}
962
963static void dump_bucket_index(map<string, rgw_bucket_dir_entry> result, Formatter *f)
964{
965 map<string, rgw_bucket_dir_entry>::iterator iter;
966 for (iter = result.begin(); iter != result.end(); ++iter) {
967 f->dump_string("object", iter->first);
968 }
969}
970
971static void dump_bucket_usage(map<RGWObjCategory, RGWStorageStats>& stats, Formatter *formatter)
972{
973 map<RGWObjCategory, RGWStorageStats>::iterator iter;
974
975 formatter->open_object_section("usage");
976 for (iter = stats.begin(); iter != stats.end(); ++iter) {
977 RGWStorageStats& s = iter->second;
978 const char *cat_name = rgw_obj_category_name(iter->first);
979 formatter->open_object_section(cat_name);
980 s.dump(formatter);
981 formatter->close_section();
982 }
983 formatter->close_section();
984}
985
986static void dump_index_check(map<RGWObjCategory, RGWStorageStats> existing_stats,
987 map<RGWObjCategory, RGWStorageStats> calculated_stats,
988 Formatter *formatter)
989{
990 formatter->open_object_section("check_result");
991 formatter->open_object_section("existing_header");
992 dump_bucket_usage(existing_stats, formatter);
993 formatter->close_section();
994 formatter->open_object_section("calculated_header");
995 dump_bucket_usage(calculated_stats, formatter);
996 formatter->close_section();
997 formatter->close_section();
998}
999
1000int RGWBucket::check_bad_index_multipart(RGWBucketAdminOpState& op_state,
d2e6a577 1001 RGWFormatterFlusher& flusher ,std::string *err_msg)
7c673cae
FG
1002{
1003 bool fix_index = op_state.will_fix_index();
1004 rgw_bucket bucket = op_state.get_bucket();
1005
d2e6a577 1006 size_t max = 1000;
7c673cae
FG
1007
1008 map<string, bool> common_prefixes;
1009
1010 bool is_truncated;
1011 map<string, bool> meta_objs;
1012 map<rgw_obj_index_key, string> all_objs;
1013
1014 RGWBucketInfo bucket_info;
1015 RGWObjectCtx obj_ctx(store);
1016 int r = store->get_bucket_instance_info(obj_ctx, bucket, bucket_info, nullptr, nullptr);
1017 if (r < 0) {
1018 ldout(store->ctx(), 0) << "ERROR: " << __func__ << "(): get_bucket_instance_info(bucket=" << bucket << ") returned r=" << r << dendl;
1019 return r;
1020 }
1021
1022 RGWRados::Bucket target(store, bucket_info);
1023 RGWRados::Bucket::List list_op(&target);
1024
1025 list_op.params.list_versions = true;
224ce89b 1026 list_op.params.ns = RGW_OBJ_NS_MULTIPART;
7c673cae
FG
1027
1028 do {
1029 vector<rgw_bucket_dir_entry> result;
1030 int r = list_op.list_objects(max, &result, &common_prefixes, &is_truncated);
1031 if (r < 0) {
1032 set_err_msg(err_msg, "failed to list objects in bucket=" + bucket.name +
1033 " err=" + cpp_strerror(-r));
1034
1035 return r;
1036 }
1037
1038 vector<rgw_bucket_dir_entry>::iterator iter;
1039 for (iter = result.begin(); iter != result.end(); ++iter) {
31f18b77
FG
1040 rgw_obj_index_key key = iter->key;
1041 rgw_obj obj(bucket, key);
1042 string oid = obj.get_oid();
7c673cae
FG
1043
1044 int pos = oid.find_last_of('.');
1045 if (pos < 0) {
1046 /* obj has no suffix */
1047 all_objs[key] = oid;
1048 } else {
1049 /* obj has suffix */
1050 string name = oid.substr(0, pos);
1051 string suffix = oid.substr(pos + 1);
1052
1053 if (suffix.compare("meta") == 0) {
1054 meta_objs[name] = true;
1055 } else {
1056 all_objs[key] = name;
1057 }
1058 }
1059 }
1060
1061 } while (is_truncated);
1062
d2e6a577
FG
1063 list<rgw_obj_index_key> objs_to_unlink;
1064 Formatter *f = flusher.get_formatter();
1065
1066 f->open_array_section("invalid_multipart_entries");
1067
7c673cae
FG
1068 for (auto aiter = all_objs.begin(); aiter != all_objs.end(); ++aiter) {
1069 string& name = aiter->second;
1070
7c673cae
FG
1071 if (meta_objs.find(name) == meta_objs.end()) {
1072 objs_to_unlink.push_back(aiter->first);
1073 }
7c673cae 1074
d2e6a577
FG
1075 if (objs_to_unlink.size() > max) {
1076 if (fix_index) {
1077 int r = store->remove_objs_from_index(bucket_info, objs_to_unlink);
1078 if (r < 0) {
1079 set_err_msg(err_msg, "ERROR: remove_obj_from_index() returned error: " +
1080 cpp_strerror(-r));
1081 return r;
1082 }
1083 }
1084
1085 dump_mulipart_index_results(objs_to_unlink, flusher.get_formatter());
1086 flusher.flush();
1087 objs_to_unlink.clear();
1088 }
1089 }
7c673cae
FG
1090
1091 if (fix_index) {
1092 int r = store->remove_objs_from_index(bucket_info, objs_to_unlink);
1093 if (r < 0) {
1094 set_err_msg(err_msg, "ERROR: remove_obj_from_index() returned error: " +
1095 cpp_strerror(-r));
1096
1097 return r;
1098 }
1099 }
1100
d2e6a577
FG
1101 dump_mulipart_index_results(objs_to_unlink, f);
1102 f->close_section();
1103 flusher.flush();
1104
7c673cae
FG
1105 return 0;
1106}
1107
1108int RGWBucket::check_object_index(RGWBucketAdminOpState& op_state,
1109 RGWFormatterFlusher& flusher,
1110 std::string *err_msg)
1111{
1112
1113 bool fix_index = op_state.will_fix_index();
1114
1115 rgw_bucket bucket = op_state.get_bucket();
1116
1117 if (!fix_index) {
1118 set_err_msg(err_msg, "check-objects flag requires fix index enabled");
1119 return -EINVAL;
1120 }
1121
1122 store->cls_obj_set_bucket_tag_timeout(bucket_info, BUCKET_TAG_TIMEOUT);
1123
1124 string prefix;
1125 rgw_obj_index_key marker;
1126 bool is_truncated = true;
1127
1128 Formatter *formatter = flusher.get_formatter();
1129 formatter->open_object_section("objects");
1130 while (is_truncated) {
1131 map<string, rgw_bucket_dir_entry> result;
1132
1133 int r = store->cls_bucket_list(bucket_info, RGW_NO_SHARD, marker, prefix, 1000, true,
1134 result, &is_truncated, &marker,
1135 bucket_object_check_filter);
1136 if (r == -ENOENT) {
1137 break;
1138 } else if (r < 0 && r != -ENOENT) {
1139 set_err_msg(err_msg, "ERROR: failed operation r=" + cpp_strerror(-r));
1140 }
1141
1142
1143 dump_bucket_index(result, formatter);
1144 flusher.flush();
1145
1146 }
1147
1148 formatter->close_section();
1149
1150 store->cls_obj_set_bucket_tag_timeout(bucket_info, 0);
1151
1152 return 0;
1153}
1154
1155
1156int RGWBucket::check_index(RGWBucketAdminOpState& op_state,
1157 map<RGWObjCategory, RGWStorageStats>& existing_stats,
1158 map<RGWObjCategory, RGWStorageStats>& calculated_stats,
1159 std::string *err_msg)
1160{
1161 rgw_bucket bucket = op_state.get_bucket();
1162 bool fix_index = op_state.will_fix_index();
1163
1164 int r = store->bucket_check_index(bucket_info, &existing_stats, &calculated_stats);
1165 if (r < 0) {
1166 set_err_msg(err_msg, "failed to check index error=" + cpp_strerror(-r));
1167 return r;
1168 }
1169
1170 if (fix_index) {
1171 r = store->bucket_rebuild_index(bucket_info);
1172 if (r < 0) {
1173 set_err_msg(err_msg, "failed to rebuild index err=" + cpp_strerror(-r));
1174 return r;
1175 }
1176 }
1177
1178 return 0;
1179}
1180
1181
1182int RGWBucket::policy_bl_to_stream(bufferlist& bl, ostream& o)
1183{
1184 RGWAccessControlPolicy_S3 policy(g_ceph_context);
1185 bufferlist::iterator iter = bl.begin();
1186 try {
1187 policy.decode(iter);
1188 } catch (buffer::error& err) {
1189 dout(0) << "ERROR: caught buffer::error, could not decode policy" << dendl;
1190 return -EIO;
1191 }
1192 policy.to_xml(o);
1193 return 0;
1194}
1195
1196static int policy_decode(RGWRados *store, bufferlist& bl, RGWAccessControlPolicy& policy)
1197{
1198 bufferlist::iterator iter = bl.begin();
1199 try {
1200 policy.decode(iter);
1201 } catch (buffer::error& err) {
1202 ldout(store->ctx(), 0) << "ERROR: caught buffer::error, could not decode policy" << dendl;
1203 return -EIO;
1204 }
1205 return 0;
1206}
1207
1208int RGWBucket::get_policy(RGWBucketAdminOpState& op_state, RGWAccessControlPolicy& policy)
1209{
1210 std::string object_name = op_state.get_object_name();
1211 rgw_bucket bucket = op_state.get_bucket();
1212 RGWObjectCtx obj_ctx(store);
1213
1214 RGWBucketInfo bucket_info;
1215 map<string, bufferlist> attrs;
1216 int ret = store->get_bucket_info(obj_ctx, bucket.tenant, bucket.name, bucket_info, NULL, &attrs);
1217 if (ret < 0) {
1218 return ret;
1219 }
1220
1221 if (!object_name.empty()) {
1222 bufferlist bl;
1223 rgw_obj obj(bucket, object_name);
1224
1225 RGWRados::Object op_target(store, bucket_info, obj_ctx, obj);
1226 RGWRados::Object::Read rop(&op_target);
1227
1228 int ret = rop.get_attr(RGW_ATTR_ACL, bl);
1229 if (ret < 0)
1230 return ret;
1231
1232 return policy_decode(store, bl, policy);
1233 }
1234
1235 map<string, bufferlist>::iterator aiter = attrs.find(RGW_ATTR_ACL);
1236 if (aiter == attrs.end()) {
1237 return -ENOENT;
1238 }
1239
1240 return policy_decode(store, aiter->second, policy);
1241}
1242
1243
1244int RGWBucketAdminOp::get_policy(RGWRados *store, RGWBucketAdminOpState& op_state,
1245 RGWAccessControlPolicy& policy)
1246{
1247 RGWBucket bucket;
1248
1249 int ret = bucket.init(store, op_state);
1250 if (ret < 0)
1251 return ret;
1252
1253 ret = bucket.get_policy(op_state, policy);
1254 if (ret < 0)
1255 return ret;
1256
1257 return 0;
1258}
1259
1260/* Wrappers to facilitate RESTful interface */
1261
1262
1263int RGWBucketAdminOp::get_policy(RGWRados *store, RGWBucketAdminOpState& op_state,
1264 RGWFormatterFlusher& flusher)
1265{
1266 RGWAccessControlPolicy policy(store->ctx());
1267
1268 int ret = get_policy(store, op_state, policy);
1269 if (ret < 0)
1270 return ret;
1271
1272 Formatter *formatter = flusher.get_formatter();
1273
1274 flusher.start(0);
1275
1276 formatter->open_object_section("policy");
1277 policy.dump(formatter);
1278 formatter->close_section();
1279
1280 flusher.flush();
1281
1282 return 0;
1283}
1284
1285int RGWBucketAdminOp::dump_s3_policy(RGWRados *store, RGWBucketAdminOpState& op_state,
1286 ostream& os)
1287{
1288 RGWAccessControlPolicy_S3 policy(store->ctx());
1289
1290 int ret = get_policy(store, op_state, policy);
1291 if (ret < 0)
1292 return ret;
1293
1294 policy.to_xml(os);
1295
1296 return 0;
1297}
1298
1299int RGWBucketAdminOp::unlink(RGWRados *store, RGWBucketAdminOpState& op_state)
1300{
1301 RGWBucket bucket;
1302
1303 int ret = bucket.init(store, op_state);
1304 if (ret < 0)
1305 return ret;
1306
1307 return bucket.unlink(op_state);
1308}
1309
1310int RGWBucketAdminOp::link(RGWRados *store, RGWBucketAdminOpState& op_state, string *err)
1311{
1312 RGWBucket bucket;
1313
1314 int ret = bucket.init(store, op_state);
1315 if (ret < 0)
1316 return ret;
1317
1318 return bucket.link(op_state, err);
1319
1320}
1321
1322int RGWBucketAdminOp::check_index(RGWRados *store, RGWBucketAdminOpState& op_state,
1323 RGWFormatterFlusher& flusher)
1324{
1325 int ret;
7c673cae
FG
1326 map<RGWObjCategory, RGWStorageStats> existing_stats;
1327 map<RGWObjCategory, RGWStorageStats> calculated_stats;
d2e6a577 1328
7c673cae
FG
1329
1330 RGWBucket bucket;
1331
1332 ret = bucket.init(store, op_state);
1333 if (ret < 0)
1334 return ret;
1335
1336 Formatter *formatter = flusher.get_formatter();
1337 flusher.start(0);
1338
d2e6a577 1339 ret = bucket.check_bad_index_multipart(op_state, flusher);
7c673cae
FG
1340 if (ret < 0)
1341 return ret;
1342
7c673cae
FG
1343 ret = bucket.check_object_index(op_state, flusher);
1344 if (ret < 0)
1345 return ret;
1346
1347 ret = bucket.check_index(op_state, existing_stats, calculated_stats);
1348 if (ret < 0)
1349 return ret;
1350
1351 dump_index_check(existing_stats, calculated_stats, formatter);
1352 flusher.flush();
1353
1354 return 0;
1355}
1356
1357int RGWBucketAdminOp::remove_bucket(RGWRados *store, RGWBucketAdminOpState& op_state,
1358 bool bypass_gc, bool keep_index_consistent)
1359{
1360 RGWBucket bucket;
1361
1362 int ret = bucket.init(store, op_state);
1363 if (ret < 0)
1364 return ret;
1365
c07f9fc5
FG
1366 std::string err_msg;
1367 ret = bucket.remove(op_state, bypass_gc, keep_index_consistent, &err_msg);
1368 if (!err_msg.empty()) {
1369 lderr(store->ctx()) << "ERROR: " << err_msg << dendl;
1370 }
1371 return ret;
7c673cae
FG
1372}
1373
1374int RGWBucketAdminOp::remove_object(RGWRados *store, RGWBucketAdminOpState& op_state)
1375{
1376 RGWBucket bucket;
1377
1378 int ret = bucket.init(store, op_state);
1379 if (ret < 0)
1380 return ret;
1381
1382 return bucket.remove_object(op_state);
1383}
1384
1385static int bucket_stats(RGWRados *store, const std::string& tenant_name, std::string& bucket_name, Formatter *formatter)
1386{
1387 RGWBucketInfo bucket_info;
1388 map<RGWObjCategory, RGWStorageStats> stats;
1389
1390 real_time mtime;
1391 RGWObjectCtx obj_ctx(store);
1392 int r = store->get_bucket_info(obj_ctx, tenant_name, bucket_name, bucket_info, &mtime);
1393 if (r < 0)
1394 return r;
1395
1396 rgw_bucket& bucket = bucket_info.bucket;
1397
1398 string bucket_ver, master_ver;
1399 string max_marker;
1400 int ret = store->get_bucket_stats(bucket_info, RGW_NO_SHARD, &bucket_ver, &master_ver, stats, &max_marker);
1401 if (ret < 0) {
1402 cerr << "error getting bucket stats ret=" << ret << std::endl;
1403 return ret;
1404 }
1405
1406 utime_t ut(mtime);
1407
1408 formatter->open_object_section("stats");
1409 formatter->dump_string("bucket", bucket.name);
31f18b77
FG
1410 formatter->dump_string("zonegroup", bucket_info.zonegroup);
1411 formatter->dump_string("placement_rule", bucket_info.placement_rule);
1412 ::encode_json("explicit_placement", bucket.explicit_placement, formatter);
7c673cae
FG
1413 formatter->dump_string("id", bucket.bucket_id);
1414 formatter->dump_string("marker", bucket.marker);
1415 formatter->dump_stream("index_type") << bucket_info.index_type;
1416 ::encode_json("owner", bucket_info.owner, formatter);
1417 formatter->dump_string("ver", bucket_ver);
1418 formatter->dump_string("master_ver", master_ver);
1419 formatter->dump_stream("mtime") << ut;
1420 formatter->dump_string("max_marker", max_marker);
1421 dump_bucket_usage(stats, formatter);
1422 encode_json("bucket_quota", bucket_info.quota, formatter);
1423 formatter->close_section();
1424
1425 return 0;
1426}
1427
1428int RGWBucketAdminOp::limit_check(RGWRados *store,
1429 RGWBucketAdminOpState& op_state,
1430 const std::list<std::string>& user_ids,
1431 RGWFormatterFlusher& flusher,
1432 bool warnings_only)
1433{
1434 int ret = 0;
1435 const size_t max_entries =
1436 store->ctx()->_conf->rgw_list_buckets_max_chunk;
1437
1438 const size_t safe_max_objs_per_shard =
1439 store->ctx()->_conf->rgw_safe_max_objects_per_shard;
1440
1441 uint16_t shard_warn_pct =
1442 store->ctx()->_conf->rgw_shard_warning_threshold;
1443 if (shard_warn_pct > 100)
1444 shard_warn_pct = 90;
1445
1446 Formatter *formatter = flusher.get_formatter();
1447 flusher.start(0);
1448
1449 formatter->open_array_section("users");
1450
1451 for (const auto& user_id : user_ids) {
1452 formatter->open_object_section("user");
1453 formatter->dump_string("user_id", user_id);
1454 bool done;
1455 formatter->open_array_section("buckets");
1456 do {
1457 RGWUserBuckets buckets;
1458 string marker;
1459 bool is_truncated;
1460
1461 ret = rgw_read_user_buckets(store, user_id, buckets,
1462 marker, string(), max_entries, false,
1463 &is_truncated);
1464 if (ret < 0)
1465 return ret;
1466
1467 map<string, RGWBucketEnt>& m_buckets = buckets.get_buckets();
1468
1469 for (const auto& iter : m_buckets) {
1470 auto& bucket = iter.second.bucket;
1471 uint32_t num_shards = 1;
1472 uint64_t num_objects = 0;
1473
1474 /* need info for num_shards */
1475 RGWBucketInfo info;
1476 RGWObjectCtx obj_ctx(store);
1477
1478 marker = bucket.name; /* Casey's location for marker update,
1479 * as we may now not reach the end of
1480 * the loop body */
1481
1482 ret = store->get_bucket_info(obj_ctx, bucket.tenant, bucket.name,
1483 info, nullptr);
1484 if (ret < 0)
1485 continue;
1486
1487 /* need stats for num_entries */
1488 string bucket_ver, master_ver;
1489 std::map<RGWObjCategory, RGWStorageStats> stats;
1490 ret = store->get_bucket_stats(info, RGW_NO_SHARD, &bucket_ver,
1491 &master_ver, stats, nullptr);
1492
1493 if (ret < 0)
1494 continue;
1495
1496 for (const auto& s : stats) {
1497 num_objects += s.second.num_objects;
1498 }
1499
1500 num_shards = info.num_shards;
31f18b77
FG
1501 uint64_t objs_per_shard =
1502 (num_shards) ? num_objects/num_shards : num_objects;
7c673cae
FG
1503 {
1504 bool warn = false;
1505 stringstream ss;
1506 if (objs_per_shard > safe_max_objs_per_shard) {
1507 double over =
1508 100 - (safe_max_objs_per_shard/objs_per_shard * 100);
1509 ss << boost::format("OVER %4f%%") % over;
1510 warn = true;
1511 } else {
1512 double fill_pct =
1513 objs_per_shard / safe_max_objs_per_shard * 100;
1514 if (fill_pct >= shard_warn_pct) {
1515 ss << boost::format("WARN %4f%%") % fill_pct;
1516 warn = true;
1517 } else {
1518 ss << "OK";
1519 }
1520 }
1521
1522 if (warn || (! warnings_only)) {
1523 formatter->open_object_section("bucket");
1524 formatter->dump_string("bucket", bucket.name);
1525 formatter->dump_string("tenant", bucket.tenant);
1526 formatter->dump_int("num_objects", num_objects);
1527 formatter->dump_int("num_shards", num_shards);
1528 formatter->dump_int("objects_per_shard", objs_per_shard);
1529 formatter->dump_string("fill_status", ss.str());
1530 formatter->close_section();
1531 }
1532 }
1533 }
1534
1535 done = (m_buckets.size() < max_entries);
1536 } while (!done); /* foreach: bucket */
1537
1538 formatter->close_section();
1539 formatter->close_section();
1540 formatter->flush(cout);
1541
1542 } /* foreach: user_id */
1543
1544 formatter->close_section();
1545 formatter->flush(cout);
1546
1547 return ret;
1548} /* RGWBucketAdminOp::limit_check */
1549
1550int RGWBucketAdminOp::info(RGWRados *store, RGWBucketAdminOpState& op_state,
1551 RGWFormatterFlusher& flusher)
1552{
1553 RGWBucket bucket;
1554 int ret;
1555
1556 string bucket_name = op_state.get_bucket_name();
1557
1558 if (!bucket_name.empty()) {
1559 ret = bucket.init(store, op_state);
1560 if (ret < 0)
1561 return ret;
1562 }
1563
1564 Formatter *formatter = flusher.get_formatter();
1565 flusher.start(0);
1566
1567 CephContext *cct = store->ctx();
1568
1569 const size_t max_entries = cct->_conf->rgw_list_buckets_max_chunk;
1570
1571 bool show_stats = op_state.will_fetch_stats();
1572 rgw_user user_id = op_state.get_user_id();
1573 if (op_state.is_user_op()) {
1574 formatter->open_array_section("buckets");
1575
1576 RGWUserBuckets buckets;
1577 string marker;
1578 bool is_truncated = false;
1579
1580 do {
1581 ret = rgw_read_user_buckets(store, op_state.get_user_id(), buckets,
1582 marker, string(), max_entries, false,
1583 &is_truncated);
1584 if (ret < 0)
1585 return ret;
1586
1587 map<string, RGWBucketEnt>& m = buckets.get_buckets();
1588 map<string, RGWBucketEnt>::iterator iter;
1589
1590 for (iter = m.begin(); iter != m.end(); ++iter) {
1591 std::string obj_name = iter->first;
1592 if (show_stats)
1593 bucket_stats(store, user_id.tenant, obj_name, formatter);
1594 else
1595 formatter->dump_string("bucket", obj_name);
1596
1597 marker = obj_name;
1598 }
1599
1600 flusher.flush();
1601 } while (is_truncated);
1602
1603 formatter->close_section();
1604 } else if (!bucket_name.empty()) {
1605 bucket_stats(store, user_id.tenant, bucket_name, formatter);
1606 } else {
1607 RGWAccessHandle handle;
1608
1609 formatter->open_array_section("buckets");
1610 if (store->list_buckets_init(&handle) >= 0) {
1611 rgw_bucket_dir_entry obj;
1612 while (store->list_buckets_next(obj, &handle) >= 0) {
1613 if (show_stats)
1614 bucket_stats(store, user_id.tenant, obj.key.name, formatter);
1615 else
1616 formatter->dump_string("bucket", obj.key.name);
1617 }
1618 }
1619
1620 formatter->close_section();
1621 }
1622
1623 flusher.flush();
1624
1625 return 0;
1626}
1627
1628
1629void rgw_data_change::dump(Formatter *f) const
1630{
1631 string type;
1632 switch (entity_type) {
1633 case ENTITY_TYPE_BUCKET:
1634 type = "bucket";
1635 break;
1636 default:
1637 type = "unknown";
1638 }
1639 encode_json("entity_type", type, f);
1640 encode_json("key", key, f);
1641 utime_t ut(timestamp);
1642 encode_json("timestamp", ut, f);
1643}
1644
1645void rgw_data_change::decode_json(JSONObj *obj) {
1646 string s;
1647 JSONDecoder::decode_json("entity_type", s, obj);
1648 if (s == "bucket") {
1649 entity_type = ENTITY_TYPE_BUCKET;
1650 } else {
1651 entity_type = ENTITY_TYPE_UNKNOWN;
1652 }
1653 JSONDecoder::decode_json("key", key, obj);
1654 utime_t ut;
1655 JSONDecoder::decode_json("timestamp", ut, obj);
1656 timestamp = ut.to_real_time();
1657}
1658
1659void rgw_data_change_log_entry::dump(Formatter *f) const
1660{
1661 encode_json("log_id", log_id, f);
1662 utime_t ut(log_timestamp);
1663 encode_json("log_timestamp", ut, f);
1664 encode_json("entry", entry, f);
1665}
1666
1667void rgw_data_change_log_entry::decode_json(JSONObj *obj) {
1668 JSONDecoder::decode_json("log_id", log_id, obj);
1669 utime_t ut;
1670 JSONDecoder::decode_json("log_timestamp", ut, obj);
1671 log_timestamp = ut.to_real_time();
1672 JSONDecoder::decode_json("entry", entry, obj);
1673}
1674
1675int RGWDataChangesLog::choose_oid(const rgw_bucket_shard& bs) {
1676 const string& name = bs.bucket.name;
1677 int shard_shift = (bs.shard_id > 0 ? bs.shard_id : 0);
1678 uint32_t r = (ceph_str_hash_linux(name.c_str(), name.size()) + shard_shift) % num_shards;
1679
1680 return (int)r;
1681}
1682
1683int RGWDataChangesLog::renew_entries()
1684{
1685 if (!store->need_to_log_data())
1686 return 0;
1687
1688 /* we can't keep the bucket name as part of the cls_log_entry, and we need
1689 * it later, so we keep two lists under the map */
1690 map<int, pair<list<rgw_bucket_shard>, list<cls_log_entry> > > m;
1691
1692 lock.Lock();
1693 map<rgw_bucket_shard, bool> entries;
1694 entries.swap(cur_cycle);
1695 lock.Unlock();
1696
1697 map<rgw_bucket_shard, bool>::iterator iter;
1698 string section;
1699 real_time ut = real_clock::now();
1700 for (iter = entries.begin(); iter != entries.end(); ++iter) {
1701 const rgw_bucket_shard& bs = iter->first;
1702
1703 int index = choose_oid(bs);
1704
1705 cls_log_entry entry;
1706
1707 rgw_data_change change;
1708 bufferlist bl;
1709 change.entity_type = ENTITY_TYPE_BUCKET;
1710 change.key = bs.get_key();
1711 change.timestamp = ut;
1712 ::encode(change, bl);
1713
1714 store->time_log_prepare_entry(entry, ut, section, change.key, bl);
1715
1716 m[index].first.push_back(bs);
1717 m[index].second.emplace_back(std::move(entry));
1718 }
1719
1720 map<int, pair<list<rgw_bucket_shard>, list<cls_log_entry> > >::iterator miter;
1721 for (miter = m.begin(); miter != m.end(); ++miter) {
1722 list<cls_log_entry>& entries = miter->second.second;
1723
1724 real_time now = real_clock::now();
1725
1726 int ret = store->time_log_add(oids[miter->first], entries, NULL);
1727 if (ret < 0) {
1728 /* we don't really need to have a special handling for failed cases here,
1729 * as this is just an optimization. */
1730 lderr(cct) << "ERROR: store->time_log_add() returned " << ret << dendl;
1731 return ret;
1732 }
1733
1734 real_time expiration = now;
1735 expiration += make_timespan(cct->_conf->rgw_data_log_window);
1736
1737 list<rgw_bucket_shard>& buckets = miter->second.first;
1738 list<rgw_bucket_shard>::iterator liter;
1739 for (liter = buckets.begin(); liter != buckets.end(); ++liter) {
1740 update_renewed(*liter, expiration);
1741 }
1742 }
1743
1744 return 0;
1745}
1746
1747void RGWDataChangesLog::_get_change(const rgw_bucket_shard& bs, ChangeStatusPtr& status)
1748{
1749 assert(lock.is_locked());
1750 if (!changes.find(bs, status)) {
1751 status = ChangeStatusPtr(new ChangeStatus);
1752 changes.add(bs, status);
1753 }
1754}
1755
1756void RGWDataChangesLog::register_renew(rgw_bucket_shard& bs)
1757{
1758 Mutex::Locker l(lock);
1759 cur_cycle[bs] = true;
1760}
1761
1762void RGWDataChangesLog::update_renewed(rgw_bucket_shard& bs, real_time& expiration)
1763{
1764 Mutex::Locker l(lock);
1765 ChangeStatusPtr status;
1766 _get_change(bs, status);
1767
1768 ldout(cct, 20) << "RGWDataChangesLog::update_renewd() bucket_name=" << bs.bucket.name << " shard_id=" << bs.shard_id << " expiration=" << expiration << dendl;
1769 status->cur_expiration = expiration;
1770}
1771
1772int RGWDataChangesLog::get_log_shard_id(rgw_bucket& bucket, int shard_id) {
1773 rgw_bucket_shard bs(bucket, shard_id);
1774
1775 return choose_oid(bs);
1776}
1777
1778int RGWDataChangesLog::add_entry(rgw_bucket& bucket, int shard_id) {
1779 if (!store->need_to_log_data())
1780 return 0;
1781
1782 rgw_bucket_shard bs(bucket, shard_id);
1783
1784 int index = choose_oid(bs);
1785 mark_modified(index, bs);
1786
1787 lock.Lock();
1788
1789 ChangeStatusPtr status;
1790 _get_change(bs, status);
1791
1792 lock.Unlock();
1793
1794 real_time now = real_clock::now();
1795
1796 status->lock->Lock();
1797
1798 ldout(cct, 20) << "RGWDataChangesLog::add_entry() bucket.name=" << bucket.name << " shard_id=" << shard_id << " now=" << now << " cur_expiration=" << status->cur_expiration << dendl;
1799
1800 if (now < status->cur_expiration) {
1801 /* no need to send, recently completed */
1802 status->lock->Unlock();
1803
1804 register_renew(bs);
1805 return 0;
1806 }
1807
1808 RefCountedCond *cond;
1809
1810 if (status->pending) {
1811 cond = status->cond;
1812
1813 assert(cond);
1814
1815 status->cond->get();
1816 status->lock->Unlock();
1817
1818 int ret = cond->wait();
1819 cond->put();
1820 if (!ret) {
1821 register_renew(bs);
1822 }
1823 return ret;
1824 }
1825
1826 status->cond = new RefCountedCond;
1827 status->pending = true;
1828
1829 string& oid = oids[index];
1830 real_time expiration;
1831
1832 int ret;
1833
1834 do {
1835 status->cur_sent = now;
1836
1837 expiration = now;
1838 expiration += ceph::make_timespan(cct->_conf->rgw_data_log_window);
1839
1840 status->lock->Unlock();
1841
1842 bufferlist bl;
1843 rgw_data_change change;
1844 change.entity_type = ENTITY_TYPE_BUCKET;
1845 change.key = bs.get_key();
1846 change.timestamp = now;
1847 ::encode(change, bl);
1848 string section;
1849
1850 ldout(cct, 20) << "RGWDataChangesLog::add_entry() sending update with now=" << now << " cur_expiration=" << expiration << dendl;
1851
1852 ret = store->time_log_add(oid, now, section, change.key, bl);
1853
1854 now = real_clock::now();
1855
1856 status->lock->Lock();
1857
1858 } while (!ret && real_clock::now() > expiration);
1859
1860 cond = status->cond;
1861
1862 status->pending = false;
1863 status->cur_expiration = status->cur_sent; /* time of when operation started, not completed */
1864 status->cur_expiration += make_timespan(cct->_conf->rgw_data_log_window);
1865 status->cond = NULL;
1866 status->lock->Unlock();
1867
1868 cond->done(ret);
1869 cond->put();
1870
1871 return ret;
1872}
1873
1874int RGWDataChangesLog::list_entries(int shard, const real_time& start_time, const real_time& end_time, int max_entries,
1875 list<rgw_data_change_log_entry>& entries,
1876 const string& marker,
1877 string *out_marker,
1878 bool *truncated) {
31f18b77
FG
1879 if (shard >= num_shards)
1880 return -EINVAL;
7c673cae
FG
1881
1882 list<cls_log_entry> log_entries;
1883
1884 int ret = store->time_log_list(oids[shard], start_time, end_time,
1885 max_entries, log_entries, marker,
1886 out_marker, truncated);
1887 if (ret < 0)
1888 return ret;
1889
1890 list<cls_log_entry>::iterator iter;
1891 for (iter = log_entries.begin(); iter != log_entries.end(); ++iter) {
1892 rgw_data_change_log_entry log_entry;
1893 log_entry.log_id = iter->id;
1894 real_time rt = iter->timestamp.to_real_time();
1895 log_entry.log_timestamp = rt;
1896 bufferlist::iterator liter = iter->data.begin();
1897 try {
1898 ::decode(log_entry.entry, liter);
1899 } catch (buffer::error& err) {
1900 lderr(cct) << "ERROR: failed to decode data changes log entry" << dendl;
1901 return -EIO;
1902 }
1903 entries.push_back(log_entry);
1904 }
1905
1906 return 0;
1907}
1908
1909int RGWDataChangesLog::list_entries(const real_time& start_time, const real_time& end_time, int max_entries,
1910 list<rgw_data_change_log_entry>& entries, LogMarker& marker, bool *ptruncated) {
1911 bool truncated;
1912 entries.clear();
1913
1914 for (; marker.shard < num_shards && (int)entries.size() < max_entries;
1915 marker.shard++, marker.marker.clear()) {
1916 int ret = list_entries(marker.shard, start_time, end_time, max_entries - entries.size(), entries,
1917 marker.marker, NULL, &truncated);
1918 if (ret == -ENOENT) {
1919 continue;
1920 }
1921 if (ret < 0) {
1922 return ret;
1923 }
1924 if (truncated) {
1925 *ptruncated = true;
1926 return 0;
1927 }
1928 }
1929
1930 *ptruncated = (marker.shard < num_shards);
1931
1932 return 0;
1933}
1934
1935int RGWDataChangesLog::get_info(int shard_id, RGWDataChangesLogInfo *info)
1936{
1937 if (shard_id >= num_shards)
1938 return -EINVAL;
1939
1940 string oid = oids[shard_id];
1941
1942 cls_log_header header;
1943
1944 int ret = store->time_log_info(oid, &header);
1945 if ((ret < 0) && (ret != -ENOENT))
1946 return ret;
1947
1948 info->marker = header.max_marker;
1949 info->last_update = header.max_time.to_real_time();
1950
1951 return 0;
1952}
1953
1954int RGWDataChangesLog::trim_entries(int shard_id, const real_time& start_time, const real_time& end_time,
1955 const string& start_marker, const string& end_marker)
1956{
1957 int ret;
1958
1959 if (shard_id > num_shards)
1960 return -EINVAL;
1961
1962 ret = store->time_log_trim(oids[shard_id], start_time, end_time, start_marker, end_marker);
1963
31f18b77 1964 if (ret == -ENOENT || ret == -ENODATA)
7c673cae
FG
1965 ret = 0;
1966
1967 return ret;
1968}
1969
1970int RGWDataChangesLog::trim_entries(const real_time& start_time, const real_time& end_time,
1971 const string& start_marker, const string& end_marker)
1972{
1973 for (int shard = 0; shard < num_shards; shard++) {
1974 int ret = store->time_log_trim(oids[shard], start_time, end_time, start_marker, end_marker);
31f18b77 1975 if (ret == -ENOENT || ret == -ENODATA) {
7c673cae
FG
1976 continue;
1977 }
1978 if (ret < 0)
1979 return ret;
1980 }
1981
1982 return 0;
1983}
1984
1985bool RGWDataChangesLog::going_down()
1986{
1987 return down_flag;
1988}
1989
1990RGWDataChangesLog::~RGWDataChangesLog() {
1991 down_flag = true;
1992 renew_thread->stop();
1993 renew_thread->join();
1994 delete renew_thread;
1995 delete[] oids;
1996}
1997
1998void *RGWDataChangesLog::ChangesRenewThread::entry() {
1999 do {
2000 dout(2) << "RGWDataChangesLog::ChangesRenewThread: start" << dendl;
2001 int r = log->renew_entries();
2002 if (r < 0) {
2003 dout(0) << "ERROR: RGWDataChangesLog::renew_entries returned error r=" << r << dendl;
2004 }
2005
2006 if (log->going_down())
2007 break;
2008
2009 int interval = cct->_conf->rgw_data_log_window * 3 / 4;
2010 lock.Lock();
2011 cond.WaitInterval(lock, utime_t(interval, 0));
2012 lock.Unlock();
2013 } while (!log->going_down());
2014
2015 return NULL;
2016}
2017
2018void RGWDataChangesLog::ChangesRenewThread::stop()
2019{
2020 Mutex::Locker l(lock);
2021 cond.Signal();
2022}
2023
2024void RGWDataChangesLog::mark_modified(int shard_id, const rgw_bucket_shard& bs)
2025{
2026 auto key = bs.get_key();
2027 modified_lock.get_read();
2028 map<int, set<string> >::iterator iter = modified_shards.find(shard_id);
2029 if (iter != modified_shards.end()) {
2030 set<string>& keys = iter->second;
2031 if (keys.find(key) != keys.end()) {
2032 modified_lock.unlock();
2033 return;
2034 }
2035 }
2036 modified_lock.unlock();
2037
2038 RWLock::WLocker wl(modified_lock);
2039 modified_shards[shard_id].insert(key);
2040}
2041
2042void RGWDataChangesLog::read_clear_modified(map<int, set<string> > &modified)
2043{
2044 RWLock::WLocker wl(modified_lock);
2045 modified.swap(modified_shards);
2046 modified_shards.clear();
2047}
2048
2049void RGWBucketCompleteInfo::dump(Formatter *f) const {
2050 encode_json("bucket_info", info, f);
2051 encode_json("attrs", attrs, f);
2052}
2053
2054void RGWBucketCompleteInfo::decode_json(JSONObj *obj) {
2055 JSONDecoder::decode_json("bucket_info", info, obj);
2056 JSONDecoder::decode_json("attrs", attrs, obj);
2057}
2058
2059class RGWBucketMetadataHandler : public RGWMetadataHandler {
2060
2061public:
2062 string get_type() override { return "bucket"; }
2063
2064 int get(RGWRados *store, string& entry, RGWMetadataObject **obj) override {
2065 RGWObjVersionTracker ot;
2066 RGWBucketEntryPoint be;
2067
2068 real_time mtime;
2069 map<string, bufferlist> attrs;
2070 RGWObjectCtx obj_ctx(store);
2071
2072 string tenant_name, bucket_name;
2073 parse_bucket(entry, &tenant_name, &bucket_name);
2074 int ret = store->get_bucket_entrypoint_info(obj_ctx, tenant_name, bucket_name, be, &ot, &mtime, &attrs);
2075 if (ret < 0)
2076 return ret;
2077
2078 RGWBucketEntryMetadataObject *mdo = new RGWBucketEntryMetadataObject(be, ot.read_version, mtime);
2079
2080 *obj = mdo;
2081
2082 return 0;
2083 }
2084
2085 int put(RGWRados *store, string& entry, RGWObjVersionTracker& objv_tracker,
2086 real_time mtime, JSONObj *obj, sync_type_t sync_type) override {
2087 RGWBucketEntryPoint be, old_be;
2088 try {
2089 decode_json_obj(be, obj);
2090 } catch (JSONDecoder::err& e) {
2091 return -EINVAL;
2092 }
2093
2094 real_time orig_mtime;
2095 map<string, bufferlist> attrs;
2096
2097 RGWObjVersionTracker old_ot;
2098 RGWObjectCtx obj_ctx(store);
2099
2100 string tenant_name, bucket_name;
2101 parse_bucket(entry, &tenant_name, &bucket_name);
2102 int ret = store->get_bucket_entrypoint_info(obj_ctx, tenant_name, bucket_name, old_be, &old_ot, &orig_mtime, &attrs);
2103 if (ret < 0 && ret != -ENOENT)
2104 return ret;
2105
2106 // are we actually going to perform this put, or is it too old?
2107 if (ret != -ENOENT &&
2108 !check_versions(old_ot.read_version, orig_mtime,
2109 objv_tracker.write_version, mtime, sync_type)) {
2110 return STATUS_NO_APPLY;
2111 }
2112
2113 objv_tracker.read_version = old_ot.read_version; /* maintain the obj version we just read */
2114
2115 ret = store->put_bucket_entrypoint_info(tenant_name, bucket_name, be, false, objv_tracker, mtime, &attrs);
2116 if (ret < 0)
2117 return ret;
2118
2119 /* link bucket */
2120 if (be.linked) {
2121 ret = rgw_link_bucket(store, be.owner, be.bucket, be.creation_time, false);
2122 } else {
2123 ret = rgw_unlink_bucket(store, be.owner, be.bucket.tenant, be.bucket.name, false);
2124 }
2125
2126 return ret;
2127 }
2128
2129 struct list_keys_info {
2130 RGWRados *store;
2131 RGWListRawObjsCtx ctx;
2132 };
2133
2134 int remove(RGWRados *store, string& entry, RGWObjVersionTracker& objv_tracker) override {
2135 RGWBucketEntryPoint be;
2136 RGWObjectCtx obj_ctx(store);
2137
2138 string tenant_name, bucket_name;
2139 parse_bucket(entry, &tenant_name, &bucket_name);
2140 int ret = store->get_bucket_entrypoint_info(obj_ctx, tenant_name, bucket_name, be, &objv_tracker, NULL, NULL);
2141 if (ret < 0)
2142 return ret;
2143
2144 /*
31f18b77 2145 * We're unlinking the bucket but we don't want to update the entrypoint here - we're removing
7c673cae
FG
2146 * it immediately and don't want to invalidate our cached objv_version or the bucket obj removal
2147 * will incorrectly fail.
2148 */
2149 ret = rgw_unlink_bucket(store, be.owner, tenant_name, bucket_name, false);
2150 if (ret < 0) {
2151 lderr(store->ctx()) << "could not unlink bucket=" << entry << " owner=" << be.owner << dendl;
2152 }
2153
2154 ret = rgw_bucket_delete_bucket_obj(store, tenant_name, bucket_name, objv_tracker);
2155 if (ret < 0) {
2156 lderr(store->ctx()) << "could not delete bucket=" << entry << dendl;
2157 }
2158 /* idempotent */
2159 return 0;
2160 }
2161
2162 void get_pool_and_oid(RGWRados *store, const string& key, rgw_pool& pool, string& oid) override {
2163 oid = key;
2164 pool = store->get_zone_params().domain_root;
2165 }
2166
2167 int list_keys_init(RGWRados *store, void **phandle) override
2168 {
2169 list_keys_info *info = new list_keys_info;
2170
2171 info->store = store;
2172
2173 *phandle = (void *)info;
2174
2175 return 0;
2176 }
2177
2178 int list_keys_next(void *handle, int max, list<string>& keys, bool *truncated) override {
2179 list_keys_info *info = static_cast<list_keys_info *>(handle);
2180
2181 string no_filter;
2182
2183 keys.clear();
2184
2185 RGWRados *store = info->store;
2186
2187 list<string> unfiltered_keys;
2188
2189 int ret = store->list_raw_objects(store->get_zone_params().domain_root, no_filter,
2190 max, info->ctx, unfiltered_keys, truncated);
2191 if (ret < 0 && ret != -ENOENT)
2192 return ret;
2193 if (ret == -ENOENT) {
2194 if (truncated)
2195 *truncated = false;
2196 return 0;
2197 }
2198
2199 // now filter out the system entries
2200 list<string>::iterator iter;
2201 for (iter = unfiltered_keys.begin(); iter != unfiltered_keys.end(); ++iter) {
2202 string& k = *iter;
2203
2204 if (k[0] != '.') {
2205 keys.push_back(k);
2206 }
2207 }
2208
2209 return 0;
2210 }
2211
2212 void list_keys_complete(void *handle) override {
2213 list_keys_info *info = static_cast<list_keys_info *>(handle);
2214 delete info;
2215 }
2216};
2217
2218class RGWBucketInstanceMetadataHandler : public RGWMetadataHandler {
2219
2220public:
2221 string get_type() override { return "bucket.instance"; }
2222
2223 int get(RGWRados *store, string& oid, RGWMetadataObject **obj) override {
2224 RGWBucketCompleteInfo bci;
2225
2226 real_time mtime;
2227 RGWObjectCtx obj_ctx(store);
2228
2229 int ret = store->get_bucket_instance_info(obj_ctx, oid, bci.info, &mtime, &bci.attrs);
2230 if (ret < 0)
2231 return ret;
2232
2233 RGWBucketInstanceMetadataObject *mdo = new RGWBucketInstanceMetadataObject(bci, bci.info.objv_tracker.read_version, mtime);
2234
2235 *obj = mdo;
2236
2237 return 0;
2238 }
2239
2240 int put(RGWRados *store, string& entry, RGWObjVersionTracker& objv_tracker,
2241 real_time mtime, JSONObj *obj, sync_type_t sync_type) override {
2242 RGWBucketCompleteInfo bci, old_bci;
2243 try {
2244 decode_json_obj(bci, obj);
2245 } catch (JSONDecoder::err& e) {
2246 return -EINVAL;
2247 }
2248
2249 real_time orig_mtime;
2250 RGWObjectCtx obj_ctx(store);
2251
2252 int ret = store->get_bucket_instance_info(obj_ctx, entry, old_bci.info,
2253 &orig_mtime, &old_bci.attrs);
2254 bool exists = (ret != -ENOENT);
2255 if (ret < 0 && exists)
2256 return ret;
2257
2258 if (!exists || old_bci.info.bucket.bucket_id != bci.info.bucket.bucket_id) {
2259 /* a new bucket, we need to select a new bucket placement for it */
2260 auto key(entry);
2261 rgw_bucket_instance_oid_to_key(key);
2262 string tenant_name;
2263 string bucket_name;
2264 string bucket_instance;
2265 parse_bucket(key, &tenant_name, &bucket_name, &bucket_instance);
2266
2267 RGWZonePlacementInfo rule_info;
2268 bci.info.bucket.name = bucket_name;
2269 bci.info.bucket.bucket_id = bucket_instance;
2270 bci.info.bucket.tenant = tenant_name;
2271 ret = store->select_bucket_location_by_rule(bci.info.placement_rule, &rule_info);
2272 if (ret < 0) {
2273 ldout(store->ctx(), 0) << "ERROR: select_bucket_placement() returned " << ret << dendl;
2274 return ret;
2275 }
2276 bci.info.index_type = rule_info.index_type;
2277 } else {
2278 /* existing bucket, keep its placement */
2279 bci.info.bucket.explicit_placement = old_bci.info.bucket.explicit_placement;
2280 bci.info.placement_rule = old_bci.info.placement_rule;
2281 }
2282
c07f9fc5
FG
2283 if (exists && old_bci.info.datasync_flag_enabled() != bci.info.datasync_flag_enabled()) {
2284 int shards_num = bci.info.num_shards? bci.info.num_shards : 1;
2285 int shard_id = bci.info.num_shards? 0 : -1;
2286
2287 if (!bci.info.datasync_flag_enabled()) {
2288 ret = store->stop_bi_log_entries(bci.info, -1);
2289 if (ret < 0) {
2290 lderr(store->ctx()) << "ERROR: failed writing bilog" << dendl;
2291 return ret;
2292 }
2293 } else {
2294 ret = store->resync_bi_log_entries(bci.info, -1);
2295 if (ret < 0) {
2296 lderr(store->ctx()) << "ERROR: failed writing bilog" << dendl;
2297 return ret;
2298 }
2299 }
2300
2301 for (int i = 0; i < shards_num; ++i, ++shard_id) {
2302 ret = store->data_log->add_entry(bci.info.bucket, shard_id);
2303 if (ret < 0) {
2304 lderr(store->ctx()) << "ERROR: failed writing data log" << dendl;
2305 return ret;
2306 }
2307 }
2308 }
2309
7c673cae
FG
2310 // are we actually going to perform this put, or is it too old?
2311 if (exists &&
2312 !check_versions(old_bci.info.objv_tracker.read_version, orig_mtime,
2313 objv_tracker.write_version, mtime, sync_type)) {
2314 objv_tracker.read_version = old_bci.info.objv_tracker.read_version;
2315 return STATUS_NO_APPLY;
2316 }
2317
2318 /* record the read version (if any), store the new version */
2319 bci.info.objv_tracker.read_version = old_bci.info.objv_tracker.read_version;
2320 bci.info.objv_tracker.write_version = objv_tracker.write_version;
2321
2322 ret = store->put_bucket_instance_info(bci.info, false, mtime, &bci.attrs);
2323 if (ret < 0)
2324 return ret;
2325
2326 objv_tracker = bci.info.objv_tracker;
2327
2328 ret = store->init_bucket_index(bci.info, bci.info.num_shards);
2329 if (ret < 0)
2330 return ret;
2331
2332 return STATUS_APPLIED;
2333 }
2334
2335 struct list_keys_info {
2336 RGWRados *store;
2337 RGWListRawObjsCtx ctx;
2338 };
2339
2340 int remove(RGWRados *store, string& entry, RGWObjVersionTracker& objv_tracker) override {
2341 RGWBucketInfo info;
2342 RGWObjectCtx obj_ctx(store);
2343
2344 int ret = store->get_bucket_instance_info(obj_ctx, entry, info, NULL, NULL);
2345 if (ret < 0 && ret != -ENOENT)
2346 return ret;
2347
2348 return rgw_bucket_instance_remove_entry(store, entry, &info.objv_tracker);
2349 }
2350
2351 void get_pool_and_oid(RGWRados *store, const string& key, rgw_pool& pool, string& oid) override {
2352 oid = RGW_BUCKET_INSTANCE_MD_PREFIX + key;
2353 rgw_bucket_instance_key_to_oid(oid);
2354 pool = store->get_zone_params().domain_root;
2355 }
2356
2357 int list_keys_init(RGWRados *store, void **phandle) override
2358 {
2359 list_keys_info *info = new list_keys_info;
2360
2361 info->store = store;
2362
2363 *phandle = (void *)info;
2364
2365 return 0;
2366 }
2367
2368 int list_keys_next(void *handle, int max, list<string>& keys, bool *truncated) override {
2369 list_keys_info *info = static_cast<list_keys_info *>(handle);
2370
2371 string no_filter;
2372
2373 keys.clear();
2374
2375 RGWRados *store = info->store;
2376
2377 list<string> unfiltered_keys;
2378
2379 int ret = store->list_raw_objects(store->get_zone_params().domain_root, no_filter,
2380 max, info->ctx, unfiltered_keys, truncated);
2381 if (ret < 0 && ret != -ENOENT)
2382 return ret;
2383 if (ret == -ENOENT) {
2384 if (truncated)
2385 *truncated = false;
2386 return 0;
2387 }
2388
2389 constexpr int prefix_size = sizeof(RGW_BUCKET_INSTANCE_MD_PREFIX) - 1;
2390 // now filter in the relevant entries
2391 list<string>::iterator iter;
2392 for (iter = unfiltered_keys.begin(); iter != unfiltered_keys.end(); ++iter) {
2393 string& k = *iter;
2394
2395 if (k.compare(0, prefix_size, RGW_BUCKET_INSTANCE_MD_PREFIX) == 0) {
2396 auto oid = k.substr(prefix_size);
2397 rgw_bucket_instance_oid_to_key(oid);
2398 keys.emplace_back(std::move(oid));
2399 }
2400 }
2401
2402 return 0;
2403 }
2404
2405 void list_keys_complete(void *handle) override {
2406 list_keys_info *info = static_cast<list_keys_info *>(handle);
2407 delete info;
2408 }
2409
2410 /*
2411 * hash entry for mdlog placement. Use the same hash key we'd have for the bucket entry
2412 * point, so that the log entries end up at the same log shard, so that we process them
2413 * in order
2414 */
2415 void get_hash_key(const string& section, const string& key, string& hash_key) override {
2416 string k;
2417 int pos = key.find(':');
2418 if (pos < 0)
2419 k = key;
2420 else
2421 k = key.substr(0, pos);
2422 hash_key = "bucket:" + k;
2423 }
2424};
2425
2426void rgw_bucket_init(RGWMetadataManager *mm)
2427{
2428 bucket_meta_handler = new RGWBucketMetadataHandler;
2429 mm->register_handler(bucket_meta_handler);
2430 bucket_instance_meta_handler = new RGWBucketInstanceMetadataHandler;
2431 mm->register_handler(bucket_instance_meta_handler);
2432}