From 7925b82c20c94043dd5e4ca69d3ef55ea43db1e0 Mon Sep 17 00:00:00 2001 From: Ray Lv Date: Mon, 20 Oct 2014 18:57:46 +0800 Subject: [PATCH] rgw: Conditional PUT on ETag Fixes: #8562 Signed-off-by: Ray Lv --- src/rgw/rgw_cache.h | 9 ++-- src/rgw/rgw_op.cc | 10 ++-- src/rgw/rgw_op.h | 4 ++ src/rgw/rgw_rados.cc | 106 +++++++++++++++++++++++++++++++++++------ src/rgw/rgw_rados.h | 33 +++++++++---- src/rgw/rgw_rest_s3.cc | 3 ++ 6 files changed, 136 insertions(+), 29 deletions(-) diff --git a/src/rgw/rgw_cache.h b/src/rgw/rgw_cache.h index c0a0e243af76d..8b1bc93bfb029 100644 --- a/src/rgw/rgw_cache.h +++ b/src/rgw/rgw_cache.h @@ -215,7 +215,8 @@ public: map* rmattrs, const bufferlist *data, RGWObjManifest *manifest, const string *ptag, list *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::put_obj_meta_impl(void *ctx, rgw_obj& obj, uint64_t size, time_ map* rmattrs, const bufferlist *data, RGWObjManifest *manifest, const string *ptag, list *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::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) { diff --git a/src/rgw/rgw_op.cc b/src/rgw/rgw_op.cc index 4100d408675dd..11b5a76d9e120 100644 --- a/src/rgw/rgw_op.cc +++ b/src/rgw/rgw_op.cc @@ -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& attrs); + int do_complete(string& etag, time_t *mtime, time_t set_mtime, + map& 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& attrs) +int RGWPutObjProcessor_Multipart::do_complete(string& etag, time_t *mtime, time_t set_mtime, + map& 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, diff --git a/src/rgw/rgw_op.h b/src/rgw/rgw_op.h index d3b227e3ebaa1..519d3492fb2d8 100644 --- a/src/rgw/rgw_op.h +++ b/src/rgw/rgw_op.h @@ -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; diff --git a/src/rgw/rgw_rados.cc b/src/rgw/rgw_rados.cc index 2c53454389be0..715f377813388 100644 --- a/src/rgw/rgw_rados.cc +++ b/src/rgw/rgw_rados.cc @@ -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& attrs) +int RGWPutObjProcessor::complete(string& etag, time_t *mtime, time_t set_mtime, + map& 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& attrs) +int RGWPutObjProcessor_Plain::do_complete(string& etag, time_t *mtime, time_t set_mtime, + map& 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& attrs) { +int RGWPutObjProcessor_Atomic::do_complete(string& etag, time_t *mtime, time_t set_mtime, + map& 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; } diff --git a/src/rgw/rgw_rados.h b/src/rgw/rgw_rados.h index f38b3ac3df347..d397a1f15e662 100644 --- a/src/rgw/rgw_rados.h +++ b/src/rgw/rgw_rados.h @@ -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& attrs) = 0; + virtual int do_complete(string& etag, time_t *mtime, time_t set_mtime, + map& attrs, + const char *if_match = NULL, const char *if_nomatch = NULL) = 0; list 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& attrs); + virtual int complete(string& etag, time_t *mtime, time_t set_mtime, + map& 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& attrs); + int do_complete(string& etag, time_t *mtime, time_t set_mtime, + map& 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& attrs); + virtual int do_complete(string& etag, time_t *mtime, time_t set_mtime, + map& 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 *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 *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& 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, diff --git a/src/rgw/rgw_rest_s3.cc b/src/rgw/rgw_rest_s3.cc index 7668d111a1b60..c2efccb438380 100644 --- a/src/rgw/rgw_rest_s3.cc +++ b/src/rgw/rgw_rest_s3.cc @@ -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(); } -- 2.39.5