bool high_precision_time;
uint32_t mod_zone_id;
uint64_t mod_pg_ver;
- const char *if_match;
- const char *if_nomatch;
+ const char *if_match{nullptr};
+ const char *if_nomatch{nullptr};
ConditionParams() :
mod_ptr(NULL), unmod_ptr(NULL), high_precision_time(false), mod_zone_id(0), mod_pg_ver(0),
rgw_user owner;
RGWObjCategory category;
int flags;
- const char *if_match;
- const char *if_nomatch;
+ const char *if_match{nullptr};
+ const char *if_nomatch{nullptr};
std::optional<uint64_t> olh_epoch;
ceph::real_time delete_at;
bool canceled;
std::list<rgw_obj_index_key> *remove_objs;
ceph::real_time expiration_time;
ceph::real_time unmod_since;
+ ceph::real_time last_mod_time_match;
ceph::real_time mtime; /* for setting delete marker mtime */
+ std::optional<uint64_t> size_match;
+ const char *if_match{nullptr};
+ const char *if_nomatch{nullptr};
bool high_precision_time;
rgw_zone_set *zones_trace;
bool abortmp;
if (!ptag && !index_op->get_optag()->empty()) {
ptag = index_op->get_optag();
}
- r = target->prepare_atomic_modification(rctx.dpp, op, reset_obj, ptag, meta.if_match, meta.if_nomatch, false, meta.modify_tail, rctx.y);
+
+ r = target->get_state(rctx.dpp, &target->state, &target->manifest, false, rctx.y);
+ if (r < 0)
+ return r;
+ RGWObjState* current_state = target->state;
+ if (!target->obj.key.instance.empty()) {
+ r = target->get_current_version_state(rctx.dpp, current_state, rctx.y);
+ if (r == -ENOENT) {
+ current_state = target->state;
+ } else if (r < 0) {
+ return r;
+ }
+ }
+
+ r = target->check_preconditions(rctx.dpp, std::nullopt, real_clock::zero(), false, meta.if_match, meta.if_nomatch, *current_state, rctx.y);
+ if (r < 0) {
+ return r;
+ }
+ bool guard = ((target->manifest) || (target->state->obj_tag.length() != 0)) && (!target->state->fake_tag);
+ bool set_attr_id_tag = guard && target->obj.key.instance.empty() && (meta.if_nomatch == nullptr || meta.if_nomatch != "*"sv);
+ r = target->prepare_atomic_modification(rctx.dpp, op, reset_obj, ptag, meta.modify_tail, set_attr_id_tag, rctx.y);
if (r < 0)
return r;
meta.mtime = params.mtime;
}
- int r = store->set_olh(dpp, target->get_ctx(), target->get_bucket_info(), marker, true,
+ int r = target->get_state(dpp, &target->state, &target->manifest, false, y);
+ if (r < 0)
+ return r;
+ RGWObjState* current_state = target->state;
+ r = target->get_current_version_state(dpp, current_state, y);
+ if (r == -ENOENT) {
+ current_state = target->state;
+ } else if (r < 0) {
+ return r;
+ }
+
+ r = target->check_preconditions(dpp, params.size_match, params.last_mod_time_match, params.high_precision_time,
+ params.if_match, nullptr, *current_state, y);
+ if (r < 0) {
+ return r;
+ }
+
+ r = store->set_olh(dpp, target->get_ctx(), target->get_bucket_info(), marker, true,
&meta, params.olh_epoch, params.unmod_since, params.high_precision_time,
y, params.zones_trace, add_log, skip_olh_obj_update);
if (r < 0) {
if (r < 0) {
return r;
}
+
+ using namespace std::string_literals;
+ if (params.if_match && params.if_match != "*"sv) {
+ if(string if_match = rgw_string_unquote(params.if_match); dirent.meta.etag != if_match) {
+ return -ERR_PRECONDITION_FAILED;
+ }
+ }
+
+ if (params.size_match.has_value()) {
+ if (params.size_match != dirent.meta.size) {
+ return -ERR_PRECONDITION_FAILED;
+ }
+ }
+
+ if (!real_clock::is_zero(params.last_mod_time_match)) {
+ struct timespec ctime = ceph::real_clock::to_timespec(dirent.meta.mtime);
+ struct timespec last_mod_time = ceph::real_clock::to_timespec(params.last_mod_time_match);
+ if (!params.high_precision_time) {
+ ctime.tv_nsec = 0;
+ last_mod_time.tv_nsec = 0;
+ }
+
+ ldpp_dout(dpp, 10) << "If-Match-Last-Modified-Time: " << params.last_mod_time_match << " Last-Modified: " << ctime << dendl;
+ if (ctime != last_mod_time) {
+ return -ERR_PRECONDITION_FAILED;
+ }
+ }
+
result.delete_marker = dirent.is_delete_marker();
r = store->unlink_obj_instance(
dpp, target->get_ctx(), target->get_bucket_info(), obj,
return r;
}
- ObjectWriteOperation op;
+ if (!state->exists) {
+ if (!force) {
+ target->invalidate_state();
+ return -ENOENT;
+ } else {
+ ldpp_dout(dpp, 5) << "WARNING: head for \"" << src_obj <<
+ "\" does not exist; will continue with deleting bucket "
+ "index entry(ies)" << dendl;
+ }
+ }
+ ObjectWriteOperation op;
if (!real_clock::is_zero(params.unmod_since)) {
struct timespec ctime = ceph::real_clock::to_timespec(state->mtime);
struct timespec unmod = ceph::real_clock::to_timespec(params.unmod_since);
}
}
- r = target->prepare_atomic_modification(dpp, op, false, NULL, NULL, NULL, true, false, y);
+ r = target->check_preconditions(dpp, params.size_match, params.last_mod_time_match, params.high_precision_time,
+ params.if_match, nullptr, *state, y);
+ if (!real_clock::is_zero(params.last_mod_time_match)) {
+ /* only delete object if mtime is equal to params.last_mod_time_match */
+ store->cls_obj_check_mtime(op, params.last_mod_time_match, params.high_precision_time, CLS_RGW_CHECK_TIME_MTIME_EQ);
+ }
if (r < 0) {
return r;
}
ctx.invalidate(obj);
}
-int RGWRados::Object::prepare_atomic_modification(const DoutPrefixProvider *dpp,
- ObjectWriteOperation& op, bool reset_obj, const string *ptag,
- const char *if_match, const char *if_nomatch, bool removal_op,
- bool modify_tail, optional_yield y)
+int RGWRados::Object::get_current_version_state(const DoutPrefixProvider *dpp, RGWObjState*& current_state, optional_yield y)
{
- int r = get_state(dpp, &state, &manifest, false, y);
- if (r < 0)
+ // objects in versioning-enabled buckets don't get overwritten.
+ // preconditions refer to the current version instead
+ current_state = state;
+ rgw_obj current_obj = obj;
+ current_obj.key.instance.clear();
+ constexpr bool follow_olh = true; // look up current version
+ int r = store->get_obj_state(dpp, &ctx, bucket_info, current_obj,
+ ¤t_state, nullptr, follow_olh, y);
+ if (r < 0) {
+ ldpp_dout(dpp, 4) << "failed to load current version or no current object version for preconditions on " << current_obj << dendl;
+ current_state = nullptr;
return r;
+ } else {
+ ldpp_dout(dpp, 4) << "loaded current object " << current_state->obj
+ << " for preconditions" << dendl;
+ }
- bool need_guard = ((manifest) || (state->obj_tag.length() != 0) ||
- if_match != NULL || if_nomatch != NULL) &&
- (!state->fake_tag);
+ return 0;
+}
- if (!state->is_atomic) {
- ldpp_dout(dpp, 20) << "prepare_atomic_modification: state is not atomic. state=" << (void *)state << dendl;
+int RGWRados::Object::check_preconditions(const DoutPrefixProvider *dpp, std::optional<uint64_t> size_match,
+ ceph::real_time last_mod_time_match, bool high_precision_time,
+ const char *if_match, const char *if_nomatch, RGWObjState& current_state, optional_yield y)
+{
+ if (size_match.has_value() && current_state.size != size_match) {
+ return -ERR_PRECONDITION_FAILED;
+ }
- if (reset_obj) {
- op.create(false);
- store->remove_rgw_head_obj(op); // we're not dropping reference here, actually removing object
+ if (!real_clock::is_zero(last_mod_time_match)) {
+ struct timespec ctime = ceph::real_clock::to_timespec(current_state.mtime);
+ struct timespec last_mod_time = ceph::real_clock::to_timespec(last_mod_time_match);
+ if (!high_precision_time) {
+ ctime.tv_nsec = 0;
+ last_mod_time.tv_nsec = 0;
}
- return 0;
- }
-
- if (need_guard) {
- /* first verify that the object wasn't replaced under */
- 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
+ ldpp_dout(dpp, 10) << "If-Match-Last-Modified-Time: " << last_mod_time_match << " Last-Modified: " << ctime << dendl;
+ if (ctime != last_mod_time) {
+ return -ERR_PRECONDITION_FAILED;
}
+ }
- if (if_match) {
- if (strcmp(if_match, "*") == 0) {
- // test the object is existing
- if (!state->exists) {
+ using namespace std::string_literals;
+ if (if_match) {
+ if (if_match == "*"sv) {
+ // test the object is existing
+ if (!current_state.exists) {
+ return -ENOENT;
+ }
+ } else {
+ bufferlist bl;
+ if (current_state.get_attr(RGW_ATTR_ETAG, bl)) {
+ string if_match_str = rgw_string_unquote(if_match);
+ string etag = string(bl.c_str(), bl.length());
+ if (if_match_str.compare(0, etag.length(), etag.c_str(), etag.length()) != 0) {
return -ERR_PRECONDITION_FAILED;
}
} else {
- bufferlist bl;
- if (!state->get_attr(RGW_ATTR_ETAG, bl) ||
- strncmp(if_match, bl.c_str(), bl.length()) != 0) {
- return -ERR_PRECONDITION_FAILED;
- }
+ return (!current_state.exists)? -ENOENT: -ERR_PRECONDITION_FAILED;
}
}
+ }
- if (if_nomatch) {
- if (strcmp(if_nomatch, "*") == 0) {
- // test the object is NOT existing
- if (state->exists) {
- return -ERR_PRECONDITION_FAILED;
- }
- } else {
- bufferlist bl;
- if (!state->get_attr(RGW_ATTR_ETAG, bl) ||
- strncmp(if_nomatch, bl.c_str(), bl.length()) == 0) {
+ if (if_nomatch) {
+ if (if_nomatch == "*"sv) {
+ // test the object is NOT existing
+ if (current_state.exists) {
+ return -ERR_PRECONDITION_FAILED;
+ }
+ } else {
+ bufferlist bl;
+ if (current_state.get_attr(RGW_ATTR_ETAG, bl)) {
+ string if_nomatch_str = rgw_string_unquote(if_nomatch);
+ string etag = string(bl.c_str(), bl.length());
+ if (if_nomatch_str.compare(0, etag.length(), etag.c_str(), etag.length()) == 0) {
return -ERR_PRECONDITION_FAILED;
}
}
}
}
+ return 0;
+}
+
+int RGWRados::Object::prepare_atomic_modification(const DoutPrefixProvider *dpp, ObjectWriteOperation& op, bool reset_obj,
+ const string *ptag, bool modify_tail, bool set_attr_id_tag, optional_yield y)
+{
+ if (!state->is_atomic) {
+ ldpp_dout(dpp, 20) << "prepare_atomic_modification: state is not atomic. state=" << (void *) state << dendl;
+
+ if (reset_obj) {
+ op.create(false);
+ store->remove_rgw_head_obj(op); // we're not dropping reference here, actually removing object
+ }
+
+ return 0;
+ }
+
+ if (set_attr_id_tag) {
+ /* 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 (reset_obj) {
if (state->exists) {
op.create(false);
}
}
- if (removal_op) {
- /* the object is being removed, no need to update its tag */
- return 0;
- }
-
if (ptag) {
state->write_tag = *ptag;
} else {
int get_state(const DoutPrefixProvider *dpp, RGWObjState **pstate, RGWObjManifest **pmanifest, bool follow_olh, optional_yield y, bool assume_noent = false);
void invalidate_state();
- int prepare_atomic_modification(const DoutPrefixProvider *dpp, librados::ObjectWriteOperation& op, bool reset_obj, const std::string *ptag,
- const char *ifmatch, const char *ifnomatch, bool removal_op, bool modify_tail, optional_yield y);
+ int get_current_version_state(const DoutPrefixProvider *dpp, RGWObjState*& current_state, optional_yield y);
+ int check_preconditions(const DoutPrefixProvider *dpp, std::optional<uint64_t> size_match,
+ ceph::real_time last_mod_time_match, bool high_precision_time,
+ const char *if_match, const char *if_nomatch, RGWObjState& current_state, optional_yield y);
+ int prepare_atomic_modification(const DoutPrefixProvider *dpp, librados::ObjectWriteOperation& op, bool reset_obj,
+ const std::string *ptag, bool modify_tail, bool set_attr_id_tag, optional_yield y);
int complete_atomic_modification(const DoutPrefixProvider *dpp, bool keep_tail, optional_yield y);
public:
bool high_precision_time;
uint32_t mod_zone_id;
uint64_t mod_pg_ver;
- const char *if_match;
- const char *if_nomatch;
+ const char *if_match{nullptr};
+ const char *if_nomatch{nullptr};
ConditionParams() :
mod_ptr(NULL), unmod_ptr(NULL), high_precision_time(false), mod_zone_id(0), mod_pg_ver(0),
ACLOwner owner; // owner/owner_display_name for bucket index
RGWObjCategory category;
int flags;
- const char *if_match;
- const char *if_nomatch;
+ const char *if_match{nullptr};
+ const char *if_nomatch{nullptr};
std::optional<uint64_t> olh_epoch;
ceph::real_time delete_at;
bool canceled;
std::list<rgw_obj_index_key> *remove_objs;
ceph::real_time expiration_time;
ceph::real_time unmod_since;
+ ceph::real_time last_mod_time_match;
ceph::real_time mtime; /* for setting delete marker mtime */
+ std::optional<uint64_t> size_match;
+ const char *if_match{nullptr};
bool high_precision_time;
rgw_zone_set *zones_trace;
bool abortmp;
parent_op.params.remove_objs = params.remove_objs;
parent_op.params.expiration_time = params.expiration_time;
parent_op.params.unmod_since = params.unmod_since;
+ parent_op.params.last_mod_time_match = params.last_mod_time_match;
parent_op.params.mtime = params.mtime;
+ parent_op.params.size_match = params.size_match;
+ parent_op.params.if_match = params.if_match;
parent_op.params.high_precision_time = params.high_precision_time;
parent_op.params.zones_trace = params.zones_trace;
parent_op.params.abortmp = params.abortmp;
{
RGWMultiDelKey *key_obj = static_cast<RGWMultiDelKey *>(find_first("Key"));
RGWMultiDelVersionId *vid = static_cast<RGWMultiDelVersionId *>(find_first("VersionId"));
+ XMLObj *etag_match = static_cast<XMLObj *>(find_first("ETag"));
+ XMLObj *last_modified_time = static_cast<XMLObj *>(find_first("LastModifiedTime"));
+ XMLObj *size = static_cast<XMLObj *>(find_first("Size"));
if (!key_obj)
return false;
version_id = vid->get_data();
}
+ if (etag_match) {
+ if_match = etag_match->get_data().c_str();
+ }
+
+ if(last_modified_time) {
+ string last_modified_time_str = last_modified_time->get_data();
+ if (last_modified_time_str.empty())
+ return false;
+
+ string last_modified_time_str_decoded = url_decode(last_modified_time_str);
+ if (parse_time(last_modified_time_str_decoded.c_str(), &last_mod_time) < 0)
+ return false;
+ }
+
+ if (size) {
+ string err;
+ long long size_tmp = strict_strtoll(size->get_data(), 10, &err);
+ if (!err.empty()) {
+ return false;
+ }
+ size_match = uint64_t(size_tmp);
+ }
+
return true;
}
XMLObjIter iter = find("Object");
RGWMultiDelObject *object = static_cast<RGWMultiDelObject *>(iter.get_next());
while (object) {
- const string& key = object->get_key();
- const string& instance = object->get_version_id();
- rgw_obj_key k(key, instance);
- objects.push_back(k);
+ objects.push_back(*object);
object = static_cast<RGWMultiDelObject *>(iter.get_next());
}
return true;
#include "rgw_xml.h"
#include "rgw_common.h"
+class RGWMultiDelObject : public XMLObj
+{
+ std::string key;
+ std::string version_id;
+ const char *if_match{nullptr};
+ ceph::real_time last_mod_time;
+ std::optional<uint64_t> size_match;
+public:
+ RGWMultiDelObject() {}
+ ~RGWMultiDelObject() override {}
+ bool xml_end(const char *el) override;
+
+ const std::string& get_key() const { return key; }
+ const std::string& get_version_id() const { return version_id; }
+ const char* get_if_match() const { return if_match; }
+ const ceph::real_time& get_last_mod_time() const { return last_mod_time; }
+ const std::optional<uint64_t> get_size_match() const { return size_match; }
+};
+
class RGWMultiDelDelete : public XMLObj
{
public:
~RGWMultiDelDelete() override {}
bool xml_end(const char *el) override;
- std::vector<rgw_obj_key> objects;
+ std::vector<RGWMultiDelObject> objects;
bool quiet;
bool is_quiet() { return quiet; }
};
~RGWMultiDelQuiet() override {}
};
-class RGWMultiDelObject : public XMLObj
-{
- std::string key;
- std::string version_id;
-public:
- RGWMultiDelObject() {}
- ~RGWMultiDelObject() override {}
- bool xml_end(const char *el) override;
-
- const std::string& get_key() { return key; }
- const std::string& get_version_id() { return version_id; }
-};
-
class RGWMultiDelKey : public XMLObj
{
public:
del_op->params.bucket_owner = s->bucket_owner.id;
del_op->params.versioning_status = s->bucket->get_info().versioning_status();
del_op->params.unmod_since = unmod_since;
+ del_op->params.last_mod_time_match = last_mod_time_match;
del_op->params.high_precision_time = s->system_request;
del_op->params.olh_epoch = epoch;
del_op->params.marker_version_id = version_id;
del_op->params.null_verid = null_verid;
+ del_op->params.size_match = size_match;
+ del_op->params.if_match = if_match;
op_ret = del_op->delete_obj(this, y, rgw::sal::FLAG_LOG_OP);
if (op_ret >= 0) {
entry.delete_multi_obj_meta.objects = std::move(ops_log_entries);
}
-void RGWDeleteMultiObj::handle_individual_object(const rgw_obj_key& o, optional_yield y, const bool skip_olh_obj_update)
+void RGWDeleteMultiObj::handle_individual_object(const RGWMultiDelObject& object, optional_yield y, const bool skip_olh_obj_update)
{
+ const string& key = object.get_key();
+ const string& instance = object.get_version_id();
+ rgw_obj_key o(key, instance);
// add the object key to the dout prefix so we can trace concurrent calls
struct ObjectPrefix : public DoutPrefixPipe {
const rgw_obj_key& o;
del_op->params.obj_owner = s->owner;
del_op->params.bucket_owner = s->bucket_owner.id;
del_op->params.marker_version_id = version_id;
+ del_op->params.last_mod_time_match = object.get_last_mod_time();
+ del_op->params.if_match = object.get_if_match();
+ del_op->params.size_match = object.get_size_match();
op_ret = del_op->delete_obj(dpp, y,
rgw::sal::FLAG_LOG_OP | (skip_olh_obj_update ? rgw::sal::FLAG_SKIP_UPDATE_OLH : 0));
send_partial_response(o, del_op->result.delete_marker, del_op->result.version_id, op_ret);
}
-void RGWDeleteMultiObj::handle_versioned_objects(const std::vector<rgw_obj_key>& objects,
+void RGWDeleteMultiObj::handle_versioned_objects(const std::vector<RGWMultiDelObject>& objects,
uint32_t max_aio,
boost::asio::yield_context yield)
{
auto group = ceph::async::spawn_throttle{yield, max_aio};
- std::map<std::string, std::vector<rgw_obj_key>> grouped_objects;
+ std::map<std::string, std::vector<RGWMultiDelObject>> grouped_objects;
// group objects by their keys
for (const auto& object : objects) {
- const std::string& key = object.name;
+ const std::string& key = object.get_key();
grouped_objects[key].push_back(object);
}
group.wait();
}
-void RGWDeleteMultiObj::handle_non_versioned_objects(const std::vector<rgw_obj_key>& objects,
+void RGWDeleteMultiObj::handle_non_versioned_objects(const std::vector<RGWMultiDelObject>& objects,
uint32_t max_aio,
boost::asio::yield_context yield)
{
group.wait();
}
-void RGWDeleteMultiObj::handle_objects(const std::vector<rgw_obj_key>& objects,
+void RGWDeleteMultiObj::handle_objects(const std::vector<RGWMultiDelObject>& objects,
uint32_t max_aio,
boost::asio::yield_context yield)
{
if (s->bucket->get_info().mfa_enabled()) {
bool has_versioned = false;
- for (auto i : multi_delete->objects) {
- if (!i.instance.empty()) {
+ for (auto object : multi_delete->objects) {
+ const string& instance = object.get_version_id();
+ if (instance.empty()) {
has_versioned = true;
break;
}
class RGWRados;
class RGWMultiCompleteUpload;
class RGWPutObj_Torrent;
+class RGWMultiDelObject;
namespace rgw::auth::registry { class StrategyRegistry; }
const char *range_str;
const char *if_mod;
const char *if_unmod;
- const char *if_match;
- const char *if_nomatch;
+ const char *if_match{nullptr};
+ const char *if_nomatch{nullptr};
uint32_t mod_zone_id;
uint64_t mod_pg_ver;
off_t ofs;
off_t ofs;
const char *supplied_md5_b64;
const char *supplied_etag;
- const char *if_match;
- const char *if_nomatch;
+ const char *if_match{nullptr};
+ const char *if_nomatch{nullptr};
std::string copy_source;
const char *copy_source_range;
RGWBucketInfo copy_source_bucket_info;
bool multipart_delete;
std::string version_id;
ceph::real_time unmod_since; /* if unmodified since */
+ ceph::real_time last_mod_time_match; /* if modified time match */
+ std::optional<uint64_t> size_match; /* if size match */
+ const char *if_match{nullptr}; /* if etag match */
bool no_precondition_error;
std::unique_ptr<RGWBulkDelete::Deleter> deleter;
bool bypass_perm;
RGWAccessControlPolicy dest_policy;
const char *if_mod;
const char *if_unmod;
- const char *if_match;
- const char *if_nomatch;
+ const char *if_match{nullptr};
+ const char *if_nomatch{nullptr};
// Required or it is not a copy operation
std::string_view copy_source;
// Not actually required
* Handles the deletion of an individual object and uses
* set_partial_response to record the outcome.
*/
- void handle_individual_object(const rgw_obj_key& object,
+ void handle_individual_object(const RGWMultiDelObject& object,
optional_yield y,
const bool skip_olh_obj_update = false);
- void handle_versioned_objects(const std::vector<rgw_obj_key>& objects,
+ void handle_versioned_objects(const std::vector<RGWMultiDelObject>& objects,
uint32_t max_aio, boost::asio::yield_context yield);
- void handle_non_versioned_objects(const std::vector<rgw_obj_key>& objects,
+ void handle_non_versioned_objects(const std::vector<RGWMultiDelObject>& objects,
uint32_t max_aio, boost::asio::yield_context yield);
- void handle_objects(const std::vector<rgw_obj_key>& objects,
+ void handle_objects(const std::vector<RGWMultiDelObject>& objects,
uint32_t max_aio, boost::asio::yield_context yield);
protected:
int RGWDeleteObj_ObjStore_S3::get_params(optional_yield y)
{
const char *if_unmod = s->info.env->get("HTTP_X_AMZ_DELETE_IF_UNMODIFIED_SINCE");
+ const char *if_last_mod_time_match = s->info.env->get("HTTP_X_AMZ_IF_MATCH_LAST_MODIFIED_TIME");
+ const char *if_size_match = s->info.env->get("HTTP_X_AMZ_IF_MATCH_SIZE");
+ if_match = s->info.env->get("HTTP_IF_MATCH");
if (s->system_request) {
s->info.args.get_bool(RGW_SYS_PARAM_PREFIX "no-precondition-error", &no_precondition_error, false);
unmod_since = utime_t(epoch, nsec).to_real_time();
}
+ if (if_last_mod_time_match) {
+ std::string if_last_mod_match_decoded = url_decode(if_last_mod_time_match);
+ int r = parse_time(if_last_mod_match_decoded.c_str(), &last_mod_time_match);
+ if (r < 0) {
+ ldpp_dout(this, 10) << "failed to parse time: " << if_last_mod_match_decoded << dendl;
+ return r;
+ }
+ }
+
+ if(if_size_match) {
+ string err;
+ long long size_tmp = strict_strtoll(if_size_match, 10, &err);
+ if (!err.empty()) {
+ ldpp_dout(s, 10) << "bad size: " << if_size_match << ": " << err << dendl;
+ return -EINVAL;
+ }
+ size_match = uint64_t(size_tmp);
+ }
+
const char *bypass_gov_header = s->info.env->get("HTTP_X_AMZ_BYPASS_GOVERNANCE_RETENTION");
if (bypass_gov_header) {
std::string bypass_gov_decoded = url_decode(bypass_gov_header);
std::list<rgw_obj_index_key>* remove_objs{nullptr};
ceph::real_time expiration_time;
ceph::real_time unmod_since;
+ ceph::real_time last_mod_time_match;
ceph::real_time mtime;
+ std::optional<uint64_t> size_match;
+ const char *if_match{nullptr};
bool high_precision_time{false};
rgw_zone_set* zones_trace{nullptr};
bool abortmp{false};
parent_op.params.remove_objs = params.remove_objs;
parent_op.params.expiration_time = params.expiration_time;
parent_op.params.unmod_since = params.unmod_since;
+ parent_op.params.last_mod_time_match = params.last_mod_time_match;
parent_op.params.mtime = params.mtime;
+ parent_op.params.size_match = params.size_match;
parent_op.params.high_precision_time = params.high_precision_time;
parent_op.params.zones_trace = params.zones_trace;
parent_op.params.abortmp = params.abortmp;