From: J. Eric Ivancich Date: Mon, 24 Mar 2025 23:45:06 +0000 (-0400) Subject: rgw: add force option to `radosgw-admin object rm ...` X-Git-Tag: v19.2.3~180^2~2 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=4f212980318eafd8480a6c0297f98ae9ad116cee;p=ceph.git rgw: add force option to `radosgw-admin object rm ...` The `radosgw-admin object rm ...` sub-command will give up if it determines that there's an issue with the head object. This can make it difficult for an admin to clean up a bucket index when there's a damaged or missing head object. When the user adds the "--yes-i-really-mean-it" command-line option, it enables the "force mode". The bucket index entry(ies) will be removed. If the object being removed is the current version in a versioned bucket, the appropriate changes to the OLH will take place. Signed-off-by: J. Eric Ivancich (cherry picked from commit 7e1ac21069e14a4d6c4d4748971943b7c8b8137c) --- diff --git a/src/rgw/driver/rados/rgw_bucket.cc b/src/rgw/driver/rados/rgw_bucket.cc index f08df5e62a6..a884cefe442 100644 --- a/src/rgw/driver/rados/rgw_bucket.cc +++ b/src/rgw/driver/rados/rgw_bucket.cc @@ -144,12 +144,19 @@ bool rgw_bucket_object_check_filter(const std::string& oid) return rgw_obj_key::oid_to_key_in_ns(oid, &key, empty_ns); } -int rgw_remove_object(const DoutPrefixProvider *dpp, rgw::sal::Driver* driver, rgw::sal::Bucket* bucket, rgw_obj_key& key, optional_yield y) +int rgw_remove_object(const DoutPrefixProvider *dpp, + rgw::sal::Driver* driver, + rgw::sal::Bucket* bucket, + rgw_obj_key& key, + optional_yield y, + const bool force) { std::unique_ptr object = bucket->get_object(key); - return object->delete_object(dpp, y, rgw::sal::FLAG_LOG_OP, nullptr, nullptr); + const uint32_t possible_force_flag = force ? rgw::sal::FLAG_FORCE_OP : 0; + + return object->delete_object(dpp, y, rgw::sal::FLAG_LOG_OP | possible_force_flag, nullptr, nullptr); } static void set_err_msg(std::string *sink, std::string msg) diff --git a/src/rgw/driver/rados/rgw_bucket.h b/src/rgw/driver/rados/rgw_bucket.h index c7154cee6dd..edbb5856aea 100644 --- a/src/rgw/driver/rados/rgw_bucket.h +++ b/src/rgw/driver/rados/rgw_bucket.h @@ -207,7 +207,12 @@ public: static RGWBucketInstanceMetadataHandlerBase *alloc(rgw::sal::Driver* driver); }; -extern int rgw_remove_object(const DoutPrefixProvider *dpp, rgw::sal::Driver* driver, rgw::sal::Bucket* bucket, rgw_obj_key& key, optional_yield y); +extern int rgw_remove_object(const DoutPrefixProvider* dpp, + rgw::sal::Driver* driver, + rgw::sal::Bucket* bucket, + rgw_obj_key& key, + optional_yield y, + const bool force = false); extern int rgw_object_get_attr(rgw::sal::Driver* driver, rgw::sal::Object* obj, const char* attr_name, bufferlist& out_bl, diff --git a/src/rgw/driver/rados/rgw_rados.cc b/src/rgw/driver/rados/rgw_rados.cc index a1370b079dc..12d04529b84 100644 --- a/src/rgw/driver/rados/rgw_rados.cc +++ b/src/rgw/driver/rados/rgw_rados.cc @@ -5737,9 +5737,13 @@ struct tombstone_entry { * Delete an object. * bucket: name of the bucket storing the object * obj: name of the object to delete + * force: if b.i. entry exists but head object does not, still remove entry * Returns: 0 on success, -ERR# otherwise. */ -int RGWRados::Object::Delete::delete_obj(optional_yield y, const DoutPrefixProvider *dpp, bool log_op) +int RGWRados::Object::Delete::delete_obj(optional_yield y, + const DoutPrefixProvider* dpp, + bool log_op, + const bool force) { RGWRados *store = target->get_store(); const rgw_obj& src_obj = target->get_obj(); @@ -5797,8 +5801,10 @@ int RGWRados::Object::Delete::delete_obj(optional_yield y, const DoutPrefixProvi return r; } result.delete_marker = dirent.is_delete_marker(); - r = store->unlink_obj_instance(dpp, target->get_ctx(), target->get_bucket_info(), obj, params.olh_epoch, - y, params.bilog_flags, params.null_verid, params.zones_trace, add_log); + r = store->unlink_obj_instance( + dpp, target->get_ctx(), target->get_bucket_info(), obj, + params.olh_epoch, y, params.bilog_flags, + params.null_verid, params.zones_trace, add_log, force); if (r < 0) { return r; } @@ -5878,8 +5884,14 @@ int RGWRados::Object::Delete::delete_obj(optional_yield y, const DoutPrefixProvi } if (!state->exists) { - target->invalidate_state(); - return -ENOENT; + if (!force) { + target->invalidate_state(); + return -ENOENT; + } else { + ldpp_dout(dpp, 5) << "WARNING: head for \"" << src_obj << + "\" does not exist; will continue with deleting bucket " + "index entry(ies)" << dendl; + } } r = target->prepare_atomic_modification(dpp, op, false, NULL, NULL, NULL, true, false, y); @@ -5960,7 +5972,8 @@ int RGWRados::delete_obj(const DoutPrefixProvider *dpp, uint16_t bilog_flags, const real_time& expiration_time, rgw_zone_set *zones_trace, - bool log_op) + bool log_op, + const bool force) // force removal even if head object is broken { RGWRados::Object del_target(this, bucket_info, obj_ctx, obj); RGWRados::Object::Delete del_op(&del_target); @@ -5972,7 +5985,7 @@ int RGWRados::delete_obj(const DoutPrefixProvider *dpp, del_op.params.zones_trace = zones_trace; del_op.params.null_verid = null_verid; - return del_op.delete_obj(y, dpp, log_op ? rgw::sal::FLAG_LOG_OP : 0); + return del_op.delete_obj(y, dpp, log_op, force); } int RGWRados::delete_raw_obj(const DoutPrefixProvider *dpp, const rgw_raw_obj& obj, optional_yield y) @@ -8176,9 +8189,10 @@ int RGWRados::apply_olh_log(const DoutPrefixProvider *dpp, std::map >& log, uint64_t *plast_ver, optional_yield y, - bool null_verid, - rgw_zone_set* zones_trace, - bool log_op) + bool null_verid, + rgw_zone_set* zones_trace, + bool log_op, + const bool force) { if (log.empty()) { return 0; @@ -8291,7 +8305,9 @@ int RGWRados::apply_olh_log(const DoutPrefixProvider *dpp, liter != remove_instances.end(); ++liter) { cls_rgw_obj_key& key = *liter; rgw_obj obj_instance(bucket, key); - int ret = delete_obj(dpp, obj_ctx, bucket_info, obj_instance, 0, y, null_verid, RGW_BILOG_FLAG_VERSIONED_OP, ceph::real_time(), zones_trace, log_op); + int ret = delete_obj(dpp, obj_ctx, bucket_info, obj_instance, 0, y, + null_verid, RGW_BILOG_FLAG_VERSIONED_OP, + ceph::real_time(), zones_trace, log_op, force); if (ret < 0 && ret != -ENOENT) { ldpp_dout(dpp, 0) << "ERROR: delete_obj() returned " << ret << " obj_instance=" << obj_instance << dendl; return ret; @@ -8395,7 +8411,15 @@ int RGWRados::clear_olh(const DoutPrefixProvider *dpp, /* * read olh log and apply it */ -int RGWRados::update_olh(const DoutPrefixProvider *dpp, RGWObjectCtx& obj_ctx, RGWObjState *state, RGWBucketInfo& bucket_info, const rgw_obj& obj, optional_yield y, rgw_zone_set *zones_trace, bool null_verid, bool log_op) +int RGWRados::update_olh(const DoutPrefixProvider* dpp, + RGWObjectCtx& obj_ctx, + RGWObjState* state, + RGWBucketInfo& bucket_info, + const rgw_obj& obj, optional_yield y, + rgw_zone_set* zones_trace, + bool null_verid, + bool log_op, + const bool force) { map > log; bool is_truncated; @@ -8406,7 +8430,9 @@ int RGWRados::update_olh(const DoutPrefixProvider *dpp, RGWObjectCtx& obj_ctx, R if (ret < 0) { return ret; } - ret = apply_olh_log(dpp, obj_ctx, *state, bucket_info, obj, state->olh_tag, log, &ver_marker, y, null_verid, zones_trace, log_op); + ret = apply_olh_log(dpp, obj_ctx, *state, bucket_info, obj, + state->olh_tag, log, &ver_marker, y, + null_verid, zones_trace, log_op, force); if (ret < 0) { return ret; } @@ -8507,8 +8533,17 @@ int RGWRados::set_olh(const DoutPrefixProvider *dpp, RGWObjectCtx& obj_ctx, return 0; } -int RGWRados::unlink_obj_instance(const DoutPrefixProvider *dpp, RGWObjectCtx& obj_ctx, RGWBucketInfo& bucket_info, const rgw_obj& target_obj, - uint64_t olh_epoch, optional_yield y, uint16_t bilog_flags, bool null_verid, rgw_zone_set *zones_trace, bool log_op) +int RGWRados::unlink_obj_instance(const DoutPrefixProvider* dpp, + RGWObjectCtx& obj_ctx, + RGWBucketInfo& bucket_info, + const rgw_obj& target_obj, + uint64_t olh_epoch, + optional_yield y, + uint16_t bilog_flags, + bool null_verid, + rgw_zone_set* zones_trace, + bool log_op, + const bool force) { string op_tag; @@ -8563,7 +8598,8 @@ int RGWRados::unlink_obj_instance(const DoutPrefixProvider *dpp, RGWObjectCtx& o // it's possible that the pending xattr from this op prevented the olh // object from being cleaned by another thread that was deleting the last // existing version. We invoke a best-effort update_olh here to handle this case. - int r = update_olh(dpp, obj_ctx, state, bucket_info, olh_obj, y, zones_trace, null_verid, log_op); + int r = update_olh(dpp, obj_ctx, state, bucket_info, olh_obj, y, + zones_trace, null_verid, log_op, force); if (r < 0 && r != -ECANCELED) { ldpp_dout(dpp, 20) << "update_olh() target_obj=" << olh_obj << " returned " << r << dendl; } @@ -8577,7 +8613,8 @@ int RGWRados::unlink_obj_instance(const DoutPrefixProvider *dpp, RGWObjectCtx& o return -EIO; } - ret = update_olh(dpp, obj_ctx, state, bucket_info, olh_obj, y, zones_trace, null_verid, log_op); + ret = update_olh(dpp, obj_ctx, state, bucket_info, olh_obj, y, + zones_trace, null_verid, log_op, force); if (ret == -ECANCELED) { /* already did what we needed, no need to retry, raced with another user */ return 0; } diff --git a/src/rgw/driver/rados/rgw_rados.h b/src/rgw/driver/rados/rgw_rados.h index f28b467fb9d..c1d1cefc20e 100644 --- a/src/rgw/driver/rados/rgw_rados.h +++ b/src/rgw/driver/rados/rgw_rados.h @@ -869,7 +869,10 @@ public: explicit Delete(RGWRados::Object *_target) : target(_target) {} - int delete_obj(optional_yield y, const DoutPrefixProvider *dpp, bool log_op = true); + int delete_obj(optional_yield y, + const DoutPrefixProvider* dpp, + bool log_op, + const bool force); // if head object missing, do a best effort }; // struct RGWRados::Object::Delete struct Stat { @@ -1271,7 +1274,8 @@ public: uint16_t bilog_flags = 0, const ceph::real_time& expiration_time = ceph::real_time(), rgw_zone_set *zones_trace = nullptr, - bool log_op = true); + bool log_op = true, + const bool force = false); // if head object missing, do a best effort int delete_raw_obj(const DoutPrefixProvider *dpp, const rgw_raw_obj& obj, optional_yield y); @@ -1371,11 +1375,17 @@ public: std::map > *log, bool *is_truncated, optional_yield y); int bucket_index_trim_olh_log(const DoutPrefixProvider *dpp, RGWBucketInfo& bucket_info, RGWObjState& obj_state, const rgw_obj& obj_instance, uint64_t ver, optional_yield y); int bucket_index_clear_olh(const DoutPrefixProvider *dpp, RGWBucketInfo& bucket_info, const std::string& olh_tag, const rgw_obj& obj_instance, optional_yield y); - int apply_olh_log(const DoutPrefixProvider *dpp, RGWObjectCtx& obj_ctx, RGWObjState& obj_state, RGWBucketInfo& bucket_info, const rgw_obj& obj, + int apply_olh_log(const DoutPrefixProvider *dpp, RGWObjectCtx& obj_ctx, RGWObjState& obj_state, + RGWBucketInfo& bucket_info, const rgw_obj& obj, bufferlist& obj_tag, std::map >& log, - uint64_t *plast_ver, optional_yield y, bool null_verid, rgw_zone_set *zones_trace = nullptr, bool log_op = true); - int update_olh(const DoutPrefixProvider *dpp, RGWObjectCtx& obj_ctx, RGWObjState *state, RGWBucketInfo& bucket_info, const rgw_obj& obj, optional_yield y, - rgw_zone_set *zones_trace = nullptr, bool null_verid = false, bool log_op = true); + uint64_t *plast_ver, optional_yield y, bool null_verid, + rgw_zone_set *zones_trace = nullptr, + bool log_op = true, + const bool force = false); + int update_olh(const DoutPrefixProvider *dpp, RGWObjectCtx& obj_ctx, RGWObjState *state, + RGWBucketInfo& bucket_info, const rgw_obj& obj, optional_yield y, + rgw_zone_set *zones_trace = nullptr, bool null_verid = false, + bool log_op = true, const bool force = false); int clear_olh(const DoutPrefixProvider *dpp, RGWObjectCtx& obj_ctx, const rgw_obj& obj, @@ -1398,8 +1408,13 @@ public: bool skip_olh_obj_update = false); // can skip the OLH object update if, for example, repairing index int repair_olh(const DoutPrefixProvider *dpp, RGWObjState* state, const RGWBucketInfo& bucket_info, const rgw_obj& obj, optional_yield y); - int unlink_obj_instance(const DoutPrefixProvider *dpp, RGWObjectCtx& obj_ctx, RGWBucketInfo& bucket_info, const rgw_obj& target_obj, - uint64_t olh_epoch, optional_yield y, uint16_t bilog_flags, bool null_verid, rgw_zone_set *zones_trace = nullptr, bool log_op = true); + int unlink_obj_instance(const DoutPrefixProvider *dpp, + RGWObjectCtx& obj_ctx, RGWBucketInfo& bucket_info, + const rgw_obj& target_obj, + uint64_t olh_epoch, optional_yield y, + uint16_t bilog_flags, bool null_verid, + rgw_zone_set *zones_trace = nullptr, + bool log_op = true, const bool force = false); void check_pending_olh_entries(const DoutPrefixProvider *dpp, std::map& pending_entries, std::map *rm_pending_entries); int remove_olh_pending_entries(const DoutPrefixProvider *dpp, const RGWBucketInfo& bucket_info, RGWObjState& state, const rgw_obj& olh_obj, std::map& pending_attrs, optional_yield y); diff --git a/src/rgw/driver/rados/rgw_sal_rados.cc b/src/rgw/driver/rados/rgw_sal_rados.cc index bcb1e811402..ad9ce6edcd0 100644 --- a/src/rgw/driver/rados/rgw_sal_rados.cc +++ b/src/rgw/driver/rados/rgw_sal_rados.cc @@ -2793,6 +2793,7 @@ RadosObject::RadosDeleteOp::RadosDeleteOp(RadosObject *_source) : parent_op(&op_target) { } + int RadosObject::RadosDeleteOp::delete_obj(const DoutPrefixProvider* dpp, optional_yield y, uint32_t flags) { parent_op.params.bucket_owner = params.bucket_owner; @@ -2811,15 +2812,16 @@ int RadosObject::RadosDeleteOp::delete_obj(const DoutPrefixProvider* dpp, option parent_op.params.parts_accounted_size = params.parts_accounted_size; parent_op.params.null_verid = params.null_verid; - int ret = parent_op.delete_obj(y, dpp, flags & FLAG_LOG_OP); - if (ret < 0) + int ret = parent_op.delete_obj(y, dpp, flags & FLAG_LOG_OP, flags & FLAG_FORCE_OP); + if (ret < 0) { return ret; + } result.delete_marker = parent_op.result.delete_marker; result.version_id = parent_op.result.version_id; return ret; -} +} // RadosObject::RadosDeleteOp::delete_obj int RadosObject::delete_object(const DoutPrefixProvider* dpp, optional_yield y, @@ -2831,15 +2833,18 @@ int RadosObject::delete_object(const DoutPrefixProvider* dpp, RGWRados::Object::Delete del_op(&del_target); del_op.params.bucket_owner = bucket->get_info().owner; - del_op.params.versioning_status = (flags & FLAG_PREVENT_VERSIONING) - ? 0 : bucket->get_info().versioning_status(); + del_op.params.versioning_status = + (flags & FLAG_PREVENT_VERSIONING) + ? 0 + : bucket->get_info().versioning_status(); del_op.params.remove_objs = remove_objs; if (objv) { del_op.params.check_objv = objv->version_for_check(); } - return del_op.delete_obj(y, dpp, flags & FLAG_LOG_OP); -} + // convert flags to bool params + return del_op.delete_obj(y, dpp, flags & FLAG_LOG_OP, flags & FLAG_FORCE_OP); +} // RadosObject::delete_object int RadosObject::copy_object(const ACLOwner& owner, const rgw_user& remote_user, diff --git a/src/rgw/rgw_admin.cc b/src/rgw/rgw_admin.cc index 620f124ef43..34132342985 100644 --- a/src/rgw/rgw_admin.cc +++ b/src/rgw/rgw_admin.cc @@ -8049,9 +8049,10 @@ next: cerr << "ERROR: could not init bucket: " << cpp_strerror(-ret) << std::endl; return -ret; } + rgw_obj_key key(object, object_version); - ret = rgw_remove_object(dpp(), driver, bucket.get(), key, null_yield); + ret = rgw_remove_object(dpp(), driver, bucket.get(), key, null_yield, yes_i_really_mean_it); if (ret < 0) { cerr << "ERROR: object remove returned: " << cpp_strerror(-ret) << std::endl; return -ret; diff --git a/src/rgw/rgw_sal.h b/src/rgw/rgw_sal.h index 76759b2d53d..276b11af3db 100644 --- a/src/rgw/rgw_sal.h +++ b/src/rgw/rgw_sal.h @@ -194,6 +194,11 @@ enum AttrsMod { static constexpr uint32_t FLAG_LOG_OP = 0x0001; static constexpr uint32_t FLAG_PREVENT_VERSIONING = 0x0002; +// if cannot do all elements of op, do as much as possible (e.g., +// delete object where head object is missing) +static constexpr uint32_t FLAG_FORCE_OP = 0x0004; + + // a simple streaming data processing abstraction /** * @brief A simple streaming data processing abstraction