From 59a91037b43b8baf99a3cf9bc336710a2019d90c Mon Sep 17 00:00:00 2001 From: Jeegn Chen Date: Wed, 25 Nov 2020 17:15:25 +0800 Subject: [PATCH] rgw: avoid infinite loop when deleting a bucket When deleting a bucket with an incomplete multipart upload that has about 2000 parts uploaded, we noticed an infinite loop, which stopped s3cmd from deleting the bucket forever. Per check, when the bucket index was sharded (for example 128 shards), the original logic in RGWRados::cls_bucket_list_unordered() did not calculate the bucket shard ID correctly when the index key of a data part was taken as the marker. The issue is not necessarily reproduced each time. It will depend on the key of the object. To reproduce it in 128-shard bucket, we use 334 as the key for the incomplete multipart upload, which will be located in Shard 127 (known by experiment). In this setup, the original logic will usually come out a shard ID smaller than 127 (since 127 is the largest one) from the marker and thus a circle is constructed, which results in an infinite loop. PS: Some times the bucket ID calculation may incorrectly going forward instead of backward. Thus, the check logic may skip some shards, which may have regular keys. In such scenarios, some non-empty buckets may be deleted by accident. Fixes: http://tracker.ceph.com/issues/49206 Signed-off-by: Jeegn Chen (cherry picked from commit 3cafe5774a5a453d58a3a6bed1f02d3200c4bb1d) Conflicts: src/rgw/rgw_rados.cc Cherry-pick notes: - Octopus cls_bucket_list_unordered doesn't take DoutPrefixProvider as first arg --- src/rgw/rgw_rados.cc | 41 ++++++++++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/src/rgw/rgw_rados.cc b/src/rgw/rgw_rados.cc index aef7ea42f1f88..36e3b304be28b 100644 --- a/src/rgw/rgw_rados.cc +++ b/src/rgw/rgw_rados.cc @@ -8535,6 +8535,22 @@ int RGWRados::cls_bucket_list_ordered(RGWBucketInfo& bucket_info, } +// A helper function to retrieve the hash source from an incomplete multipart entry +// by removing everything from the second last dot to the end. +static int parse_index_hash_source(const std::string& oid_wo_ns, std::string *index_hash_source) { + std::size_t found = oid_wo_ns.rfind('.'); + if (found == std::string::npos || found < 1) { + return -EINVAL; + } + found = oid_wo_ns.rfind('.', found - 1); + if (found == std::string::npos || found < 1) { + return -EINVAL; + } + *index_hash_source = oid_wo_ns.substr(0, found); + return 0; +} + + int RGWRados::cls_bucket_list_unordered(RGWBucketInfo& bucket_info, int shard_id, const rgw_obj_index_key& start_after, @@ -8576,18 +8592,11 @@ int RGWRados::cls_bucket_list_unordered(RGWBucketInfo& bucket_info, // in it, so we need to get to the bucket shard index, so we can // start reading from there - std::string key; - // test whether object name is a multipart meta name - if(! multipart_meta_filter.filter(start_after.name, key)) { - // if multipart_meta_filter fails, must be "regular" (i.e., - // unadorned) and the name is the key - key = start_after.name; - } // now convert the key (oid) to an rgw_obj_key since that will // separate out the namespace, name, and instance rgw_obj_key obj_key; - bool parsed = rgw_obj_key::parse_raw_oid(key, &obj_key); + bool parsed = rgw_obj_key::parse_raw_oid(start_after.name, &obj_key); if (!parsed) { ldout(cct, 0) << "ERROR: RGWRados::cls_bucket_list_unordered received an invalid " @@ -8601,7 +8610,21 @@ int RGWRados::cls_bucket_list_unordered(RGWBucketInfo& bucket_info, } else { // so now we have the key used to compute the bucket index shard // and can extract the specific shard from it - current_shard = svc.bi_rados->bucket_shard_index(obj_key.name, num_shards); + if (obj_key.ns == RGW_OBJ_NS_MULTIPART) { + // Use obj_key.ns == RGW_OBJ_NS_MULTIPART instead of + // the implementation relying on MultipartMetaFilter + // because MultipartMetaFilter only checks .meta suffix, which may + // exclude data multiparts but include some regular objects with .meta suffix + // by mistake. + string index_hash_source; + r = parse_index_hash_source(obj_key.name, &index_hash_source); + if (r < 0) { + return r; + } + current_shard = svc.bi_rados->bucket_shard_index(index_hash_source, num_shards); + } else { + current_shard = svc.bi_rados->bucket_shard_index(obj_key.name, num_shards); + } } } -- 2.39.5