@@ -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,34 @@ 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+ if (string if_match = rgw_string_unquote (params.if_match ); dirent.meta .etag != if_match) {
6461+ return -ERR_PRECONDITION_FAILED;
6462+ }
6463+ }
6464+
6465+ if (params.size_match .has_value ()) {
6466+ if (params.size_match != dirent.meta .size ) {
6467+ return -ERR_PRECONDITION_FAILED;
6468+ }
6469+ }
6470+
6471+ if (!real_clock::is_zero (params.last_mod_time_match )) {
6472+ struct timespec ctime = ceph::real_clock::to_timespec (dirent.meta .mtime );
6473+ struct timespec last_mod_time = ceph::real_clock::to_timespec (params.last_mod_time_match );
6474+ if (!params.high_precision_time ) {
6475+ ctime.tv_nsec = 0 ;
6476+ last_mod_time.tv_nsec = 0 ;
6477+ }
6478+
6479+ ldpp_dout (dpp, 10 ) << " If-Match-Last-Modified-Time: " << params.last_mod_time_match << " Last-Modified: " << ctime << dendl;
6480+ if (ctime != last_mod_time) {
6481+ return -ERR_PRECONDITION_FAILED;
6482+ }
6483+ }
6484+
64206485 result.delete_marker = dirent.is_delete_marker ();
64216486 r = store->unlink_obj_instance (
64226487 dpp, target->get_ctx (), target->get_bucket_info (), obj,
@@ -6458,8 +6523,18 @@ int RGWRados::Object::Delete::delete_obj(optional_yield y,
64586523 return r;
64596524 }
64606525
6461- ObjectWriteOperation op;
6526+ if (!state->exists ) {
6527+ if (!force) {
6528+ target->invalidate_state ();
6529+ return -ENOENT;
6530+ } else {
6531+ ldpp_dout (dpp, 5 ) << " WARNING: head for \" " << src_obj <<
6532+ " \" does not exist; will continue with deleting bucket "
6533+ " index entry(ies)" << dendl;
6534+ }
6535+ }
64626536
6537+ ObjectWriteOperation op;
64636538 if (!real_clock::is_zero (params.unmod_since )) {
64646539 struct timespec ctime = ceph::real_clock::to_timespec (state->mtime );
64656540 struct timespec unmod = ceph::real_clock::to_timespec (params.unmod_since );
@@ -6514,7 +6589,12 @@ int RGWRados::Object::Delete::delete_obj(optional_yield y,
65146589 }
65156590 }
65166591
6517- r = target->prepare_atomic_modification (dpp, op, false , NULL , NULL , NULL , true , false , y);
6592+ r = target->check_preconditions (dpp, params.size_match , params.last_mod_time_match , params.high_precision_time ,
6593+ params.if_match , nullptr , *state, y);
6594+ if (!real_clock::is_zero (params.last_mod_time_match )) {
6595+ /* only delete object if mtime is equal to params.last_mod_time_match */
6596+ store->cls_obj_check_mtime (op, params.last_mod_time_match , params.high_precision_time , CLS_RGW_CHECK_TIME_MTIME_EQ);
6597+ }
65186598 if (r < 0 ) {
65196599 return r;
65206600 }
@@ -7083,68 +7163,112 @@ void RGWRados::Object::invalidate_state()
70837163 ctx.invalidate (obj);
70847164}
70857165
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)
7166+ int RGWRados::Object::get_current_version_state (const DoutPrefixProvider *dpp, RGWObjState*& current_state, optional_yield y)
70907167{
7091- int r = get_state (dpp, &state, &manifest, false , y);
7092- if (r < 0 )
7168+ // objects in versioning-enabled buckets don't get overwritten.
7169+ // preconditions refer to the current version instead
7170+ current_state = state;
7171+ rgw_obj current_obj = obj;
7172+ current_obj.key .instance .clear ();
7173+ constexpr bool follow_olh = true ; // look up current version
7174+ int r = store->get_obj_state (dpp, &ctx, bucket_info, current_obj,
7175+ ¤t_state, nullptr , follow_olh, y);
7176+ if (r < 0 ) {
7177+ ldpp_dout (dpp, 4 ) << " failed to load current version or no current object version for preconditions on " << current_obj << dendl;
7178+ current_state = nullptr ;
70937179 return r;
7180+ } else {
7181+ ldpp_dout (dpp, 4 ) << " loaded current object " << current_state->obj
7182+ << " for preconditions" << dendl;
7183+ }
70947184
7095- bool need_guard = ((manifest) || (state->obj_tag .length () != 0 ) ||
7096- if_match != NULL || if_nomatch != NULL ) &&
7097- (!state->fake_tag );
7185+ return 0 ;
7186+ }
70987187
7099- if (!state->is_atomic ) {
7100- ldpp_dout (dpp, 20 ) << " prepare_atomic_modification: state is not atomic. state=" << (void *)state << dendl;
7188+ int RGWRados::Object::check_preconditions (const DoutPrefixProvider *dpp, std::optional<uint64_t > size_match,
7189+ ceph::real_time last_mod_time_match, bool high_precision_time,
7190+ const char *if_match, const char *if_nomatch, RGWObjState& current_state, optional_yield y)
7191+ {
7192+ if (size_match.has_value () && current_state.size != size_match) {
7193+ return -ERR_PRECONDITION_FAILED;
7194+ }
71017195
7102- if (reset_obj) {
7103- op.create (false );
7104- store->remove_rgw_head_obj (op); // we're not dropping reference here, actually removing object
7196+ if (!real_clock::is_zero (last_mod_time_match)) {
7197+ struct timespec ctime = ceph::real_clock::to_timespec (current_state.mtime );
7198+ struct timespec last_mod_time = ceph::real_clock::to_timespec (last_mod_time_match);
7199+ if (!high_precision_time) {
7200+ ctime.tv_nsec = 0 ;
7201+ last_mod_time.tv_nsec = 0 ;
71057202 }
71067203
7107- return 0 ;
7108- }
7109-
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
7204+ ldpp_dout (dpp, 10 ) << " If-Match-Last-Modified-Time: " << last_mod_time_match << " Last-Modified: " << ctime << dendl;
7205+ if (ctime != last_mod_time) {
7206+ return -ERR_PRECONDITION_FAILED;
71157207 }
7208+ }
71167209
7117- if (if_match) {
7118- if (strcmp (if_match, " *" ) == 0 ) {
7119- // test the object is existing
7120- if (!state->exists ) {
7210+ using namespace std ::string_literals;
7211+ if (if_match) {
7212+ if (if_match == " *" sv) {
7213+ // test the object is existing
7214+ if (!current_state.exists ) {
7215+ return -ENOENT;
7216+ }
7217+ } else {
7218+ bufferlist bl;
7219+ if (current_state.get_attr (RGW_ATTR_ETAG, bl)) {
7220+ string if_match_str = rgw_string_unquote (if_match);
7221+ string etag = string (bl.c_str (), bl.length ());
7222+ if (if_match_str.compare (0 , etag.length (), etag.c_str (), etag.length ()) != 0 ) {
71217223 return -ERR_PRECONDITION_FAILED;
71227224 }
71237225 } 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- }
7226+ return (!current_state.exists )? -ENOENT: -ERR_PRECONDITION_FAILED;
71297227 }
71307228 }
7229+ }
71317230
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 ) {
7231+ if (if_nomatch) {
7232+ if (if_nomatch == " *" sv) {
7233+ // test the object is NOT existing
7234+ if (current_state.exists ) {
7235+ return -ERR_PRECONDITION_FAILED;
7236+ }
7237+ } else {
7238+ bufferlist bl;
7239+ if (current_state.get_attr (RGW_ATTR_ETAG, bl)) {
7240+ string if_nomatch_str = rgw_string_unquote (if_nomatch);
7241+ string etag = string (bl.c_str (), bl.length ());
7242+ if (if_nomatch_str.compare (0 , etag.length (), etag.c_str (), etag.length ()) == 0 ) {
71427243 return -ERR_PRECONDITION_FAILED;
71437244 }
71447245 }
71457246 }
71467247 }
71477248
7249+ return 0 ;
7250+ }
7251+
7252+ int RGWRados::Object::prepare_atomic_modification (const DoutPrefixProvider *dpp, ObjectWriteOperation& op, bool reset_obj,
7253+ const string *ptag, bool modify_tail, bool set_attr_id_tag, optional_yield y)
7254+ {
7255+ if (!state->is_atomic ) {
7256+ ldpp_dout (dpp, 20 ) << " prepare_atomic_modification: state is not atomic. state=" << (void *) state << dendl;
7257+
7258+ if (reset_obj) {
7259+ op.create (false );
7260+ store->remove_rgw_head_obj (op); // we're not dropping reference here, actually removing object
7261+ }
7262+
7263+ return 0 ;
7264+ }
7265+
7266+ if (set_attr_id_tag) {
7267+ /* first verify that the object wasn't replaced under */
7268+ op.cmpxattr (RGW_ATTR_ID_TAG, LIBRADOS_CMPXATTR_OP_EQ, state->obj_tag );
7269+ // FIXME: need to add FAIL_NOTEXIST_OK for racing deletion
7270+ }
7271+
71487272 if (reset_obj) {
71497273 if (state->exists ) {
71507274 op.create (false );
@@ -7154,11 +7278,6 @@ int RGWRados::Object::prepare_atomic_modification(const DoutPrefixProvider *dpp,
71547278 }
71557279 }
71567280
7157- if (removal_op) {
7158- /* the object is being removed, no need to update its tag */
7159- return 0 ;
7160- }
7161-
71627281 if (ptag) {
71637282 state->write_tag = *ptag;
71647283 } else {
0 commit comments