Skip to content

Commit 7850229

Browse files
authored
Merge pull request ceph#63348 from AliMasarweh/wip-alimasa-68183
RGW | fix conditional Delete, MultiDelete and Put Reviewed-by: Casey Bodley <[email protected]>, Matt Benjamin <[email protected]>
2 parents 095c1f1 + b99a47f commit 7850229

File tree

11 files changed

+305
-91
lines changed

11 files changed

+305
-91
lines changed

src/rgw/driver/dbstore/common/dbstore.h

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1829,8 +1829,8 @@ class DB {
18291829
bool high_precision_time;
18301830
uint32_t mod_zone_id;
18311831
uint64_t mod_pg_ver;
1832-
const char *if_match;
1833-
const char *if_nomatch;
1832+
const char *if_match{nullptr};
1833+
const char *if_nomatch{nullptr};
18341834

18351835
ConditionParams() :
18361836
mod_ptr(NULL), unmod_ptr(NULL), high_precision_time(false), mod_zone_id(0), mod_pg_ver(0),
@@ -1872,8 +1872,8 @@ class DB {
18721872
rgw_user owner;
18731873
RGWObjCategory category;
18741874
int flags;
1875-
const char *if_match;
1876-
const char *if_nomatch;
1875+
const char *if_match{nullptr};
1876+
const char *if_nomatch{nullptr};
18771877
std::optional<uint64_t> olh_epoch;
18781878
ceph::real_time delete_at;
18791879
bool canceled;
@@ -1915,7 +1915,11 @@ class DB {
19151915
std::list<rgw_obj_index_key> *remove_objs;
19161916
ceph::real_time expiration_time;
19171917
ceph::real_time unmod_since;
1918+
ceph::real_time last_mod_time_match;
19181919
ceph::real_time mtime; /* for setting delete marker mtime */
1920+
std::optional<uint64_t> size_match;
1921+
const char *if_match{nullptr};
1922+
const char *if_nomatch{nullptr};
19191923
bool high_precision_time;
19201924
rgw_zone_set *zones_trace;
19211925
bool abortmp;

src/rgw/driver/rados/rgw_rados.cc

Lines changed: 180 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -3262,7 +3262,27 @@ int RGWRados::Object::Write::_do_write_meta(uint64_t size, uint64_t accounted_si
32623262
if (!ptag && !index_op->get_optag()->empty()) {
32633263
ptag = index_op->get_optag();
32643264
}
3265-
r = target->prepare_atomic_modification(rctx.dpp, op, reset_obj, ptag, meta.if_match, meta.if_nomatch, false, meta.modify_tail, rctx.y);
3265+
3266+
r = target->get_state(rctx.dpp, &target->state, &target->manifest, false, rctx.y);
3267+
if (r < 0)
3268+
return r;
3269+
RGWObjState* current_state = target->state;
3270+
if (!target->obj.key.instance.empty()) {
3271+
r = target->get_current_version_state(rctx.dpp, current_state, rctx.y);
3272+
if (r == -ENOENT) {
3273+
current_state = target->state;
3274+
} else if (r < 0) {
3275+
return r;
3276+
}
3277+
}
3278+
3279+
r = target->check_preconditions(rctx.dpp, std::nullopt, real_clock::zero(), false, meta.if_match, meta.if_nomatch, *current_state, rctx.y);
3280+
if (r < 0) {
3281+
return r;
3282+
}
3283+
bool guard = ((target->manifest) || (target->state->obj_tag.length() != 0)) && (!target->state->fake_tag);
3284+
bool set_attr_id_tag = guard && target->obj.key.instance.empty() && (meta.if_nomatch == nullptr || meta.if_nomatch != "*"sv);
3285+
r = target->prepare_atomic_modification(rctx.dpp, op, reset_obj, ptag, meta.modify_tail, set_attr_id_tag, rctx.y);
32663286
if (r < 0)
32673287
return r;
32683288

@@ -6404,7 +6424,24 @@ int RGWRados::Object::Delete::delete_obj(optional_yield y,
64046424
meta.mtime = params.mtime;
64056425
}
64066426

6407-
int r = store->set_olh(dpp, target->get_ctx(), target->get_bucket_info(), marker, true,
6427+
int r = target->get_state(dpp, &target->state, &target->manifest, false, y);
6428+
if (r < 0)
6429+
return r;
6430+
RGWObjState* current_state = target->state;
6431+
r = target->get_current_version_state(dpp, current_state, y);
6432+
if (r == -ENOENT) {
6433+
current_state = target->state;
6434+
} else if (r < 0) {
6435+
return r;
6436+
}
6437+
6438+
r = target->check_preconditions(dpp, params.size_match, params.last_mod_time_match, params.high_precision_time,
6439+
params.if_match, nullptr, *current_state, y);
6440+
if (r < 0) {
6441+
return r;
6442+
}
6443+
6444+
r = store->set_olh(dpp, target->get_ctx(), target->get_bucket_info(), marker, true,
64086445
&meta, params.olh_epoch, params.unmod_since, params.high_precision_time,
64096446
y, params.zones_trace, add_log);
64106447
if (r < 0) {
@@ -6417,6 +6454,38 @@ int RGWRados::Object::Delete::delete_obj(optional_yield y,
64176454
if (r < 0) {
64186455
return r;
64196456
}
6457+
6458+
using namespace std::string_literals;
6459+
if (params.if_match && params.if_match != "*"sv) {
6460+
string if_match = rgw_string_unquote(params.if_match);
6461+
ldpp_dout(dpp, 10) << "checking precondtion if_match: " << if_match << ", etag: " << dirent.meta.etag << dendl;
6462+
if(dirent.meta.etag != if_match) {
6463+
return -ERR_PRECONDITION_FAILED;
6464+
}
6465+
}
6466+
6467+
if (params.size_match.has_value()) {
6468+
ldpp_dout(dpp, 10) << "checking precondtion size_match: " << params.size_match << ", size: " << dirent.meta.size << dendl;
6469+
if (params.size_match != dirent.meta.size) {
6470+
return -ERR_PRECONDITION_FAILED;
6471+
}
6472+
}
6473+
6474+
if (!real_clock::is_zero(params.last_mod_time_match)) {
6475+
struct timespec ctime = ceph::real_clock::to_timespec(dirent.meta.mtime);
6476+
struct timespec last_mod_time = ceph::real_clock::to_timespec(params.last_mod_time_match);
6477+
if (!params.high_precision_time) {
6478+
ctime.tv_nsec = 0;
6479+
last_mod_time.tv_nsec = 0;
6480+
}
6481+
6482+
ldpp_dout(dpp, 10) << "checking precondtion If-Match-Last-Modified-Time: " << params.last_mod_time_match
6483+
<< ", Last-Modified: " << ctime << ", with high_precision_time:" << params.high_precision_time << dendl;
6484+
if (ctime != last_mod_time) {
6485+
return -ERR_PRECONDITION_FAILED;
6486+
}
6487+
}
6488+
64206489
result.delete_marker = dirent.is_delete_marker();
64216490
r = store->unlink_obj_instance(
64226491
dpp, target->get_ctx(), target->get_bucket_info(), obj,
@@ -6458,8 +6527,18 @@ int RGWRados::Object::Delete::delete_obj(optional_yield y,
64586527
return r;
64596528
}
64606529

6461-
ObjectWriteOperation op;
6530+
if (!state->exists) {
6531+
if (!force) {
6532+
target->invalidate_state();
6533+
return -ENOENT;
6534+
} else {
6535+
ldpp_dout(dpp, 5) << "WARNING: head for \"" << src_obj <<
6536+
"\" does not exist; will continue with deleting bucket "
6537+
"index entry(ies)" << dendl;
6538+
}
6539+
}
64626540

6541+
ObjectWriteOperation op;
64636542
if (!real_clock::is_zero(params.unmod_since)) {
64646543
struct timespec ctime = ceph::real_clock::to_timespec(state->mtime);
64656544
struct timespec unmod = ceph::real_clock::to_timespec(params.unmod_since);
@@ -6514,7 +6593,12 @@ int RGWRados::Object::Delete::delete_obj(optional_yield y,
65146593
}
65156594
}
65166595

6517-
r = target->prepare_atomic_modification(dpp, op, false, NULL, NULL, NULL, true, false, y);
6596+
r = target->check_preconditions(dpp, params.size_match, params.last_mod_time_match, params.high_precision_time,
6597+
params.if_match, nullptr, *state, y);
6598+
if (!real_clock::is_zero(params.last_mod_time_match)) {
6599+
/* only delete object if mtime is equal to params.last_mod_time_match */
6600+
store->cls_obj_check_mtime(op, params.last_mod_time_match, params.high_precision_time, CLS_RGW_CHECK_TIME_MTIME_EQ);
6601+
}
65186602
if (r < 0) {
65196603
return r;
65206604
}
@@ -7083,68 +7167,119 @@ void RGWRados::Object::invalidate_state()
70837167
ctx.invalidate(obj);
70847168
}
70857169

7086-
int RGWRados::Object::prepare_atomic_modification(const DoutPrefixProvider *dpp,
7087-
ObjectWriteOperation& op, bool reset_obj, const string *ptag,
7088-
const char *if_match, const char *if_nomatch, bool removal_op,
7089-
bool modify_tail, optional_yield y)
7170+
int RGWRados::Object::get_current_version_state(const DoutPrefixProvider *dpp, RGWObjState*& current_state, optional_yield y)
70907171
{
7091-
int r = get_state(dpp, &state, &manifest, false, y);
7092-
if (r < 0)
7172+
// objects in versioning-enabled buckets don't get overwritten.
7173+
// preconditions refer to the current version instead
7174+
current_state = state;
7175+
rgw_obj current_obj = obj;
7176+
current_obj.key.instance.clear();
7177+
constexpr bool follow_olh = true; // look up current version
7178+
int r = store->get_obj_state(dpp, &ctx, bucket_info, current_obj,
7179+
&current_state, nullptr, follow_olh, y);
7180+
if (r < 0) {
7181+
ldpp_dout(dpp, 4) << "failed to load current version or no current object version for preconditions on " << current_obj << dendl;
7182+
current_state = nullptr;
70937183
return r;
7184+
} else {
7185+
ldpp_dout(dpp, 4) << "loaded current object " << current_state->obj
7186+
<< " for preconditions" << dendl;
7187+
}
70947188

7095-
bool need_guard = ((manifest) || (state->obj_tag.length() != 0) ||
7096-
if_match != NULL || if_nomatch != NULL) &&
7097-
(!state->fake_tag);
7098-
7099-
if (!state->is_atomic) {
7100-
ldpp_dout(dpp, 20) << "prepare_atomic_modification: state is not atomic. state=" << (void *)state << dendl;
7189+
return 0;
7190+
}
71017191

7102-
if (reset_obj) {
7103-
op.create(false);
7104-
store->remove_rgw_head_obj(op); // we're not dropping reference here, actually removing object
7192+
int RGWRados::Object::check_preconditions(const DoutPrefixProvider *dpp, std::optional<uint64_t> size_match,
7193+
ceph::real_time last_mod_time_match, bool high_precision_time,
7194+
const char *if_match, const char *if_nomatch, RGWObjState& current_state, optional_yield y)
7195+
{
7196+
if (size_match.has_value()) {
7197+
ldpp_dout(dpp, 10) << "RGWRados::Object::check_preconditions size_match: " << size_match.value() << ", size: " << current_state.size << dendl;
7198+
if (current_state.size != size_match) {
7199+
return -ERR_PRECONDITION_FAILED;
71057200
}
7106-
7107-
return 0;
71087201
}
71097202

7110-
if (need_guard) {
7111-
/* first verify that the object wasn't replaced under */
7112-
if (if_nomatch == NULL || strcmp(if_nomatch, "*") != 0) {
7113-
op.cmpxattr(RGW_ATTR_ID_TAG, LIBRADOS_CMPXATTR_OP_EQ, state->obj_tag);
7114-
// FIXME: need to add FAIL_NOTEXIST_OK for racing deletion
7203+
if (!real_clock::is_zero(last_mod_time_match)) {
7204+
struct timespec ctime = ceph::real_clock::to_timespec(current_state.mtime);
7205+
struct timespec last_mod_time = ceph::real_clock::to_timespec(last_mod_time_match);
7206+
if (!high_precision_time) {
7207+
ctime.tv_nsec = 0;
7208+
last_mod_time.tv_nsec = 0;
7209+
}
7210+
7211+
ldpp_dout(dpp, 10) << "RGWRados::Object::check_preconditions If-Match-Last-Modified-Time: " << last_mod_time_match
7212+
<< ", Last-Modified: " << ctime << ", with high_precision_time:" << high_precision_time << dendl;
7213+
if (ctime != last_mod_time) {
7214+
return -ERR_PRECONDITION_FAILED;
71157215
}
7216+
}
71167217

7117-
if (if_match) {
7118-
if (strcmp(if_match, "*") == 0) {
7119-
// test the object is existing
7120-
if (!state->exists) {
7218+
using namespace std::string_literals;
7219+
if (if_match) {
7220+
if (if_match == "*"sv) {
7221+
// test the object is existing
7222+
if (!current_state.exists) {
7223+
return -ENOENT;
7224+
}
7225+
} else {
7226+
bufferlist bl;
7227+
if (current_state.get_attr(RGW_ATTR_ETAG, bl)) {
7228+
string if_match_str = rgw_string_unquote(if_match);
7229+
string etag = string(bl.c_str(), bl.length());
7230+
ldpp_dout(dpp, 10) << "RGWRados::Object::check_preconditions if_match: " << if_match_str << ", etag: " << etag << dendl;
7231+
if (if_match_str.compare(0, etag.length(), etag.c_str(), etag.length()) != 0) {
71217232
return -ERR_PRECONDITION_FAILED;
71227233
}
71237234
} else {
7124-
bufferlist bl;
7125-
if (!state->get_attr(RGW_ATTR_ETAG, bl) ||
7126-
strncmp(if_match, bl.c_str(), bl.length()) != 0) {
7127-
return -ERR_PRECONDITION_FAILED;
7128-
}
7235+
ldpp_dout(dpp, 10) << "RGWRados::Object::check_preconditions if_match object has no etag" << dendl;
7236+
return (!current_state.exists)? -ENOENT: -ERR_PRECONDITION_FAILED;
71297237
}
71307238
}
7239+
}
71317240

7132-
if (if_nomatch) {
7133-
if (strcmp(if_nomatch, "*") == 0) {
7134-
// test the object is NOT existing
7135-
if (state->exists) {
7136-
return -ERR_PRECONDITION_FAILED;
7137-
}
7138-
} else {
7139-
bufferlist bl;
7140-
if (!state->get_attr(RGW_ATTR_ETAG, bl) ||
7141-
strncmp(if_nomatch, bl.c_str(), bl.length()) == 0) {
7241+
if (if_nomatch) {
7242+
if (if_nomatch == "*"sv) {
7243+
// test the object is NOT existing
7244+
if (current_state.exists) {
7245+
return -ERR_PRECONDITION_FAILED;
7246+
}
7247+
} else {
7248+
bufferlist bl;
7249+
if (current_state.get_attr(RGW_ATTR_ETAG, bl)) {
7250+
string if_nomatch_str = rgw_string_unquote(if_nomatch);
7251+
string etag = string(bl.c_str(), bl.length());
7252+
ldpp_dout(dpp, 10) << "RGWRados::Object::check_preconditions if_match: " << if_nomatch_str << ", etag: " << etag << dendl;
7253+
if (if_nomatch_str.compare(0, etag.length(), etag.c_str(), etag.length()) == 0) {
71427254
return -ERR_PRECONDITION_FAILED;
71437255
}
71447256
}
71457257
}
71467258
}
71477259

7260+
return 0;
7261+
}
7262+
7263+
int RGWRados::Object::prepare_atomic_modification(const DoutPrefixProvider *dpp, ObjectWriteOperation& op, bool reset_obj,
7264+
const string *ptag, bool modify_tail, bool set_attr_id_tag, optional_yield y)
7265+
{
7266+
if (!state->is_atomic) {
7267+
ldpp_dout(dpp, 20) << "prepare_atomic_modification: state is not atomic. state=" << (void *) state << dendl;
7268+
7269+
if (reset_obj) {
7270+
op.create(false);
7271+
store->remove_rgw_head_obj(op); // we're not dropping reference here, actually removing object
7272+
}
7273+
7274+
return 0;
7275+
}
7276+
7277+
if (set_attr_id_tag) {
7278+
/* first verify that the object wasn't replaced under */
7279+
op.cmpxattr(RGW_ATTR_ID_TAG, LIBRADOS_CMPXATTR_OP_EQ, state->obj_tag);
7280+
// FIXME: need to add FAIL_NOTEXIST_OK for racing deletion
7281+
}
7282+
71487283
if (reset_obj) {
71497284
if (state->exists) {
71507285
op.create(false);
@@ -7154,11 +7289,6 @@ int RGWRados::Object::prepare_atomic_modification(const DoutPrefixProvider *dpp,
71547289
}
71557290
}
71567291

7157-
if (removal_op) {
7158-
/* the object is being removed, no need to update its tag */
7159-
return 0;
7160-
}
7161-
71627292
if (ptag) {
71637293
state->write_tag = *ptag;
71647294
} else {

src/rgw/driver/rados/rgw_rados.h

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -717,8 +717,12 @@ class RGWRados
717717
int get_state(const DoutPrefixProvider *dpp, RGWObjState **pstate, RGWObjManifest **pmanifest, bool follow_olh, optional_yield y, bool assume_noent = false);
718718
void invalidate_state();
719719

720-
int prepare_atomic_modification(const DoutPrefixProvider *dpp, librados::ObjectWriteOperation& op, bool reset_obj, const std::string *ptag,
721-
const char *ifmatch, const char *ifnomatch, bool removal_op, bool modify_tail, optional_yield y);
720+
int get_current_version_state(const DoutPrefixProvider *dpp, RGWObjState*& current_state, optional_yield y);
721+
int check_preconditions(const DoutPrefixProvider *dpp, std::optional<uint64_t> size_match,
722+
ceph::real_time last_mod_time_match, bool high_precision_time,
723+
const char *if_match, const char *if_nomatch, RGWObjState& current_state, optional_yield y);
724+
int prepare_atomic_modification(const DoutPrefixProvider *dpp, librados::ObjectWriteOperation& op, bool reset_obj,
725+
const std::string *ptag, bool modify_tail, bool set_attr_id_tag, optional_yield y);
722726
int complete_atomic_modification(const DoutPrefixProvider *dpp, bool keep_tail, optional_yield y);
723727

724728
public:
@@ -782,8 +786,8 @@ class RGWRados
782786
bool high_precision_time;
783787
uint32_t mod_zone_id;
784788
uint64_t mod_pg_ver;
785-
const char *if_match;
786-
const char *if_nomatch;
789+
const char *if_match{nullptr};
790+
const char *if_nomatch{nullptr};
787791

788792
ConditionParams() :
789793
mod_ptr(NULL), unmod_ptr(NULL), high_precision_time(false), mod_zone_id(0), mod_pg_ver(0),
@@ -829,8 +833,8 @@ class RGWRados
829833
ACLOwner owner; // owner/owner_display_name for bucket index
830834
RGWObjCategory category;
831835
int flags;
832-
const char *if_match;
833-
const char *if_nomatch;
836+
const char *if_match{nullptr};
837+
const char *if_nomatch{nullptr};
834838
std::optional<uint64_t> olh_epoch;
835839
ceph::real_time delete_at;
836840
bool canceled;
@@ -877,7 +881,10 @@ class RGWRados
877881
std::list<rgw_obj_index_key> *remove_objs;
878882
ceph::real_time expiration_time;
879883
ceph::real_time unmod_since;
884+
ceph::real_time last_mod_time_match;
880885
ceph::real_time mtime; /* for setting delete marker mtime */
886+
std::optional<uint64_t> size_match;
887+
const char *if_match{nullptr};
881888
bool high_precision_time;
882889
rgw_zone_set *zones_trace;
883890
bool abortmp;

src/rgw/driver/rados/rgw_sal_rados.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3589,7 +3589,10 @@ int RadosObject::RadosDeleteOp::delete_obj(const DoutPrefixProvider* dpp, option
35893589
parent_op.params.remove_objs = params.remove_objs;
35903590
parent_op.params.expiration_time = params.expiration_time;
35913591
parent_op.params.unmod_since = params.unmod_since;
3592+
parent_op.params.last_mod_time_match = params.last_mod_time_match;
35923593
parent_op.params.mtime = params.mtime;
3594+
parent_op.params.size_match = params.size_match;
3595+
parent_op.params.if_match = params.if_match;
35933596
parent_op.params.high_precision_time = params.high_precision_time;
35943597
parent_op.params.zones_trace = params.zones_trace;
35953598
parent_op.params.abortmp = params.abortmp;

0 commit comments

Comments
 (0)