rgw_rest_user.cc
rgw_role.cc
rgw_swift_auth.cc
+ rgw_tag.cc
+ rgw_tag_s3.cc
rgw_tools.cc
rgw_usage.cc
rgw_user.cc
"response-content-language",
"response-content-type",
"response-expires",
+ "tagging",
"torrent",
"uploadId",
"uploads",
{ ERR_TOO_MANY_BUCKETS, {400, "TooManyBuckets" }},
{ ERR_MALFORMED_XML, {400, "MalformedXML" }},
{ ERR_AMZ_CONTENT_SHA256_MISMATCH, {400, "XAmzContentSHA256Mismatch" }},
+ { ERR_INVALID_TAG, {400, "InvalidTag"}},
{ ERR_LENGTH_REQUIRED, {411, "MissingContentLength" }},
{ EACCES, {403, "AccessDenied" }},
{ EPERM, {403, "AccessDenied" }},
{ ERR_USER_EXIST, {409, "UserAlreadyExists" }},
{ ERR_EMAIL_EXIST, {409, "EmailExists" }},
{ ERR_KEY_EXIST, {409, "KeyExists"}},
+ { ERR_TAG_CONFLICT, {409, "OperationAborted"}},
{ ERR_INVALID_SECRET_KEY, {400, "InvalidSecretKey"}},
{ ERR_INVALID_KEY_TYPE, {400, "InvalidKeyType"}},
{ ERR_INVALID_CAP, {400, "InvalidCapability"}},
(name.compare("versioning") == 0) ||
(name.compare("website") == 0) ||
(name.compare("requestPayment") == 0) ||
- (name.compare("torrent") == 0)) {
+ (name.compare("torrent") == 0) ||
+ (name.compare("tagging") == 0)) {
sub_resources[name] = val;
} else if (name[0] == 'r') { // root of all evil
if ((name.compare("response-content-type") == 0) ||
#define RGW_AMZ_PREFIX "x-amz-"
#define RGW_AMZ_META_PREFIX RGW_AMZ_PREFIX "meta-"
#define RGW_AMZ_WEBSITE_REDIRECT_LOCATION RGW_AMZ_PREFIX "website-redirect-location"
+#define RGW_AMZ_TAG_COUNT RGW_AMZ_PREFIX "tagging-count"
#define RGW_SYS_PARAM_PREFIX "rgwx-"
#define RGW_ATTR_PG_VER RGW_ATTR_PREFIX "pg_ver"
#define RGW_ATTR_SOURCE_ZONE RGW_ATTR_PREFIX "source_zone"
+#define RGW_ATTR_TAGS RGW_ATTR_PREFIX RGW_AMZ_PREFIX "tagging"
#define RGW_ATTR_TEMPURL_KEY1 RGW_ATTR_META_PREFIX "temp-url-key"
#define RGW_ATTR_TEMPURL_KEY2 RGW_ATTR_META_PREFIX "temp-url-key-2"
#define ERR_DELETE_CONFLICT 2206
#define ERR_NO_SUCH_BUCKET_POLICY 2207
#define ERR_INVALID_LOCATION_CONSTRAINT 2208
+#define ERR_TAG_CONFLICT 2209
+#define ERR_INVALID_TAG 2210
#define ERR_BUSY_RESHARDING 2300
RGW_OP_PUT_BUCKET_POLICY,
RGW_OP_GET_BUCKET_POLICY,
RGW_OP_DELETE_BUCKET_POLICY,
-
+ RGW_OP_PUT_OBJ_TAGGING,
+ RGW_OP_GET_OBJ_TAGGING,
+ RGW_OP_DELETE_OBJ_TAGGING,
/* rgw specific */
RGW_OP_ADMIN_SET_METADATA,
RGW_OP_GET_OBJ_LAYOUT,
extern void url_encode(const std::string& src,
string& dst);
extern std::string url_encode(const std::string& src);
-
/* destination should be CEPH_CRYPTO_HMACSHA1_DIGESTSIZE bytes long */
extern void calc_hmac_sha1(const char *key, int key_len,
const char *msg, int msg_len, char *dest);
#include "rgw_client_io.h"
#include "rgw_compression.h"
#include "rgw_role.h"
+#include "rgw_tag_s3.h"
#include "cls/lock/cls_lock_client.h"
#include "cls/rgw/cls_rgw_client.h"
return 0;
}
+int RGWGetObjTags::verify_permission()
+{
+ if (!verify_object_permission(s, RGW_PERM_READ))
+ return -EACCES;
+
+ return 0;
+}
+
+void RGWGetObjTags::pre_exec(){
+ rgw_bucket_object_pre_exec(s);
+}
+
+void RGWGetObjTags::execute()
+{
+ rgw_obj obj;
+ map<string,bufferlist> attrs;
+
+ obj = rgw_obj(s->bucket, s->object);
+
+ store->set_atomic(s->obj_ctx, obj);
+
+ op_ret = get_obj_attrs(store, s, obj, attrs);
+ auto tags = attrs.find(RGW_ATTR_TAGS);
+ if(tags != attrs.end()){
+ has_tags = true;
+ tags_bl.append(tags->second);
+ }
+ send_response_data(tags_bl);
+}
+
+int RGWPutObjTags::verify_permission()
+{
+ if (!verify_object_permission(s, RGW_PERM_WRITE)) {
+ return -EACCES;
+ }
+ return 0;
+}
+
+void RGWPutObjTags::execute()
+{
+
+ op_ret = get_params();
+ if (op_ret < 0)
+ return;
+
+
+ if (s->object.empty()){
+ op_ret= -EINVAL; // we only support tagging on existing objects
+ return;
+ }
+
+ rgw_obj obj;
+ obj = rgw_obj(s->bucket, s->object);
+ store->set_atomic(s->obj_ctx, obj);
+ op_ret = modify_obj_attr(store, s, obj, RGW_ATTR_TAGS, tags_bl);
+ if (op_ret == -ECANCELED){
+ op_ret = -ERR_TAG_CONFLICT;
+ }
+}
+
+void RGWDeleteObjTags::pre_exec(){
+ rgw_bucket_object_pre_exec(s);
+}
+
+
+int RGWDeleteObjTags::verify_permission(){
+
+ if (!s->object.empty()){
+ if(!verify_object_permission(s, RGW_PERM_WRITE)) {
+ return -EACCES;
+ }
+ }
+ return 0;
+}
+
+void RGWDeleteObjTags::execute() {
+ if (s->object.empty())
+ return;
+
+ rgw_obj obj;
+ obj = rgw_obj(s->bucket, s->object);
+ store->set_atomic(s->obj_ctx, obj);
+ map <string, bufferlist> attrs;
+ map <string, bufferlist> rmattr;
+ bufferlist bl;
+ rmattr[RGW_ATTR_TAGS] = bl;
+ op_ret = store->set_attrs(s->obj_ctx, s->bucket_info, obj, attrs, &rmattr);
+}
+
int RGWOp::do_aws4_auth_completion()
{
ldout(s->cct, 5) << "NOTICE: call to do_aws4_auth_completion" << dendl;
populate_with_generic_attrs(s, attrs);
rgw_get_request_metadata(s->cct, s->info, attrs);
encode_delete_at_attr(delete_at, attrs);
+ encode_obj_tags_attr(obj_tags.get(), attrs);
/* Add a custom metadata to expose the information whether an object
* is an SLO or not. Appending the attribute must be performed AFTER
#include "rgw_lc.h"
#include "rgw_torrent.h"
+#include "rgw_tag.h"
#include "include/assert.h"
}
};
+class RGWGetObjTags : public RGWOp {
+ protected:
+ bufferlist tags_bl;
+ bool has_tags{false};
+ public:
+ int verify_permission();
+ void execute();
+ void pre_exec();
+
+ virtual void send_response_data(bufferlist& bl) = 0;
+ virtual const string name() noexcept override { return "get_obj_tags"; }
+ virtual uint32_t op_mask() { return RGW_OP_TYPE_READ; }
+ RGWOpType get_type() { return RGW_OP_GET_OBJ_TAGGING; }
+
+};
+
+class RGWPutObjTags : public RGWOp {
+ protected:
+ bufferlist tags_bl;
+ public:
+ int verify_permission();
+ void execute();
+
+ virtual void send_response() = 0;
+ virtual int get_params() = 0;
+ virtual const string name() { return "put_obj_tags"; }
+ virtual uint32_t op_mask() { return RGW_OP_TYPE_WRITE; }
+ RGWOpType get_type() { return RGW_OP_PUT_OBJ_TAGGING; }
+
+};
+
+class RGWDeleteObjTags: public RGWOp {
+ public:
+ void pre_exec();
+ int verify_permission();
+ void execute();
+
+ virtual void send_response() = 0;
+ virtual const string name() { return "delete_obj_tags"; }
+ virtual uint32_t op_mask() { return RGW_OP_TYPE_DELETE; }
+ RGWOpType get_type() { return RGW_OP_DELETE_OBJ_TAGGING;}
+};
+
class RGWBulkDelete : public RGWOp {
public:
struct acct_path_t {
string etag;
bool chunked_upload;
RGWAccessControlPolicy policy;
+ std::unique_ptr <RGWObjTags> obj_tags;
const char *dlo_manifest;
RGWSLOInfo *slo_info;
map<string, bufferlist> attrs;
attrs[RGW_ATTR_DELETE_AT] = delatbl;
} /* encode_delete_at_attr */
+static inline void encode_obj_tags_attr(RGWObjTags* obj_tags, map<string, bufferlist>& attrs)
+{
+ if (obj_tags == nullptr){
+ // we assume the user submitted a tag format which we couldn't parse since
+ // this wouldn't be parsed later by get/put obj tags, lets delete if the
+ // attr was populated
+ return;
+ }
+
+ bufferlist tagsbl;
+ obj_tags->encode(tagsbl);
+ attrs[RGW_ATTR_TAGS] = tagsbl;
+}
+
static inline int encode_dlo_manifest_attr(const char * const dlo_manifest,
map<string, bufferlist>& attrs)
{
only_bucket = true;
break;
case OP_DELETE:
- only_bucket = true;
+ if (!s->info.args.exists("tagging")){
+ only_bucket = true;
+ }
break;
case OP_OPTIONS:
only_bucket = true;
int get_params() override;
};
+class RGWGetObjTags_ObjStore : public RGWGetObjTags {
+public:
+ RGWGetObjTags_ObjStore() {};
+ ~RGWGetObjTags_ObjStore() {};
+};
+
+class RGWPutObjTags_ObjStore: public RGWPutObjTags {
+public:
+ RGWPutObjTags_ObjStore() {};
+ ~RGWPutObjTags_ObjStore() {};
+};
+
class RGWListBuckets_ObjStore : public RGWListBuckets {
public:
RGWListBuckets_ObjStore() {}
#include "common/utf8.h"
#include "common/ceph_json.h"
#include "common/safe_io.h"
+#include "common/backport14.h"
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/utility/string_view.hpp>
#include "rgw_user.h"
#include "rgw_cors.h"
#include "rgw_cors_s3.h"
+#include "rgw_tag_s3.h"
#include "rgw_client_io.h"
/* User custom metadata. */
name += sizeof(RGW_ATTR_PREFIX) - 1;
dump_header(s, name, iter->second);
+ } else if (iter->first.compare(RGW_ATTR_TAGS) == 0) {
+ RGWObjTags obj_tags;
+ try{
+ bufferlist::iterator it = iter->second.begin();
+ obj_tags.decode(it);
+ } catch (buffer::error &err) {
+ ldout(s->cct,0) << "Error caught buffer::error couldn't decode TagSet " << dendl;
+ }
+ dump_header(s, RGW_AMZ_TAG_COUNT, obj_tags.count());
}
}
}
return res;
}
+void RGWGetObjTags_ObjStore_S3::send_response_data(bufferlist& bl)
+{
+ dump_errno(s);
+ end_header(s, this, "application/xml");
+ dump_start(s);
+
+ s->formatter->open_object_section_in_ns("Tagging", XMLNS_AWS_S3);
+ s->formatter->open_object_section("TagSet");
+ if (has_tags){
+ RGWObjTagSet_S3 tagset;
+ bufferlist::iterator iter = bl.begin();
+ try {
+ tagset.decode(iter);
+ } catch (buffer::error& err) {
+ ldout(s->cct,0) << "ERROR: caught buffer::error, couldn't decode TagSet" << dendl;
+ op_ret= -EIO;
+ return;
+ }
+ tagset.dump_xml(s->formatter);
+ }
+ s->formatter->close_section();
+ s->formatter->close_section();
+ rgw_flush_formatter_and_reset(s, s->formatter);
+}
+
+
+int RGWPutObjTags_ObjStore_S3::get_params()
+{
+ RGWObjTagsXMLParser parser;
+
+ if (!parser.init()){
+ return -EINVAL;
+ }
+
+ char *data=nullptr;
+ int len=0;
+
+ const auto max_size = s->cct->_conf->rgw_max_put_param_size;
+ int r = rgw_rest_read_all_input(s, &data, &len, max_size, false);
+
+ if (r < 0)
+ return r;
+
+ auto data_deleter = std::unique_ptr<char, decltype(free)*>{data, free};
+
+ if (!parser.parse(data, len, 1)) {
+ return -ERR_MALFORMED_XML;
+ }
+
+ RGWObjTagSet_S3 *obj_tags_s3;
+ RGWObjTagging_S3 *tagging;
+
+ tagging = static_cast<RGWObjTagging_S3 *>(parser.find_first("Tagging"));
+ obj_tags_s3 = static_cast<RGWObjTagSet_S3 *>(tagging->find_first("TagSet"));
+ if(!obj_tags_s3){
+ return -ERR_MALFORMED_XML;
+ }
+
+ RGWObjTags obj_tags;
+ r = obj_tags_s3->rebuild(obj_tags);
+ if (r < 0)
+ return r;
+
+ obj_tags.encode(tags_bl);
+ ldout(s->cct, 20) << "Read " << obj_tags.count() << "tags" << dendl;
+
+ return 0;
+}
+
+void RGWPutObjTags_ObjStore_S3::send_response()
+{
+ if (op_ret)
+ set_req_state_err(s, op_ret);
+ dump_errno(s);
+ end_header(s, this, "application/xml");
+ dump_start(s);
+
+}
+
+void RGWDeleteObjTags_ObjStore_S3::send_response()
+{
+ int r = op_ret;
+ if (r == -ENOENT)
+ r = 0;
+ if (!r)
+ r = STATUS_NO_CONTENT;
+
+ set_req_state_err(s, r);
+ dump_errno(s);
+ end_header(s, this);
+}
void RGWListBuckets_ObjStore_S3::send_response_begin(bool has_buckets)
{
} /* copy_source */
+ /* handle object tagging */
+ auto tag_str = s->info.env->get("HTTP_X_AMZ_TAGGING");
+ if (tag_str){
+ obj_tags = ceph::make_unique<RGWObjTags>();
+ ret = obj_tags->set_from_string(tag_str);
+ if (ret < 0){
+ ldout(s->cct,0) << "setting obj tags failed with " << ret << dendl;
+ if (ret == -ERR_INVALID_TAG){
+ ret = -EINVAL; //s3 returns only -EINVAL for PUT requests
+ }
+
+ return ret;
+ }
+ }
+
return RGWPutObj_ObjStore::get_params();
}
if (r < 0)
return r;
+ r = get_tags();
+ if (r < 0)
+ return r;
+
+
min_len = post_policy.min_length;
max_len = post_policy.max_length;
+
+
+ return 0;
+}
+
+int RGWPostObj_ObjStore_S3::get_tags()
+{
+ string tags_str;
+ if (part_str(parts, "tagging", &tags_str)) {
+ RGWObjTagsXMLParser parser;
+ if (!parser.init()){
+ ldout(s->cct, 0) << "Couldn't init RGWObjTags XML parser" << dendl;
+ err_msg = "Server couldn't process the request";
+ return -EINVAL; // TODO: This class of errors in rgw code should be a 5XX error
+ }
+ if (!parser.parse(tags_str.c_str(), tags_str.size(), 1)) {
+ ldout(s->cct,0 ) << "Invalid Tagging XML" << dendl;
+ err_msg = "Invalid Tagging XML";
+ return -EINVAL;
+ }
+
+ RGWObjTagSet_S3 *obj_tags_s3;
+ RGWObjTagging_S3 *tagging;
+
+ tagging = static_cast<RGWObjTagging_S3 *>(parser.find_first("Tagging"));
+ obj_tags_s3 = static_cast<RGWObjTagSet_S3 *>(tagging->find_first("TagSet"));
+ if(!obj_tags_s3){
+ return -ERR_MALFORMED_XML;
+ }
+
+ RGWObjTags obj_tags;
+ int r = obj_tags_s3->rebuild(obj_tags);
+ if (r < 0)
+ return r;
+
+ bufferlist tags_bl;
+ obj_tags.encode(tags_bl);
+ ldout(s->cct, 20) << "Read " << obj_tags.count() << "tags" << dendl;
+ attrs[RGW_ATTR_TAGS] = tags_bl;
+ }
+
+
return 0;
}
return new RGWListMultipart_ObjStore_S3;
} else if (s->info.args.exists("layout")) {
return new RGWGetObjLayout_ObjStore_S3;
+ } else if (is_tagging_op()) {
+ return new RGWGetObjTags_ObjStore_S3;
}
return get_obj_op(true);
}
{
if (is_acl_op()) {
return new RGWPutACLs_ObjStore_S3;
+ } else if (is_tagging_op()) {
+ return new RGWPutObjTags_ObjStore_S3;
}
+
if (s->init_state.src_bucket.empty())
return new RGWPutObj_ObjStore_S3;
else
RGWOp *RGWHandler_REST_Obj_S3::op_delete()
{
+ if (is_tagging_op()) {
+ return new RGWDeleteObjTags_ObjStore_S3;
+ }
string upload_id = s->info.args.get("uploadId");
if (upload_id.empty())
bufferlist* manifest_bl) override;
};
+class RGWGetObjTags_ObjStore_S3 : public RGWGetObjTags_ObjStore
+{
+ bufferlist tags_bl;
+public:
+ RGWGetObjTags_ObjStore_S3() {}
+ ~RGWGetObjTags_ObjStore_S3() {}
+
+ void send_response_data(bufferlist &bl) override;
+};
+
+class RGWPutObjTags_ObjStore_S3 : public RGWPutObjTags_ObjStore
+{
+public:
+ RGWPutObjTags_ObjStore_S3() {}
+ ~RGWPutObjTags_ObjStore_S3() {}
+
+ int get_params() override;
+ void send_response() override;
+};
+
+class RGWDeleteObjTags_ObjStore_S3 : public RGWDeleteObjTags
+{
+public:
+ ~RGWDeleteObjTags_ObjStore_S3() override {}
+ void send_response() override;
+};
+
class RGWListBuckets_ObjStore_S3 : public RGWListBuckets_ObjStore {
public:
RGWListBuckets_ObjStore_S3() {}
const rgw::auth::StrategyRegistry* auth_registry_ptr = nullptr;
int get_policy();
+ int get_tags();
void rebuild_key(string& key);
std::string get_current_filename() const override;
bool is_cors_op() {
return s->info.args.exists("cors");
}
+ bool is_tagging_op() {
+ return s->info.args.exists("tagging");
+ }
bool is_obj_update_op() override {
- return is_acl_op();
+ return is_acl_op() || is_tagging_op() ;
}
RGWOp *get_obj_op(bool get_data);
--- /dev/null
+
+#include <map>
+#include <string>
+
+#include <common/errno.h>
+#include <boost/algorithm/string.hpp>
+
+#include "rgw_tag.h"
+
+static constexpr uint32_t MAX_OBJ_TAGS=10;
+static constexpr uint32_t MAX_TAG_KEY_SIZE=128;
+static constexpr uint32_t MAX_TAG_VAL_SIZE=256;
+
+bool RGWObjTags::add_tag(const string&key, const string& val){
+ return tags.emplace(std::make_pair(key,val)).second;
+}
+
+int RGWObjTags::check_and_add_tag(const string&key, const string& val){
+ if (tags.size() == MAX_OBJ_TAGS ||
+ key.size() > MAX_TAG_KEY_SIZE ||
+ val.size() > MAX_TAG_VAL_SIZE ||
+ key.size() == 0){
+ return -ERR_INVALID_TAG;
+ }
+
+ // if we get a conflicting key, either the XML is malformed or the user
+ // supplied an invalid string
+ if (!add_tag(key,val))
+ return -EINVAL;
+
+ return 0;
+}
+
+int RGWObjTags::set_from_string(const string& input){
+ int ret=0;
+ vector <string> kvs;
+ boost::split(kvs, input, boost::is_any_of("&"));
+ for (const auto& kv: kvs){
+ auto p = kv.find("=");
+ string key,val;
+ if (p != string::npos) {
+ ret = check_and_add_tag(url_decode(kv.substr(0,p)),
+ url_decode(kv.substr(p+1)));
+ } else {
+ ret = check_and_add_tag(url_decode(kv));
+ }
+
+ if (ret < 0)
+ return ret;
+ }
+ return ret;
+}
--- /dev/null
+#ifndef RGW_TAG_H
+#define RGW_TAG_H
+
+#include <map>
+#include <string>
+#include <include/types.h>
+
+#include "rgw_common.h"
+
+class RGWObjTags
+{
+ protected:
+ std::map <std::string, std::string> tags;
+ public:
+ RGWObjTags() {}
+ ~RGWObjTags() {}
+
+ void encode(bufferlist& bl) const {
+ ENCODE_START(1,1,bl);
+ ::encode(tags, bl);
+ ENCODE_FINISH(bl);
+ }
+
+ void decode(bufferlist::iterator &bl) {
+ DECODE_START_LEGACY_COMPAT_LEN(1, 1, 1, bl);
+ ::decode(tags,bl);
+ DECODE_FINISH(bl);
+ }
+
+ void dump(Formatter *f) const;
+ bool add_tag(const std::string& key, const std::string& val="");
+ int check_and_add_tag(const std::string& key, const std::string& val="");
+ size_t count() const {return tags.size();}
+ int set_from_string(const std::string& input);
+ const map <std::string,std::string>& get_tags() const {return tags;}
+};
+WRITE_CLASS_ENCODER(RGWObjTags)
+
+#endif /* RGW_TAG_H */
--- /dev/null
+#include <map>
+#include <string>
+#include <iostream>
+
+#include "include/types.h"
+
+#include "rgw_tag_s3.h"
+
+bool RGWObjTagEntry_S3::xml_end(const char*){
+ RGWObjTagKey_S3 *key_obj = static_cast<RGWObjTagKey_S3 *>(find_first("Key"));
+ RGWObjTagValue_S3 *val_obj = static_cast<RGWObjTagValue_S3 *>(find_first("Value"));
+
+ if (!key_obj)
+ return false;
+
+ string s = key_obj->get_data();
+ if (s.empty()){
+ return false;
+ }
+
+ key = s;
+ if (val_obj) {
+ val = val_obj->get_data();
+ }
+
+ return true;
+}
+
+bool RGWObjTagSet_S3::xml_end(const char*){
+ XMLObjIter iter = find("Tag");
+ RGWObjTagEntry_S3 *tagentry = static_cast<RGWObjTagEntry_S3 *>(iter.get_next());
+ while (tagentry) {
+ const std::string& key = tagentry->get_key();
+ const std::string& val = tagentry->get_val();
+ if (!add_tag(key,val))
+ return false;
+
+ tagentry = static_cast<RGWObjTagEntry_S3 *>(iter.get_next());
+ }
+ return true;
+}
+
+int RGWObjTagSet_S3::rebuild(RGWObjTags& dest){
+ int ret;
+ for (const auto &it: tags){
+ ret = dest.check_and_add_tag(it.first, it.second);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+bool RGWObjTagging_S3::xml_end(const char*){
+ RGWObjTagSet_S3 *tagset = static_cast<RGWObjTagSet_S3 *> (find_first("TagSet"));
+ return tagset != nullptr;
+
+}
+
+void RGWObjTagSet_S3::dump_xml(Formatter *f){
+ for (const auto& tag: tags){
+ f->open_object_section("Tag");
+ f->dump_string("Key", tag.first);
+ f->dump_string("Value", tag.second);
+ f->close_section();
+ }
+}
+
+XMLObj *RGWObjTagsXMLParser::alloc_obj(const char *el){
+ XMLObj* obj = nullptr;
+ if(strcmp(el,"Tagging") == 0) {
+ obj = new RGWObjTagging_S3();
+ } else if (strcmp(el,"TagSet") == 0) {
+ obj = new RGWObjTagSet_S3();
+ } else if (strcmp(el,"Tag") == 0) {
+ obj = new RGWObjTagEntry_S3();
+ } else if (strcmp(el,"Key") == 0) {
+ obj = new RGWObjTagKey_S3();
+ } else if (strcmp(el,"Value") == 0) {
+ obj = new RGWObjTagValue_S3();
+ }
+
+ return obj;
+}
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef RGW_TAG_S3_H
+#define RGW_TAG_S3_H
+
+#include <map>
+#include <string>
+#include <iostream>
+#include <include/types.h>
+#include <common/Formatter.h>
+#include <expat.h>
+
+#include "rgw_tag.h"
+#include "rgw_xml.h"
+
+struct RGWObjTagKey_S3: public XMLObj
+{
+};
+
+struct RGWObjTagValue_S3: public XMLObj
+{
+};
+
+class RGWObjTagEntry_S3: public XMLObj
+{
+ std::string key;
+ std::string val;
+public:
+ RGWObjTagEntry_S3() {}
+ RGWObjTagEntry_S3(std::string k,std::string v):key(k),val(v) {};
+ ~RGWObjTagEntry_S3() {}
+
+ bool xml_end(const char*) override;
+ const std::string& get_key () const { return key;}
+ const std::string& get_val () const { return val;}
+ //void to_xml(CephContext *cct, ostream& out) const;
+};
+
+class RGWObjTagSet_S3: public RGWObjTags, public XMLObj
+{
+public:
+ bool xml_end(const char*) override;
+ void dump_xml(Formatter *f);
+ int rebuild(RGWObjTags& dest);
+};
+
+class RGWObjTagging_S3: public XMLObj
+{
+public:
+ bool xml_end(const char*) override;
+};
+
+class RGWObjTagsXMLParser : public RGWXMLParser
+{
+ XMLObj *alloc_obj(const char *el);
+public:
+ RGWObjTagsXMLParser() {}
+ ~RGWObjTagsXMLParser() {}
+};
+
+#endif /* RGW_TAG_S3_H */