]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
rgw: hook up delete-marker detection 64272/head
authorMatt Benjamin <mbenjamin@redhat.com>
Wed, 9 Jul 2025 15:11:45 +0000 (11:11 -0400)
committerMatt Benjamin <mbenjamin@redhat.com>
Wed, 9 Jul 2025 15:11:45 +0000 (11:11 -0400)
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 <mbenjamin@redhat.com>
src/rgw/driver/rados/rgw_rados.cc
src/rgw/driver/rados/rgw_sal_rados.cc
src/rgw/rgw_rest.cc
src/rgw/rgw_sal.h
src/rgw/rgw_sal_filter.h
src/rgw/rgw_sal_store.h

index 054604002754084240ba9e91ac9d8bf137cedbb1..6213191600d03c261fbc7ccee8dfc1443b197613 100644 (file)
@@ -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;
   }
 
index 2bf0bdc972662368e625c0cba1753514facafc58..41a9334d2137b6b655f928ec7471afcd7d83ff0c 100644 (file)
@@ -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;
   }
 
index 0592d66e3c1504ca48a81ddceae420726172c8cd..4c1c70d9e017bc8e955ddbe99f2d659e8837b354 100644 (file)
@@ -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)
index 075f54d38fdbc2577ab62577d745d509754b27f2..cb35e3a60b6e38158994536b6dbb788260d94bdb 100644 (file)
@@ -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 */
index 7627921633c5b11223a18338cd2cfd25a7f1618f..9dadd949cf013412c898304cc62bf26e3da66621 100644 (file)
@@ -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); };
index da98eb41f997becd97a4979f3c534750e5085bab..c50452e0ada396b45d55dc865a60a8e92c458d87 100644 (file)
@@ -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); }