OPTION(rgw_curl_low_speed_time, OPT_INT) // low speed time for certain curl calls
OPTION(rgw_copy_obj_progress, OPT_BOOL) // should dump progress during long copy operations?
OPTION(rgw_copy_obj_progress_every_bytes, OPT_INT) // min bytes between copy progress output
+OPTION(rgw_copy_verify_object, OPT_BOOL) // verify if the copied object is identical to source
OPTION(rgw_obj_tombstone_cache_size, OPT_INT) // how many objects in tombstone cache, which is used in multi-zone sync to keep
// track of removed objects' mtime
.set_default(1_M)
.set_description("Send copy-object progress info after these many bytes"),
+ Option("rgw_copy_verify_object", Option::TYPE_BOOL, Option::LEVEL_ADVANCED)
+ .set_default(false)
+ .set_description("Verify if the object copied is identical to its source")
+ .set_long_description(
+ "If true, this option computes the MD5 checksum of the data which is written at the"
+ "destination and checks if it is identical to the ETAG stored in the source."),
+
Option("rgw_obj_tombstone_cache_size", Option::TYPE_INT, Option::LEVEL_ADVANCED)
.set_default(1000)
.set_description("Max number of entries to keep in tombstone cache")
rgw::putobj::ObjectProcessor *processor;
void (*progress_cb)(off_t, void *);
void *progress_data;
- bufferlist extra_data_bl;
+ bufferlist extra_data_bl, full_obj;
uint64_t extra_data_left{0};
bool need_to_process_attrs{true};
uint64_t data_len{0};
uint64_t ofs{0};
uint64_t lofs{0}; /* logical ofs */
std::function<int(map<string, bufferlist>&)> attrs_handler;
+ string calculated_etag;
public:
RGWRadosPutObj(CephContext* cct,
CompressorRef& plugin,
const uint64_t lofs = data_len;
data_len += size;
+ if (cct->_conf->rgw_copy_verify_object)
+ full_obj.append(bl);
return filter->process(std::move(bl), lofs);
}
uint64_t get_data_len() {
return data_len;
}
+
+ string get_calculated_etag() {
+ MD5 hash;
+ unsigned char m[CEPH_CRYPTO_MD5_DIGESTSIZE];
+ char calc_md5[CEPH_CRYPTO_MD5_DIGESTSIZE * 2 + 1];
+
+ hash.Update((const unsigned char *)full_obj.c_str(), data_len);
+ hash.Final(m);
+ buf_to_hex(m, CEPH_CRYPTO_MD5_DIGESTSIZE, calc_md5);
+ calculated_etag = calc_md5;
+ return calculated_etag;
+ }
};
/*
if (ret < 0) {
goto set_err_state;
}
+
+ if (cct->_conf->rgw_copy_verify_object) {
+ if (cb.get_calculated_etag().compare(etag)) {
+ ret = -EIO;
+ ldout(cct, 0) << "ERROR: source and destination objects don't match. Expected etag:"
+ << etag << " Computed etag:" << cb.get_calculated_etag() << dendl;
+ goto set_err_state;
+ }
+ }
+
if (copy_if_newer && canceled) {
ldout(cct, 20) << "raced with another write of obj: " << dest_obj << dendl;
obj_ctx.invalidate(dest_obj); /* object was overwritten */