]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
rgw: add force option to `radosgw-admin object rm ...`
authorJ. Eric Ivancich <ivancich@redhat.com>
Mon, 24 Mar 2025 23:45:06 +0000 (19:45 -0400)
committerJ. Eric Ivancich <ivancich@redhat.com>
Wed, 9 Apr 2025 16:44:09 +0000 (12:44 -0400)
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 <ivancich@redhat.com>
(cherry picked from commit 7e1ac21069e14a4d6c4d4748971943b7c8b8137c)

src/rgw/driver/rados/rgw_bucket.cc
src/rgw/driver/rados/rgw_bucket.h
src/rgw/driver/rados/rgw_rados.cc
src/rgw/driver/rados/rgw_rados.h
src/rgw/driver/rados/rgw_sal_rados.cc
src/rgw/rgw_admin.cc
src/rgw/rgw_sal.h

index f08df5e62a6bb478a73f6284bbb9a4d2be5a5f7b..a884cefe442cf74ac30fafd6cc96884354c792bf 100644 (file)
@@ -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<rgw::sal::Object> 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)
index c7154cee6ddaf68d8db05cec67491cd6cfbef143..edbb5856aeaa256c28d8d1b3bd76a948892a4b2e 100644 (file)
@@ -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,
index a1370b079dc6b81c058dca98c7e2a3510d63e1a5..12d04529b84b2e2baa1d754001c54c754777cb2d 100644 (file)
@@ -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<uint64_t, std::vector<rgw_bucket_olh_log_entry> >& 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<uint64_t, vector<rgw_bucket_olh_log_entry> > 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;
   }
index f28b467fb9d4d7637981c543ce25dd333187615b..c1d1cefc20e66564dfd668e0eece0b6011ddb84e 100644 (file)
@@ -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<uint64_t, std::vector<rgw_bucket_olh_log_entry> > *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<uint64_t, std::vector<rgw_bucket_olh_log_entry> >& 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<std::string, bufferlist>& pending_entries, std::map<std::string, bufferlist> *rm_pending_entries);
   int remove_olh_pending_entries(const DoutPrefixProvider *dpp, const RGWBucketInfo& bucket_info, RGWObjState& state, const rgw_obj& olh_obj, std::map<std::string, bufferlist>& pending_attrs, optional_yield y);
index bcb1e811402833ca1fc4bdf36266266d93105635..ad9ce6edcd031702b50a040d6731fbf2950cc881 100644 (file)
@@ -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,
index 620f124ef43b7a2f1f208b6b3eac600cf1e573e8..34132342985aaa5193c7879d093660bb87ce7bc3 100644 (file)
@@ -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;
index 76759b2d53d948d0c7849156da26d30c105b883a..276b11af3dbcf759b1cade58f8cfaf0313d8ed83 100644 (file)
@@ -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