]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
rgw: Conditional PUT on ETag 3160/head
authorRay Lv <xiangyulv@gmail.com>
Mon, 20 Oct 2014 10:57:46 +0000 (18:57 +0800)
committerRay Lv <raylv@yahoo-inc.com>
Fri, 12 Dec 2014 08:31:16 +0000 (00:31 -0800)
Fixes: #8562
Signed-off-by: Ray Lv <raylv@yahoo-inc.com>
src/rgw/rgw_cache.h
src/rgw/rgw_op.cc
src/rgw/rgw_op.h
src/rgw/rgw_rados.cc
src/rgw/rgw_rados.h
src/rgw/rgw_rest_s3.cc

index c0a0e243af76d9ffe73e55bc55d0833829efaf2c..8b1bc93bfb029e013d4ba9a401878798372b8464 100644 (file)
@@ -215,7 +215,8 @@ public:
                    map<std::string, bufferlist>* rmattrs, const bufferlist *data,
                    RGWObjManifest *manifest, const string *ptag, list<string> *remove_objs,
                    bool modify_version, RGWObjVersionTracker *objv_tracker, time_t set_mtime,
-                   const string& owner);
+                   const string& owner,
+                   const char *if_match = NULL, const char *if_nomatch = NULL);
   int put_obj_data(void *ctx, rgw_obj& obj, const char *data,
               off_t ofs, size_t len, bool exclusive);
 
@@ -402,7 +403,8 @@ int RGWCache<T>::put_obj_meta_impl(void *ctx, rgw_obj& obj, uint64_t size, time_
                               map<std::string, bufferlist>* rmattrs, const bufferlist *data,
                               RGWObjManifest *manifest, const string *ptag, list<string> *remove_objs,
                               bool modify_version, RGWObjVersionTracker *objv_tracker, time_t set_mtime,
-                              const string& owner)
+                              const string& owner,
+                              const char *if_match, const char *if_nomatch)
 {
   rgw_bucket bucket;
   string oid;
@@ -424,7 +426,8 @@ int RGWCache<T>::put_obj_meta_impl(void *ctx, rgw_obj& obj, uint64_t size, time_
     }
   }
   int ret = T::put_obj_meta_impl(ctx, obj, size, mtime, attrs, category, flags, rmattrs, data, manifest, ptag, remove_objs,
-                                 modify_version, objv_tracker, set_mtime, owner);
+                                 modify_version, objv_tracker, set_mtime, owner,
+                                 if_match, if_nomatch);
   if (cacheable) {
     string name = normal_name(bucket, oid);
     if (ret >= 0) {
index 4100d408675dd0edfd08c16b79dd2784b62071cd..11b5a76d9e120b9801b6e35a704ce9fe08270beb 100644 (file)
@@ -1383,7 +1383,9 @@ class RGWPutObjProcessor_Multipart : public RGWPutObjProcessor_Atomic
 
 protected:
   int prepare(RGWRados *store, void *obj_ctx, string *oid_rand);
-  int do_complete(string& etag, time_t *mtime, time_t set_mtime, map<string, bufferlist>& attrs);
+  int do_complete(string& etag, time_t *mtime, time_t set_mtime,
+                  map<string, bufferlist>& attrs,
+                  const char *if_match = NULL, const char *if_nomatch = NULL);
 
 public:
   bool immutable_head() { return true; }
@@ -1454,7 +1456,9 @@ static bool is_v2_upload_id(const string& upload_id)
   return (strncmp(uid, MULTIPART_UPLOAD_ID_PREFIX, sizeof(MULTIPART_UPLOAD_ID_PREFIX) - 1) == 0);
 }
 
-int RGWPutObjProcessor_Multipart::do_complete(string& etag, time_t *mtime, time_t set_mtime, map<string, bufferlist>& attrs)
+int RGWPutObjProcessor_Multipart::do_complete(string& etag, time_t *mtime, time_t set_mtime,
+                                              map<string, bufferlist>& attrs,
+                                              const char *if_match, const char *if_nomatch)
 {
   complete_writing_data();
 
@@ -1744,7 +1748,7 @@ void RGWPutObj::execute()
 
   rgw_get_request_metadata(s->cct, s->info, attrs);
 
-  ret = processor->complete(etag, &mtime, 0, attrs);
+  ret = processor->complete(etag, &mtime, 0, attrs, if_match, if_nomatch);
 done:
   dispose_processor(processor);
   perfcounter->tinc(l_rgw_put_lat,
index d3b227e3ebaa1f3bd3f663fbec4ec6edd10c8431..519d3492fb2d898ab9e4430795bf9f965fcf0ce1 100644 (file)
@@ -330,6 +330,8 @@ protected:
   off_t ofs;
   const char *supplied_md5_b64;
   const char *supplied_etag;
+  const char *if_match;
+  const char *if_nomatch;
   string etag;
   bool chunked_upload;
   RGWAccessControlPolicy policy;
@@ -344,6 +346,8 @@ public:
     ofs = 0;
     supplied_md5_b64 = NULL;
     supplied_etag = NULL;
+    if_match = NULL;
+    if_nomatch = NULL;
     chunked_upload = false;
     obj_manifest = NULL;
     mtime = 0;
index 2c53454389be01178e5b1783eef493fd4757bfe6..715f3778133885022d3e538d44f5eaa716b56014 100644 (file)
@@ -866,9 +866,11 @@ void RGWObjVersionTracker::generate_new_write_ver(CephContext *cct)
   append_rand_alpha(cct, write_version.tag, write_version.tag, TAG_LEN);
 }
 
-int RGWPutObjProcessor::complete(string& etag, time_t *mtime, time_t set_mtime, map<string, bufferlist>& attrs)
+int RGWPutObjProcessor::complete(string& etag, time_t *mtime, time_t set_mtime,
+                                 map<string, bufferlist>& attrs,
+                                 const char *if_match, const char * if_nomatch)
 {
-  int r = do_complete(etag, mtime, set_mtime, attrs);
+  int r = do_complete(etag, mtime, set_mtime, attrs, if_match, if_nomatch);
   if (r < 0)
     return r;
 
@@ -920,7 +922,9 @@ int RGWPutObjProcessor_Plain::handle_data(bufferlist& bl, off_t _ofs, MD5 *hash,
   return 0;
 }
 
-int RGWPutObjProcessor_Plain::do_complete(string& etag, time_t *mtime, time_t set_mtime, map<string, bufferlist>& attrs)
+int RGWPutObjProcessor_Plain::do_complete(string& etag, time_t *mtime, time_t set_mtime,
+                                          map<string, bufferlist>& attrs,
+                                          const char *if_match, const char *if_nomatch)
 {
   RGWRados::PutObjMetaExtraParams params;
   params.set_mtime = set_mtime;
@@ -1184,7 +1188,10 @@ int RGWPutObjProcessor_Atomic::complete_writing_data()
   return 0;
 }
 
-int RGWPutObjProcessor_Atomic::do_complete(string& etag, time_t *mtime, time_t set_mtime, map<string, bufferlist>& attrs) {
+int RGWPutObjProcessor_Atomic::do_complete(string& etag, time_t *mtime, time_t set_mtime,
+                                           map<string, bufferlist>& attrs,
+                                           const char *if_match,
+                                           const char *if_nomatch) {
   int r = complete_writing_data();
   if (r < 0)
     return r;
@@ -1196,6 +1203,8 @@ int RGWPutObjProcessor_Atomic::do_complete(string& etag, time_t *mtime, time_t s
   extra_params.data = &first_chunk;
   extra_params.manifest = &manifest;
   extra_params.ptag = &unique_tag; /* use req_id as operation tag */
+  extra_params.if_match = if_match;
+  extra_params.if_nomatch = if_nomatch;
   extra_params.mtime = mtime;
   extra_params.set_mtime = set_mtime;
   extra_params.owner = bucket_owner;
@@ -2814,7 +2823,8 @@ int RGWRados::put_obj_meta_impl(void *ctx, rgw_obj& obj,  uint64_t size,
                   bool modify_version,
                   RGWObjVersionTracker *objv_tracker,
                   time_t set_mtime,
-                  const string& bucket_owner)
+                  const string& bucket_owner,
+                  const char *if_match, const char *if_nomatch)
 {
   rgw_bucket bucket;
   rgw_rados_ref ref;
@@ -2834,7 +2844,8 @@ int RGWRados::put_obj_meta_impl(void *ctx, rgw_obj& obj,  uint64_t size,
     op.create(true); // exclusive create
   } else {
     bool reset_obj = (flags & PUT_OBJ_CREATE) != 0;
-    r = prepare_atomic_for_write(rctx, obj, op, &state, reset_obj, ptag);
+    r = prepare_atomic_for_write(rctx, obj, op, &state, reset_obj, ptag,
+                                 if_match, if_nomatch);
     if (r < 0)
       return r;
   }
@@ -2960,9 +2971,33 @@ done_cancel:
    * - object was removed (ENOENT)
    * should treat it as a success
    */
-  if ((r == -ECANCELED || r == -ENOENT) ||
-      (!(flags & PUT_OBJ_EXCL) && r == -EEXIST)) {
-    r = 0;
+  if (if_match == NULL && if_nomatch == NULL) {
+    if ((r == -ECANCELED || r == -ENOENT) ||
+        (!(flags & PUT_OBJ_EXCL) && r == -EEXIST)) {
+      r = 0;
+    }
+  } else {
+    if (if_match != NULL) {
+      // only overwrite existing object
+      if (strcmp(if_match, "*") == 0) {
+        if (r == -ENOENT) {
+          r = -ERR_PRECONDITION_FAILED;
+        } else if (r == -ECANCELED) {
+          r = 0;
+        }
+      }
+    }
+
+    if (if_nomatch != NULL) {
+      // only create a new object
+      if (strcmp(if_nomatch, "*") == 0) {
+        if (r == -EEXIST) {
+          r = -ERR_PRECONDITION_FAILED;
+        } else if (r == -ENOENT) {
+          r = 0;
+        }
+      }
+    }
   }
 
   return r;
@@ -4079,7 +4114,8 @@ int RGWRados::append_atomic_test(RGWRadosCtx *rctx, rgw_obj& obj,
 
 int RGWRados::prepare_atomic_for_write_impl(RGWRadosCtx *rctx, rgw_obj& obj,
                             ObjectWriteOperation& op, RGWObjState **pstate,
-                           bool reset_obj, const string *ptag)
+                           bool reset_obj, const string *ptag,
+                            const char *if_match, const char *if_nomatch)
 {
   int r = get_obj_state(rctx, obj, pstate, NULL);
   if (r < 0)
@@ -4087,7 +4123,9 @@ int RGWRados::prepare_atomic_for_write_impl(RGWRadosCtx *rctx, rgw_obj& obj,
 
   RGWObjState *state = *pstate;
 
-  bool need_guard = (state->has_manifest || (state->obj_tag.length() != 0)) && (!state->fake_tag);
+  bool need_guard = (state->has_manifest || (state->obj_tag.length() != 0) ||
+                     if_match != NULL || if_nomatch != NULL) &&
+                     (!state->fake_tag);
 
   if (!state->is_atomic) {
     ldout(cct, 20) << "prepare_atomic_for_write_impl: state is not atomic. state=" << (void *)state << dendl;
@@ -4102,8 +4140,44 @@ int RGWRados::prepare_atomic_for_write_impl(RGWRadosCtx *rctx, rgw_obj& obj,
 
   if (need_guard) {
     /* first verify that the object wasn't replaced under */
-    op.cmpxattr(RGW_ATTR_ID_TAG, LIBRADOS_CMPXATTR_OP_EQ, state->obj_tag);
-    // FIXME: need to add FAIL_NOTEXIST_OK for racing deletion
+    if (if_nomatch == NULL || strcmp(if_nomatch, "*") != 0) {
+      op.cmpxattr(RGW_ATTR_ID_TAG, LIBRADOS_CMPXATTR_OP_EQ, state->obj_tag); 
+      // FIXME: need to add FAIL_NOTEXIST_OK for racing deletion
+    }
+
+    if (if_match) {
+      if (strcmp(if_match, "*") == 0) {
+        // test the object is existing
+        if (!state->exists) {
+          r = -ERR_PRECONDITION_FAILED;
+          return r;
+        }
+      } else {
+        bufferlist bl;
+        if (!state->get_attr(RGW_ATTR_ETAG, bl) ||
+            strncmp(if_match, bl.c_str(), bl.length()) != 0) {
+          r = -ERR_PRECONDITION_FAILED;
+          return r;
+        }
+      }
+    }
+
+    if (if_nomatch) {
+      if (strcmp(if_nomatch, "*") == 0) {
+        // test the object is NOT existing
+        if (state->exists) {
+          r = -ERR_PRECONDITION_FAILED;
+          return r;
+        }
+      } else {
+        bufferlist bl;
+        if (!state->get_attr(RGW_ATTR_ETAG, bl) ||
+            strncmp(if_nomatch, bl.c_str(), bl.length()) == 0) {
+          r = -ERR_PRECONDITION_FAILED;
+          return r;
+        }
+      }
+    }
   }
 
   if (reset_obj) {
@@ -4132,7 +4206,8 @@ int RGWRados::prepare_atomic_for_write_impl(RGWRadosCtx *rctx, rgw_obj& obj,
 
 int RGWRados::prepare_atomic_for_write(RGWRadosCtx *rctx, rgw_obj& obj,
                             ObjectWriteOperation& op, RGWObjState **pstate,
-                           bool reset_obj, const string *ptag)
+                           bool reset_obj, const string *ptag,
+                            const char *if_match, const char *if_nomatch)
 {
   if (!rctx) {
     *pstate = NULL;
@@ -4140,7 +4215,8 @@ int RGWRados::prepare_atomic_for_write(RGWRadosCtx *rctx, rgw_obj& obj,
   }
 
   int r;
-  r = prepare_atomic_for_write_impl(rctx, obj, op, pstate, reset_obj, ptag);
+  r = prepare_atomic_for_write_impl(rctx, obj, op, pstate, reset_obj, ptag,
+                                    if_match, if_nomatch);
 
   return r;
 }
index f38b3ac3df347b54ef2d60ecce220d05e207cb8e..d397a1f15e66223a9c886701e54584ae4508f519 100644 (file)
@@ -550,7 +550,9 @@ protected:
   bool is_complete;
   string bucket_owner;
 
-  virtual int do_complete(string& etag, time_t *mtime, time_t set_mtime, map<string, bufferlist>& attrs) = 0;
+  virtual int do_complete(string& etag, time_t *mtime, time_t set_mtime,
+                          map<string, bufferlist>& attrs,
+                          const char *if_match = NULL, const char *if_nomatch = NULL) = 0;
 
   list<rgw_obj> objs;
 
@@ -570,7 +572,9 @@ public:
   virtual void complete_hash(MD5 *hash) {
     assert(0);
   }
-  virtual int complete(string& etag, time_t *mtime, time_t set_mtime, map<string, bufferlist>& attrs);
+  virtual int complete(string& etag, time_t *mtime, time_t set_mtime,
+                       map<string, bufferlist>& attrs,
+                       const char *if_match = NULL, const char *if_nomatch = NULL);
 
   CephContext *ctx();
 };
@@ -587,7 +591,9 @@ class RGWPutObjProcessor_Plain : public RGWPutObjProcessor
 protected:
   int prepare(RGWRados *store, void *obj_ctx, string *oid_rand);
   int handle_data(bufferlist& bl, off_t ofs, MD5 *hash /* NULL expected */, void **phandle, bool *again);
-  int do_complete(string& etag, time_t *mtime, time_t set_mtime, map<string, bufferlist>& attrs);
+  int do_complete(string& etag, time_t *mtime, time_t set_mtime,
+                  map<string, bufferlist>& attrs,
+                  const char *if_match = NULL, const char *if_nomatch = NULL);
 
 public:
   int throttle_data(void *handle, bool need_to_wait) { return 0; }
@@ -649,7 +655,9 @@ protected:
   RGWObjManifest::generator manifest_gen;
 
   int write_data(bufferlist& bl, off_t ofs, void **phandle, bool exclusive);
-  virtual int do_complete(string& etag, time_t *mtime, time_t set_mtime, map<string, bufferlist>& attrs);
+  virtual int do_complete(string& etag, time_t *mtime, time_t set_mtime,
+                          map<string, bufferlist>& attrs,
+                          const char *if_match = NULL, const char *if_nomatch = NULL);
 
   int prepare_next_part(off_t ofs);
   int complete_parts();
@@ -1294,10 +1302,14 @@ class RGWRados
                          librados::ObjectOperation& op, RGWObjState **state);
   int prepare_atomic_for_write_impl(RGWRadosCtx *rctx, rgw_obj& obj,
                          librados::ObjectWriteOperation& op, RGWObjState **pstate,
-                        bool reset_obj, const string *ptag);
+                        bool reset_obj, const string *ptag,
+                         const char *if_match = NULL,
+                         const char *if_nomatch = NULL);
   int prepare_atomic_for_write(RGWRadosCtx *rctx, rgw_obj& obj,
                          librados::ObjectWriteOperation& op, RGWObjState **pstate,
-                        bool reset_obj, const string *ptag);
+                        bool reset_obj, const string *ptag,
+                         const char *if_match = NULL,
+                         const char *if_nomatch = NULL);
 
   void atomic_write_finish(RGWObjState *state, int r) {
     if (state && r == -ECANCELED) {
@@ -1494,6 +1506,8 @@ public:
     const bufferlist *data;
     RGWObjManifest *manifest;
     const string *ptag;
+    const char *if_match;
+    const char *if_nomatch;
     list<string> *remove_objs;
     bool modify_version;
     RGWObjVersionTracker *objv_tracker;
@@ -1502,6 +1516,7 @@ public:
 
     PutObjMetaExtraParams() : mtime(NULL), rmattrs(NULL),
                      data(NULL), manifest(NULL), ptag(NULL),
+                     if_match(NULL), if_nomatch(NULL),
                      remove_objs(NULL), modify_version(false),
                      objv_tracker(NULL), set_mtime(0) {}
   };
@@ -1513,7 +1528,8 @@ public:
               RGWObjManifest *manifest, const string *ptag, list<string> *remove_objs,
               bool modify_version, RGWObjVersionTracker *objv_tracker,
               time_t set_mtime /* 0 for don't set */,
-              const string& owner);
+              const string& owner,
+              const char *if_match = NULL, const char *if_nomatch = NULL);
 
   virtual int put_obj_meta(void *ctx, rgw_obj& obj, uint64_t size, time_t *mtime,
               map<std::string, bufferlist>& attrs, RGWObjCategory category, int flags,
@@ -1527,7 +1543,8 @@ public:
                            RGWObjCategory category, int flags, PutObjMetaExtraParams& params) {
     return put_obj_meta_impl(ctx, obj, size, params.mtime, attrs, category, flags,
                         params.rmattrs, params.data, params.manifest, params.ptag, params.remove_objs,
-                        params.modify_version, params.objv_tracker, params.set_mtime, params.owner);
+                        params.modify_version, params.objv_tracker, params.set_mtime, params.owner,
+                        params.if_match, params.if_nomatch);
   }
 
   virtual int put_obj_data(void *ctx, rgw_obj& obj, const char *data,
index 7668d111a1b6068ca9b1314645c24c07f1951bd7..c2efccb438380847252f6f13e7813f8014f14aae 100644 (file)
@@ -513,6 +513,9 @@ int RGWPutObj_ObjStore_S3::get_params()
 
   policy = s3policy;
 
+  if_match = s->info.env->get("HTTP_IF_MATCH");
+  if_nomatch = s->info.env->get("HTTP_IF_NONE_MATCH");
+
   return RGWPutObj_ObjStore::get_params();
 }