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);
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;
}
}
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) {
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; }
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();
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,
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;
ofs = 0;
supplied_md5_b64 = NULL;
supplied_etag = NULL;
+ if_match = NULL;
+ if_nomatch = NULL;
chunked_upload = false;
obj_manifest = NULL;
mtime = 0;
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;
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;
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;
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;
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;
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;
}
* - 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;
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)
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;
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) {
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;
}
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;
}
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;
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();
};
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; }
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();
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) {
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;
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) {}
};
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,
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,
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();
}