static RGWObjCategory main_category = RGWObjCategory::Main;
#define RGW_USAGE_OBJ_PREFIX "usage."
+// reads attribute as std::string
+static inline void read_attr(std::map<std::string, bufferlist>& attrs,
+ const std::string& attr_name,
+ std::string& dest,
+ bool* found = nullptr) {
+ auto i = attrs.find(attr_name);
+ if (i != attrs.end()) {
+ dest = rgw_bl_str(i->second);
+ }
+ if (found) *found = i != attrs.end();
+}
+
+// reads attribute as bufferlist
+static inline void read_attr(std::map<std::string, bufferlist>& attrs,
+ const std::string& attr_name,
+ bufferlist& dest,
+ bool* found = nullptr) {
+ auto i = attrs.find(attr_name);
+ if (i != attrs.cend()) {
+ dest = i->second; // copy
+ }
+ if (found) {
+ *found = i != attrs.end();
+ }
+}
+
rgw_raw_obj rgw_obj_select::get_raw_obj(RGWRados* store) const
{
if (!is_raw) {
attrset, 0, real_time(), NULL, dpp, y);
}
-int RGWRados::reindex_obj(const RGWBucketInfo& bucket_info,
- const rgw_obj& obj,
+
+int RGWRados::reindex_obj(rgw::sal::Driver* driver,
+ RGWBucketInfo& bucket_info,
+ const rgw_obj& head_obj,
const DoutPrefixProvider* dpp,
optional_yield y)
{
- if (bucket_info.versioned()) {
- ldpp_dout(dpp, 10) << "WARNING: " << __func__ <<
- ": cannot process versioned bucket \"" <<
- bucket_info.bucket.get_key() << "\"" <<
- dendl;
- return -ENOTSUP;
+ // used for trimming pending entries; max value means all versions trimmed
+ const uint64_t max_ver = std::numeric_limits<uint64_t>::max();
+ // used for linking an olh
+ const std::string empty_op_tag = "";
+
+ int ret;
+ RGWObjectCtx obj_ctx(driver);
+
+ // aids in printing out name of bucket/object
+ auto p = [](const rgw_obj& o) -> std::string {
+ std::stringstream ss;
+ ss << o.bucket.name << ':' << o.key;
+ return ss.str();
+ };
+
+ // since the code for linking a versioned object and adding a delete
+ // marker is so similar, we bring the common OLH-handling code into
+ // this lambda
+ auto link_helper = [&](const bool is_delete_marker,
+ rgw_bucket_dir_entry_meta& meta,
+ const std::string& log_tag) -> int {
+ int ret = 0;
+
+ // convert the head object name into the OLH object by removing
+ // the instance info
+ rgw_obj olh_obj = head_obj;
+ olh_obj.key.instance.clear();
+
+ RGWObjState* olh_state { nullptr };
+ RGWObjManifest* olh_manifest { nullptr }; // we don't use, but must send in
+ ret = get_obj_state(dpp, &obj_ctx, bucket_info, olh_obj,
+ &olh_state, &olh_manifest,
+ false, // don't follow olh
+ y);
+ if (ret < 0) {
+ ldpp_dout(dpp, 0) << "ERROR: " << __func__ <<
+ ": during " << log_tag << " get_obj_state on OLH object " <<
+ olh_obj.key << " returned: " << cpp_strerror(-ret) << dendl;
+ return ret;
+ }
+
+ // In order to update the data in the OLH object we're calling
+ // bucket_index_link_olh followed by bucket_index_trim_olh_log
+ // since that churns metadata less than a call to set_olh
+ // would. bucket_index_link_olh does leave entries in the OLH
+ // object's pending log since normally OLH updates are paired with
+ // other ops, but we remove such entries below.
+ ret = bucket_index_link_olh(dpp,
+ bucket_info,
+ *olh_state,
+ head_obj,
+ is_delete_marker,
+ empty_op_tag,
+ &meta,
+ 0, // zero olh_epoch means calculated in CLS
+ ceph::real_clock::zero(), // unmod_since
+ true, // high_precision_time
+ y,
+ nullptr, // zones trace
+ false); // log data change
+ if (ret < 0) {
+ ldpp_dout(dpp, 0) << "ERROR: " << __func__ <<
+ ": during " << log_tag << " set_index_link_olh returned: " <<
+ cpp_strerror(-ret) << dendl;
+ return ret;
+ }
+
+ // bucket_)index_link_olh leaves a pending_log entry in the OLH;
+ // this trims it out
+ ret = bucket_index_trim_olh_log(dpp,
+ bucket_info,
+ *olh_state,
+ head_obj,
+ max_ver,
+ y);
+ if (ret < 0) {
+ ldpp_dout(dpp, 0) << "ERROR: " << __func__ <<
+ ": during " << log_tag <<
+ " bucket_index_trim_olh_log returned: " <<
+ cpp_strerror(-ret) << dendl;
+ return ret;
+ }
+
+ return 0;
+ }; // link_helper lambda
+
+ librados::IoCtx head_obj_ctx;
+ ret = get_obj_head_ioctx(dpp, bucket_info, head_obj, &head_obj_ctx);
+ if (ret < 0) {
+ ldpp_dout(dpp, 0) << "ERROR: " << __func__ <<
+ ": get_obj_head_ioctx for " << p(head_obj) << " returned: " <<
+ cpp_strerror(-ret) << dendl;
+ return ret;
}
- Bucket target(this, bucket_info);
- RGWRados::Bucket::UpdateIndex update_idx(&target, obj);
- const std::string* no_write_tag = nullptr;
+ const int64_t pool_id = head_obj_ctx.get_id();
+ const bool is_versioned = bucket_info.versioned();
+ const bool has_instance = ! head_obj.key.instance.empty();
+
+ ldpp_dout(dpp, 20) << "INFO: " << __func__ << ": reindexing " <<
+ p(head_obj) << dendl;
+
+ RGWObjState *head_state { nullptr };
+ RGWObjManifest *head_manifest { nullptr };
- int ret = update_idx.prepare(dpp, RGWModifyOp::CLS_RGW_OP_ADD, no_write_tag, y);
+ // if head_obj does not exist does not return -ENOENT but instead
+ // sets head_state->exists to false
+ ret = get_obj_state(dpp, &obj_ctx, bucket_info, head_obj,
+ &head_state, &head_manifest,
+ false, // don't follow olh
+ y);
if (ret < 0) {
ldpp_dout(dpp, 0) << "ERROR: " << __func__ <<
- ": update index prepare for \"" << obj << "\" returned: " <<
+ ": get_obj_state on " << p(head_obj) << " returned: " <<
cpp_strerror(-ret) << dendl;
return ret;
}
- return 0;
-}
+ if (! head_state->exists && is_versioned && has_instance) {
+ // head object does not exist if it's a delete marker; handle here
+ // and return
+ ldpp_dout(dpp, 20) << "INFO: " << __func__ << ": indexing " <<
+ p(head_obj) << " as delete marker" << dendl;
+
+ // empty metadata object is fine for delete marker
+ rgw_bucket_dir_entry_meta meta;
+
+ return link_helper(true, meta, "set delete marker");
+ } else if (ret < 0) {
+ ldpp_dout(dpp, 0) << "ERROR: " << __func__ <<
+ ": unable to complete stat of " << p(head_obj) << "; returned: " <<
+ cpp_strerror(-ret) << dendl;
+ return ret;
+ }
+
+ // data we'll pull from head object xattrs
+ std::string etag;
+ std::string content_type;
+ std::string storage_class;
+ bufferlist acl_bl;
+ bool found_olh_info { false };
+ bufferlist olh_info_bl;
+ bool appendable { false };
+ bufferlist part_num_bl;
+
+ rgw::sal::Attrs& attr_set = head_state->attrset;
+ read_attr(attr_set, RGW_ATTR_ETAG, etag);
+ read_attr(attr_set, RGW_ATTR_CONTENT_TYPE, content_type);
+ read_attr(attr_set, RGW_ATTR_STORAGE_CLASS, storage_class);
+ read_attr(attr_set, RGW_ATTR_ACL, acl_bl);
+ read_attr(attr_set, RGW_ATTR_OLH_INFO, olh_info_bl, &found_olh_info);
+ read_attr(attr_set, RGW_ATTR_APPEND_PART_NUM, part_num_bl, &appendable);
+
+ // check for a pure OLH object and if so exit early
+ if (found_olh_info) {
+ try {
+ auto iter = olh_info_bl.cbegin();
+ RGWOLHInfo info;
+ decode(info, iter);
+ if (! info.target.key.instance.empty()) {
+ // since there is a listed instance this appears to be a pure
+ // OLH (i.e., no data); we won't index as we index actual
+ // objects with data and set the OLH then
+ ldpp_dout(dpp, 20) << "INFO: " << __func__ << ": " <<
+ p(head_obj) << " appears to be a pure OLH object; ignoring" << dendl;
+ return 0;
+ }
+ } catch (buffer::error& err) {
+ ldpp_dout(dpp, 0) << "ERROR: " << __func__ <<
+ ": unable to decode OLH info for " << p(head_obj) << dendl;
+ return -EIO;
+ }
+ }
+
+ Bucket bkt(this, bucket_info);
+ RGWRados::Bucket::UpdateIndex update_idx(&bkt, head_obj);
+
+ // note: we can skip calling prepare() since there's no transaction
+ // and we don't specify a write tag (i.e., transaction tag)
+ ret = update_idx.complete(dpp,
+ pool_id,
+ 0, // bucket index epoch
+ head_state->size,
+ head_state->accounted_size,
+ head_state->mtime,
+ etag,
+ content_type,
+ storage_class,
+ &acl_bl,
+ RGWObjCategory::Main, // RGWObjCategory category,
+ nullptr, // remove_objs list
+ y,
+ nullptr, // user data string
+ appendable);
+ if (ret < 0) {
+ ldpp_dout(dpp, 0) << "ERROR: " << __func__ <<
+ ": update index complete for " << p(head_obj) << " returned: " <<
+ cpp_strerror(-ret) << dendl;
+ return ret;
+ }
+
+ if (bucket_info.versioned()) {
+ ldpp_dout(dpp, 20) << "INFO: " << __func__ << ": since " <<
+ bucket_info.bucket << " appears to be versioned, setting OLH for " <<
+ p(head_obj) << dendl;
+
+ // write OLH and instance entries
+ rgw_bucket_dir_entry_meta meta;
+ meta.category = RGWObjCategory::Main;
+ meta.mtime = head_state->mtime;
+ meta.size = head_state->size;
+ meta.accounted_size = head_state->accounted_size;
+ meta.etag = etag;
+ meta.content_type = content_type;
+ meta.appendable = appendable;
+
+ ret = link_helper(false, meta, "linking version");
+ } // if bucket is versioned
+
+ return ret;
+} // RGWRados::reindex_obj
+
struct obj_time_weight {
real_time mtime;
return r;
}
return 0;
-}
+} // RGWRados::repair_olh
int RGWRados::bucket_index_trim_olh_log(const DoutPrefixProvider *dpp,
RGWBucketInfo& bucket_info,
/* update olh object */
r = rgw_rados_operate(dpp, ref.pool.ioctx(), ref.obj.oid, &op, y);
if (r < 0) {
- ldpp_dout(dpp, 0) << "ERROR: could not apply olh update, r=" << r << dendl;
+ ldpp_dout(dpp, 0) << "ERROR: " << __func__ << ": could not apply olh update to oid \"" << ref.obj.oid << "\", r=" << r << dendl;
return r;
}
const rgw_obj& target_obj, bool delete_marker,
rgw_bucket_dir_entry_meta *meta,
uint64_t olh_epoch, real_time unmod_since, bool high_precision_time,
- optional_yield y, rgw_zone_set *zones_trace, bool log_data_change)
+ optional_yield y, rgw_zone_set *zones_trace, bool log_data_change,
+ bool skip_olh_obj_update)
{
string op_tag;
return -EIO;
}
+ // exit early if we're skipping the olh update and just updating the index
+ if (skip_olh_obj_update) {
+ return 0;
+ }
+
ret = update_olh(dpp, obj_ctx, state, bucket_info, olh_obj, y);
if (ret == -ECANCELED) { /* already did what we needed, no need to retry, raced with another user */
ret = 0;
return 0;
}
if (r < 0) {
- ldpp_dout(dpp, 0) << "ERROR: could not apply olh update, r=" << r << dendl;
+ ldpp_dout(dpp, 0) << "ERROR: " << __func__ << ": could not apply olh update to oid \"" << ref.obj.oid << "\", r=" << r << dendl;
return r;
}
}
D3nDataCache* d3n_data_cache{nullptr};
int rewrite_obj(RGWBucketInfo& dest_bucket_info, const rgw_obj& obj, const DoutPrefixProvider *dpp, optional_yield y);
- int reindex_obj(const RGWBucketInfo& dest_bucket_info,
+ int reindex_obj(rgw::sal::Driver* driver,
+ RGWBucketInfo& dest_bucket_info,
const rgw_obj& obj,
const DoutPrefixProvider* dpp,
optional_yield y);
int apply_olh_log(const DoutPrefixProvider *dpp, RGWObjectCtx& obj_ctx, RGWObjState& obj_state, RGWBucketInfo& bucket_info, const rgw_obj& obj,
bufferlist& obj_tag, std::map<uint64_t, std::vector<rgw_bucket_olh_log_entry> >& log,
uint64_t *plast_ver, optional_yield y, rgw_zone_set *zones_trace = nullptr);
- int update_olh(const DoutPrefixProvider *dpp, RGWObjectCtx& obj_ctx, RGWObjState *state, RGWBucketInfo& bucket_info, const rgw_obj& obj, optional_yield y, rgw_zone_set *zones_trace = nullptr);
+ int update_olh(const DoutPrefixProvider *dpp, RGWObjectCtx& obj_ctx, RGWObjState *state, RGWBucketInfo& bucket_info, const rgw_obj& obj, optional_yield y,
+ rgw_zone_set *zones_trace = nullptr);
int clear_olh(const DoutPrefixProvider *dpp,
RGWObjectCtx& obj_ctx,
const rgw_obj& obj,
const std::string& tag,
const uint64_t ver,
optional_yield y);
- int set_olh(const DoutPrefixProvider *dpp, RGWObjectCtx& obj_ctx, RGWBucketInfo& bucket_info, const rgw_obj& target_obj, bool delete_marker, rgw_bucket_dir_entry_meta *meta,
- uint64_t olh_epoch, ceph::real_time unmod_since, bool high_precision_time,
- optional_yield y, rgw_zone_set *zones_trace = nullptr, bool log_data_change = false);
+ int set_olh(const DoutPrefixProvider *dpp,
+ RGWObjectCtx& obj_ctx,
+ RGWBucketInfo& bucket_info,
+ const rgw_obj& target_obj,
+ bool delete_marker,
+ rgw_bucket_dir_entry_meta *meta,
+ uint64_t olh_epoch,
+ ceph::real_time unmod_since,
+ bool high_precision_time,
+ optional_yield y,
+ rgw_zone_set *zones_trace = nullptr,
+ bool log_data_change = false,
+ bool skip_olh_obj_update = false); // can skip the OLH object update if, for example, repairing index
int repair_olh(const DoutPrefixProvider *dpp, RGWObjState* state, const RGWBucketInfo& bucket_info,
const rgw_obj& obj, optional_yield y);
int unlink_obj_instance(const DoutPrefixProvider *dpp, RGWObjectCtx& obj_ctx, RGWBucketInfo& bucket_info, const rgw_obj& target_obj,