From 8654b1f202ad5719901dbcd8c64a384a75a08adc Mon Sep 17 00:00:00 2001 From: Matt Benjamin Date: Wed, 9 Jul 2025 11:11:45 -0400 Subject: [PATCH] rgw: hook up delete-marker detection Introduces is_delete_marker() getter in rgw::sal::Object interface, and implements it for rgw/sal/rados (and filter), replacing unimplemented Object::get_delete_marker(). (Looked into rgw/sal/posix, but delete marker handling seems not working as expected there, so deferring for now.) Next, the new interface is used in GET/HEAD request paths to return a correct AWS-specified x-amz-delete-marker header when required, c.f. https://docs.aws.amazon.com/AmazonS3/latest/userguide/DeleteMarker.html. Signed-off-by: Matt Benjamin --- src/rgw/driver/rados/rgw_rados.cc | 8 +++++--- src/rgw/driver/rados/rgw_sal_rados.cc | 3 +++ src/rgw/rgw_rest.cc | 15 ++++++++++++++- src/rgw/rgw_sal.h | 4 ++-- src/rgw/rgw_sal_filter.h | 2 +- src/rgw/rgw_sal_store.h | 4 ++-- 6 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/rgw/driver/rados/rgw_rados.cc b/src/rgw/driver/rados/rgw_rados.cc index 0546040027540..6213191600d03 100644 --- a/src/rgw/driver/rados/rgw_rados.cc +++ b/src/rgw/driver/rados/rgw_rados.cc @@ -6863,12 +6863,12 @@ int RGWRados::get_obj_state(const DoutPrefixProvider *dpp, RGWObjectCtx *rctx, RGWObjStateManifest* sm = nullptr; int r = get_obj_state(dpp, rctx, bucket_info, obj, &sm, follow_olh, y, assume_noent); - if (r < 0) { - return r; - } if (pstate) { *pstate = &sm->state; } + if (r < 0) { + return r; + } if (pmanifest) { if (sm->manifest) { *pmanifest = &(*sm->manifest); @@ -9474,6 +9474,8 @@ int RGWRados::follow_olh(const DoutPrefixProvider *dpp, RGWBucketInfo& bucket_in } if (olh.removed) { + /* the object is a delete marker */ + state->is_dm = true; return -ENOENT; } diff --git a/src/rgw/driver/rados/rgw_sal_rados.cc b/src/rgw/driver/rados/rgw_sal_rados.cc index 2bf0bdc972662..41a9334d2137b 100644 --- a/src/rgw/driver/rados/rgw_sal_rados.cc +++ b/src/rgw/driver/rados/rgw_sal_rados.cc @@ -2773,6 +2773,9 @@ int RadosObject::load_obj_state(const DoutPrefixProvider* dpp, optional_yield y, int ret = store->getRados()->get_obj_state(dpp, rados_ctx, bucket->get_info(), get_obj(), &pstate, &manifest, follow_olh, y); if (ret < 0) { + if (ret == -ENOENT) { + state.is_dm = pstate->is_dm; + } return ret; } diff --git a/src/rgw/rgw_rest.cc b/src/rgw/rgw_rest.cc index 0592d66e3c150..4c1c70d9e017b 100644 --- a/src/rgw/rgw_rest.cc +++ b/src/rgw/rgw_rest.cc @@ -1931,7 +1931,20 @@ int RGWHandler_REST::read_permissions(RGWOp* op_obj, optional_yield y) return -EINVAL; } - return do_read_permissions(op_obj, only_bucket, y); + auto ret = do_read_permissions(op_obj, only_bucket, y); + switch (s->op) { + case OP_HEAD: + case OP_GET: + if (ret == -ENOENT /* note, access already accounted for */) [[unlikely]] { + (void) s->object->load_obj_state(s, s->yield, true /* follow_olh */); + auto tf = s->object->is_delete_marker() ? "true" : "false"; + dump_header(s, "x-amz-delete-marker", tf); + } + default: + break; + } + + return ret; } void RGWRESTMgr::register_resource(string resource, RGWRESTMgr *mgr) diff --git a/src/rgw/rgw_sal.h b/src/rgw/rgw_sal.h index 075f54d38fdbc..cb35e3a60b6e3 100644 --- a/src/rgw/rgw_sal.h +++ b/src/rgw/rgw_sal.h @@ -1214,6 +1214,8 @@ class Object { virtual void set_compressed() = 0; /** Check if this object is compressed */ virtual bool is_compressed() = 0; + /** True if this object is a delete marker (newest version is deleted) */ + virtual bool is_delete_marker() = 0; /** Check if object is synced */ virtual bool is_sync_completed(const DoutPrefixProvider* dpp, optional_yield y, @@ -1339,8 +1341,6 @@ class Object { virtual void set_hash_source(std::string s) = 0; /** Build an Object Identifier string for this object */ virtual std::string get_oid(void) const = 0; - /** True if this object is a delete marker (newest version is deleted) */ - virtual bool get_delete_marker(void) = 0; /** True if this object is stored in the extra data pool */ virtual bool get_in_extra_data(void) = 0; /** True if this object exists in the store */ diff --git a/src/rgw/rgw_sal_filter.h b/src/rgw/rgw_sal_filter.h index 7627921633c5b..9dadd949cf013 100644 --- a/src/rgw/rgw_sal_filter.h +++ b/src/rgw/rgw_sal_filter.h @@ -787,6 +787,7 @@ public: virtual bool is_prefetch_data() override { return next->is_prefetch_data(); } virtual void set_compressed() override { return next->set_compressed(); } virtual bool is_compressed() override { return next->is_compressed(); } + virtual bool is_delete_marker() override { return next->is_delete_marker(); } bool is_sync_completed(const DoutPrefixProvider* dpp, optional_yield y, const ceph::real_time& obj_mtime) override { return next->is_sync_completed(dpp, y, obj_mtime); @@ -865,7 +866,6 @@ public: virtual std::string get_hash_source(void) override { return next->get_hash_source(); }; virtual void set_hash_source(std::string s) override { return next->set_hash_source(s); }; virtual std::string get_oid(void) const override { return next->get_oid(); }; - virtual bool get_delete_marker(void) override { return next->get_delete_marker(); }; virtual bool get_in_extra_data(void) override { return next->get_in_extra_data(); }; virtual bool exists(void) override { return next->exists(); }; virtual void set_in_extra_data(bool i) override { return next->set_in_extra_data(i); }; diff --git a/src/rgw/rgw_sal_store.h b/src/rgw/rgw_sal_store.h index da98eb41f997b..c50452e0ada39 100644 --- a/src/rgw/rgw_sal_store.h +++ b/src/rgw/rgw_sal_store.h @@ -25,6 +25,7 @@ struct RGWObjState { bool is_atomic{false}; bool has_attrs{false}; bool exists{false}; + bool is_dm{false}; uint64_t size{0}; //< size of raw object uint64_t accounted_size{0}; //< size before compression, encryption ceph::real_time mtime; @@ -279,7 +280,6 @@ class StoreObject : public Object { protected: RGWObjState state; Bucket* bucket = nullptr; - bool delete_marker{false}; jspan_context trace_ctx{false, false}; public: @@ -299,6 +299,7 @@ class StoreObject : public Object { virtual bool is_prefetch_data() override { return state.prefetch_data; } virtual void set_compressed() override { state.compressed = true; } virtual bool is_compressed() override { return state.compressed; } + virtual bool is_delete_marker() override { return state.is_dm; } virtual void invalidate() override { rgw_obj obj = state.obj; bool is_atomic = state.is_atomic; @@ -342,7 +343,6 @@ class StoreObject : public Object { virtual std::string get_hash_source(void) override { return state.obj.index_hash_source; } virtual void set_hash_source(std::string s) override { state.obj.index_hash_source = s; } virtual std::string get_oid(void) const override { return state.obj.key.get_oid(); } - virtual bool get_delete_marker(void) override { return delete_marker; } virtual bool get_in_extra_data(void) override { return state.obj.is_in_extra_data(); } virtual bool exists(void) override { return state.exists; } virtual void set_in_extra_data(bool i) override { state.obj.set_in_extra_data(i); } -- 2.39.5