int RGWGetObj::handle_slo_manifest(bufferlist& bl)
{
  RGWSLOInfo slo_info;
  bufferlist::iterator bliter = bl.begin();
  try {
    decode(slo_info, bliter);
  } catch (buffer::error& err) {
    ldout(s->cct, 0) << "ERROR: failed to decode slo manifest" << dendl;
    return -EIO;
  }
  ldout(s->cct, 2) << "RGWGetObj::handle_slo_manifest()" << dendl;

  vector<RGWAccessControlPolicy> allocated_acls;
  map<string, pair<RGWAccessControlPolicy *, boost::optional<Policy>>> policies;
  map<string, rgw_bucket> buckets;

  map<uint64_t, rgw_slo_part> slo_parts;

  MD5 etag_sum;
  total_len = 0;

  for (const auto& entry : slo_info.entries) {
    const string& path = entry.path;

    /* If the path starts with slashes, strip them all. */
    const size_t pos_init = path.find_first_not_of('/');
    /* According to the documentation of std::string::find following check
     * is not necessary as we should get the std::string::npos propagation
     * here. This might be true with the accuracy to implementation's bugs.
     * See following question on SO:
     * http://stackoverflow.com/questions/1011790/why-does-stdstring-findtext-stdstringnpos-not-return-npos
     */
    if (pos_init == string::npos) {
      return -EINVAL;
    }

    const size_t pos_sep = path.find('/', pos_init);
    if (pos_sep == string::npos) {
      return -EINVAL;
    }

    string bucket_name = path.substr(pos_init, pos_sep - pos_init);
    string obj_name = path.substr(pos_sep + 1);

    rgw_bucket bucket;
    RGWAccessControlPolicy *bucket_acl;
    Policy* bucket_policy;

    if (bucket_name.compare(s->bucket.name) != 0) {
      const auto& piter = policies.find(bucket_name);
      if (piter != policies.end()) {
        bucket_acl = piter->second.first;
        bucket_policy = piter->second.second.get_ptr();
	bucket = buckets[bucket_name];
      } else {
	allocated_acls.push_back(RGWAccessControlPolicy(s->cct));
	RGWAccessControlPolicy& _bucket_acl = allocated_acls.back();

        RGWBucketInfo bucket_info;
        map<string, bufferlist> bucket_attrs;
        RGWObjectCtx obj_ctx(store);
        int r = store->get_bucket_info(obj_ctx, s->user->user_id.tenant,
                                       bucket_name, bucket_info, nullptr,
                                       &bucket_attrs);
        if (r < 0) {
          ldout(s->cct, 0) << "could not get bucket info for bucket="
			   << bucket_name << dendl;
          return r;
        }
        bucket = bucket_info.bucket;
        bucket_acl = &_bucket_acl;
        r = read_bucket_policy(store, s, bucket_info, bucket_attrs, bucket_acl,
                               bucket);
        if (r < 0) {
          ldout(s->cct, 0) << "failed to read bucket ACL for bucket "
                           << bucket << dendl;
          return r;
	}
	auto _bucket_policy = get_iam_policy_from_attr(
	  s->cct, store, bucket_attrs, bucket_info.bucket.tenant);
        bucket_policy = _bucket_policy.get_ptr();
	buckets[bucket_name] = bucket;
        policies[bucket_name] = make_pair(bucket_acl, _bucket_policy);
      }
    } else {
      bucket = s->bucket;
      bucket_acl = s->bucket_acl.get();
      bucket_policy = s->iam_policy.get_ptr();
    }

    rgw_slo_part part;
    part.bucket_acl = bucket_acl;
    part.bucket_policy = bucket_policy;
    part.bucket = bucket;
    part.obj_name = obj_name;
    part.size = entry.size_bytes;
    part.etag = entry.etag;
    ldout(s->cct, 20) << "slo_part: ofs=" << ofs
                      << " bucket=" << part.bucket
                      << " obj=" << part.obj_name
                      << " size=" << part.size
                      << " etag=" << part.etag
                      << dendl;

    etag_sum.Update((const unsigned char *)entry.etag.c_str(),
                    entry.etag.length());

    slo_parts[total_len] = part;
    total_len += part.size;
  }

  complete_etag(etag_sum, &lo_etag);

  s->obj_size = slo_info.total_size;
  ldout(s->cct, 20) << "s->obj_size=" << s->obj_size << dendl;

  int r = RGWRados::Object::Read::range_to_ofs(total_len, ofs, end);
  if (r < 0) {
    return r;
  }

  total_len = end - ofs + 1;

  r = iterate_slo_parts(s->cct, store, ofs, end, slo_parts,
        get_obj_user_manifest_iterate_cb, (void *)this);
  if (r < 0) {
    return r;
  }

  return 0;
}