@@ -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+ ¤t_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 {
0 commit comments