#endif
#define LIBRGW_FILE_VER_MAJOR 1
-#define LIBRGW_FILE_VER_MINOR 1
-#define LIBRGW_FILE_VER_EXTRA 7
+#define LIBRGW_FILE_VER_MINOR 2
+#define LIBRGW_FILE_VER_EXTRA 0
#define LIBRGW_FILE_VERSION(maj, min, extra) ((maj << 16) + (min << 8) + extra)
#define LIBRGW_FILE_VERSION_CODE LIBRGW_FILE_VERSION(LIBRGW_FILE_VER_MAJOR, LIBRGW_FILE_VER_MINOR, LIBRGW_FILE_VER_EXTRA)
int rgw_commit(struct rgw_fs *rgw_fs, struct rgw_file_handle *fh,
uint64_t offset, uint64_t length, uint32_t flags);
+/*
+ extended attributes
+ */
+typedef struct rgw_xattrstr
+{
+ char *val;
+ uint32_t len;
+} rgw_xattrstr;
+
+typedef struct rgw_xattr
+{
+ rgw_xattrstr key;
+ rgw_xattrstr val;
+} rgw_xattr;
+
+typedef struct rgw_xattrlist
+{
+ rgw_xattr *xattrs;
+ uint32_t xattr_cnt;
+} rgw_xattrlist;
+
+#define RGW_GETXATTR_FLAG_NONE 0x0000
+
+typedef int (*rgw_getxattr_cb)(rgw_xattrlist *attrs, void *arg,
+ uint32_t flags);
+
+int rgw_getxattrs(struct rgw_fs *rgw_fs, struct rgw_file_handle *fh,
+ rgw_xattrlist *attrs, rgw_getxattr_cb cb, void *cb_arg,
+ uint32_t flags);
+
+#define RGW_LSXATTR_FLAG_NONE 0x0000
+#define RGW_LSXATTR_FLAG_STOP 0x0001
+
+int rgw_lsxattrs(struct rgw_fs *rgw_fs, struct rgw_file_handle *fh,
+ rgw_xattrstr *filter_prefix /* unimplemented for now */,
+ rgw_getxattr_cb cb, void *cb_arg, uint32_t flags);
+
+#define RGW_SETXATTR_FLAG_NONE 0x0000
+
+int rgw_setxattrs(struct rgw_fs *rgw_fs, struct rgw_file_handle *fh,
+ rgw_xattrlist *attrs, uint32_t flags);
+
+#define RGW_RMXATTR_FLAG_NONE 0x0000
+
+int rgw_rmxattrs(struct rgw_fs *rgw_fs, struct rgw_file_handle *fh,
+ rgw_xattrlist *attrs, uint32_t flags);
+
#ifdef __cplusplus
}
#endif
return 0;
}
+ class XattrHash
+ {
+ public:
+ std::size_t operator()(const rgw_xattrstr& att) const noexcept {
+ return XXH64(att.val, att.len, 5882300);
+ }
+ };
+
+ class XattrEqual
+ {
+ public:
+ bool operator()(const rgw_xattrstr& lhs, const rgw_xattrstr& rhs) const {
+ return ((lhs.len == rhs.len) &&
+ (strncmp(lhs.val, rhs.val, lhs.len) == 0));
+ }
+ };
+
+ /* well-known attributes */
+ static const std::unordered_set<
+ rgw_xattrstr, XattrHash, XattrEqual> rgw_exposed_attrs = {
+ rgw_xattrstr{const_cast<char*>(RGW_ATTR_ETAG), sizeof(RGW_ATTR_ETAG)-1}
+ };
+
+ static inline bool is_exposed_attr(const rgw_xattrstr& k) {
+ return (rgw_exposed_attrs.find(k) != rgw_exposed_attrs.end());
+ }
+
LookupFHResult RGWLibFS::stat_bucket(RGWFileHandle* parent, const char *path,
RGWLibFS::BucketStats& bs,
uint32_t flags)
return 0;
} /* RGWLibFS::setattr */
- /* called under rgw_fh->mtx held */
+ static inline std::string prefix_xattr_keystr(const rgw_xattrstr& key) {
+ std::string keystr;
+ keystr.reserve(sizeof(RGW_ATTR_META_PREFIX) + key.len);
+ keystr += {RGW_ATTR_META_PREFIX};
+ keystr += string{key.val, key.len};
+ return keystr;
+ }
+
+ static inline std::string_view unprefix_xattr_keystr(const std::string& key)
+ {
+ std::string_view svk{key};
+ auto pos = svk.find(RGW_ATTR_META_PREFIX);
+ if (pos == std::string_view::npos) {
+ return std::string_view{""};
+ } else if (pos == 0) {
+ svk.remove_prefix(sizeof(RGW_ATTR_META_PREFIX)-1);
+ }
+ return svk;
+ }
+
+ int RGWLibFS::getxattrs(RGWFileHandle* rgw_fh, rgw_xattrlist *attrs,
+ rgw_getxattr_cb cb, void *cb_arg,
+ uint32_t flags)
+ {
+ /* cannot store on fs_root, should not on buckets? */
+ if ((rgw_fh->is_bucket()) ||
+ (rgw_fh->is_root())) {
+ return -EINVAL;
+ }
+
+ int rc, rc2, rc3;
+ string obj_name{rgw_fh->relative_object_name2()};
+
+ RGWGetAttrsRequest req(cct, rgwlib.get_store()->get_user(user.user_id),
+ rgw_fh->bucket_name(), obj_name);
+
+ for (uint32_t ix = 0; ix < attrs->xattr_cnt; ++ix) {
+ auto& xattr = attrs->xattrs[ix];
+
+ /* pass exposed attr keys as given, else prefix */
+ std::string k = is_exposed_attr(xattr.key)
+ ? std::string{xattr.key.val, xattr.key.len}
+ : prefix_xattr_keystr(xattr.key);
+
+ req.emplace_key(std::move(k));
+ }
+
+ if (ldlog_p1(get_context(), ceph_subsys_rgw, 15)) {
+ lsubdout(get_context(), rgw, 15)
+ << __func__
+ << " get keys for: "
+ << rgw_fh->object_name()
+ << " keys:"
+ << dendl;
+ for (const auto& attr: req.get_attrs()) {
+ lsubdout(get_context(), rgw, 15)
+ << "\tkey: " << attr.first << dendl;
+ }
+ }
+
+ rc = rgwlib.get_fe()->execute_req(&req);
+ rc2 = req.get_ret();
+ rc3 = ((rc == 0) && (rc2 == 0)) ? 0 : -EIO;
+
+ /* call back w/xattr data */
+ if (rc3 == 0) {
+ const auto& attrs = req.get_attrs();
+ for (const auto& attr : attrs) {
+
+ if (!attr.second.has_value())
+ continue;
+
+ const auto& k = attr.first;
+ const auto& v = attr.second.value();
+
+ /* return exposed attr keys as given, else unprefix --
+ * yes, we could have memoized the exposed check, but
+ * to be efficient it would need to be saved with
+ * RGWGetAttrs::attrs, I think */
+ std::string_view svk =
+ is_exposed_attr(rgw_xattrstr{const_cast<char*>(k.c_str()),
+ uint32_t(k.length())})
+ ? k
+ : unprefix_xattr_keystr(k);
+
+ /* skip entries not matching prefix */
+ if (svk.empty())
+ continue;
+
+ rgw_xattrstr xattr_k = { const_cast<char*>(svk.data()),
+ uint32_t(svk.length())};
+ rgw_xattrstr xattr_v =
+ {const_cast<char*>(const_cast<buffer::list&>(v).c_str()),
+ uint32_t(v.length())};
+ rgw_xattr xattr = { xattr_k, xattr_v };
+ rgw_xattrlist xattrlist = { &xattr, 1 };
+
+ cb(&xattrlist, cb_arg, RGW_GETXATTR_FLAG_NONE);
+ }
+ }
+
+ return rc3;
+ } /* RGWLibFS::getxattrs */
+
+ int RGWLibFS::lsxattrs(
+ RGWFileHandle* rgw_fh, rgw_xattrstr *filter_prefix, rgw_getxattr_cb cb,
+ void *cb_arg, uint32_t flags)
+ {
+ /* cannot store on fs_root, should not on buckets? */
+ if ((rgw_fh->is_bucket()) ||
+ (rgw_fh->is_root())) {
+ return -EINVAL;
+ }
+
+ int rc, rc2, rc3;
+ string obj_name{rgw_fh->relative_object_name2()};
+
+ RGWGetAttrsRequest req(cct, rgwlib.get_store()->get_user(user.user_id),
+ rgw_fh->bucket_name(), obj_name);
+
+ rc = rgwlib.get_fe()->execute_req(&req);
+ rc2 = req.get_ret();
+ rc3 = ((rc == 0) && (rc2 == 0)) ? 0 : -EIO;
+
+ /* call back w/xattr data--check for eof */
+ if (rc3 == 0) {
+ const auto& keys = req.get_attrs();
+ for (const auto& k : keys) {
+
+ /* return exposed attr keys as given, else unprefix */
+ std::string_view svk =
+ is_exposed_attr(rgw_xattrstr{const_cast<char*>(k.first.c_str()),
+ uint32_t(k.first.length())})
+ ? k.first
+ : unprefix_xattr_keystr(k.first);
+
+ /* skip entries not matching prefix */
+ if (svk.empty())
+ continue;
+
+ rgw_xattrstr xattr_k = { const_cast<char*>(svk.data()),
+ uint32_t(svk.length())};
+ rgw_xattrstr xattr_v = { nullptr, 0 };
+ rgw_xattr xattr = { xattr_k, xattr_v };
+ rgw_xattrlist xattrlist = { &xattr, 1 };
+
+ auto cbr = cb(&xattrlist, cb_arg, RGW_LSXATTR_FLAG_NONE);
+ if (cbr & RGW_LSXATTR_FLAG_STOP)
+ break;
+ }
+ }
+
+ return rc3;
+ } /* RGWLibFS::lsxattrs */
+
+ int RGWLibFS::setxattrs(RGWFileHandle* rgw_fh, rgw_xattrlist *attrs,
+ uint32_t flags)
+ {
+ /* cannot store on fs_root, should not on buckets? */
+ if ((rgw_fh->is_bucket()) ||
+ (rgw_fh->is_root())) {
+ return -EINVAL;
+ }
+
+ int rc, rc2;
+ string obj_name{rgw_fh->relative_object_name2()};
+
+ RGWSetAttrsRequest req(cct, rgwlib.get_store()->get_user(user.user_id),
+ rgw_fh->bucket_name(), obj_name);
+
+ for (uint32_t ix = 0; ix < attrs->xattr_cnt; ++ix) {
+ auto& xattr = attrs->xattrs[ix];
+ buffer::list attr_bl;
+ /* don't allow storing at RGW_ATTR_META_PREFIX */
+ if (! (xattr.key.len > 0))
+ continue;
+
+ /* reject lexical match with any exposed attr */
+ if (is_exposed_attr(xattr.key))
+ continue;
+
+ string k = prefix_xattr_keystr(xattr.key);
+ attr_bl.append(xattr.val.val, xattr.val.len);
+ req.emplace_attr(k.c_str(), std::move(attr_bl));
+ }
+
+ /* don't send null requests */
+ if (! (req.get_attrs().size() > 0)) {
+ return -EINVAL;
+ }
+
+ rc = rgwlib.get_fe()->execute_req(&req);
+ rc2 = req.get_ret();
+
+ return (((rc == 0) && (rc2 == 0)) ? 0 : -EIO);
+
+ } /* RGWLibFS::setxattrs */
+
+ int RGWLibFS::rmxattrs(RGWFileHandle* rgw_fh, rgw_xattrlist* attrs,
+ uint32_t flags)
+ {
+ /* cannot store on fs_root, should not on buckets? */
+ if ((rgw_fh->is_bucket()) ||
+ (rgw_fh->is_root())) {
+ return -EINVAL;
+ }
+
+ int rc, rc2;
+ string obj_name{rgw_fh->relative_object_name2()};
+
+ RGWRMAttrsRequest req(cct, rgwlib.get_store()->get_user(user.user_id),
+ rgw_fh->bucket_name(), obj_name);
+
+ for (uint32_t ix = 0; ix < attrs->xattr_cnt; ++ix) {
+ auto& xattr = attrs->xattrs[ix];
+ /* don't allow storing at RGW_ATTR_META_PREFIX */
+ if (! (xattr.key.len > 0)) {
+ continue;
+ }
+ string k = prefix_xattr_keystr(xattr.key);
+ req.emplace_key(std::move(k));
+ }
+
+ /* don't send null requests */
+ if (! (req.get_attrs().size() > 0)) {
+ return -EINVAL;
+ }
+
+ rc = rgwlib.get_fe()->execute_req(&req);
+ rc2 = req.get_ret();
+
+ return (((rc == 0) && (rc2 == 0)) ? 0 : -EIO);
+
+ } /* RGWLibFS::rmxattrs */
+
+ /* called with rgw_fh->mtx held */
void RGWLibFS::update_fh(RGWFileHandle *rgw_fh)
{
int rc, rc2;
return rgw_fh->commit(offset, length, RGWFileHandle::FLAG_NONE);
}
+/*
+ extended attributes
+ */
+
+int rgw_getxattrs(struct rgw_fs *rgw_fs, struct rgw_file_handle *fh,
+ rgw_xattrlist *attrs, rgw_getxattr_cb cb, void *cb_arg,
+ uint32_t flags)
+{
+ RGWLibFS *fs = static_cast<RGWLibFS*>(rgw_fs->fs_private);
+ RGWFileHandle* rgw_fh = get_rgwfh(fh);
+
+ return fs->getxattrs(rgw_fh, attrs, cb, cb_arg, flags);
+}
+
+int rgw_lsxattrs(struct rgw_fs *rgw_fs, struct rgw_file_handle *fh,
+ rgw_xattrstr *filter_prefix /* ignored */,
+ rgw_getxattr_cb cb, void *cb_arg, uint32_t flags)
+{
+ RGWLibFS *fs = static_cast<RGWLibFS*>(rgw_fs->fs_private);
+ RGWFileHandle* rgw_fh = get_rgwfh(fh);
+
+ return fs->lsxattrs(rgw_fh, filter_prefix, cb, cb_arg, flags);
+}
+
+int rgw_setxattrs(struct rgw_fs *rgw_fs, struct rgw_file_handle *fh,
+ rgw_xattrlist *attrs, uint32_t flags)
+{
+ RGWLibFS *fs = static_cast<RGWLibFS*>(rgw_fs->fs_private);
+ RGWFileHandle* rgw_fh = get_rgwfh(fh);
+
+ return fs->setxattrs(rgw_fh, attrs, flags);
+}
+
+int rgw_rmxattrs(struct rgw_fs *rgw_fs, struct rgw_file_handle *fh,
+ rgw_xattrlist *attrs, uint32_t flags)
+{
+ RGWLibFS *fs = static_cast<RGWLibFS*>(rgw_fs->fs_private);
+ RGWFileHandle* rgw_fh = get_rgwfh(fh);
+
+ return fs->rmxattrs(rgw_fh, attrs, flags);
+}
+
} /* extern "C" */
return full_object_name(true /* omit_bucket */);
}
+ inline std::string relative_object_name2() {
+ std::string rname = full_object_name(true /* omit_bucket */);
+ if (is_dir()) {
+ rname += "/";
+ }
+ return rname;
+ }
+
inline std::string format_child_name(const std::string& cbasename,
bool is_dir) const {
std::string child_name{relative_object_name()};
int setattr(RGWFileHandle* rgw_fh, struct stat* st, uint32_t mask,
uint32_t flags);
+ int getxattrs(RGWFileHandle* rgw_fh, rgw_xattrlist* attrs,
+ rgw_getxattr_cb cb, void *cb_arg, uint32_t flags);
+
+ int lsxattrs(RGWFileHandle* rgw_fh, rgw_xattrstr *filter_prefix,
+ rgw_getxattr_cb cb, void *cb_arg, uint32_t flags);
+
+ int setxattrs(RGWFileHandle* rgw_fh, rgw_xattrlist* attrs, uint32_t flags);
+
+ int rmxattrs(RGWFileHandle* rgw_fh, rgw_xattrlist* attrs, uint32_t flags);
+
void update_fh(RGWFileHandle *rgw_fh);
LookupFHResult stat_bucket(RGWFileHandle* parent, const char *path,
}; /* RGWCopyObjRequest */
+class RGWGetAttrsRequest : public RGWLibRequest,
+ public RGWGetAttrs /* RGWOp */
+{
+public:
+ const std::string& bucket_name;
+ const std::string& obj_name;
+
+ RGWGetAttrsRequest(CephContext* _cct,
+ std::unique_ptr<rgw::sal::RGWUser> _user,
+ const std::string& _bname, const std::string& _oname)
+ : RGWLibRequest(_cct, std::move(_user)), RGWGetAttrs(),
+ bucket_name(_bname), obj_name(_oname) {
+ op = this;
+ }
+
+ const flat_map<std::string, std::optional<buffer::list>>& get_attrs() {
+ return attrs;
+ }
+
+ virtual bool only_bucket() { return false; }
+
+ virtual int op_init() {
+ // assign store, s, and dialect_handler
+ RGWObjectCtx* rados_ctx
+ = static_cast<RGWObjectCtx*>(get_state()->obj_ctx);
+ // framework promises to call op_init after parent init
+ assert(rados_ctx);
+ RGWOp::init(rados_ctx->get_store(), get_state(), this);
+ op = this; // assign self as op: REQUIRED
+ return 0;
+ }
+
+ virtual int header_init() {
+
+ struct req_state* s = get_state();
+ s->info.method = "GET";
+ s->op = OP_GET;
+
+ std::string uri = make_uri(bucket_name, obj_name);
+ s->relative_uri = uri;
+ s->info.request_uri = uri;
+ s->info.effective_uri = uri;
+ s->info.request_params = "";
+ s->info.domain = ""; /* XXX ? */
+
+ return 0;
+ }
+
+ virtual int get_params() {
+ return 0;
+ }
+
+ virtual void send_response() {}
+
+}; /* RGWGetAttrsRequest */
+
class RGWSetAttrsRequest : public RGWLibRequest,
public RGWSetAttrs /* RGWOp */
{
op = this;
}
+ const std::map<std::string, buffer::list>& get_attrs() {
+ return attrs;
+ }
+
bool only_bucket() override { return false; }
int op_init() override {
}; /* RGWSetAttrsRequest */
+class RGWRMAttrsRequest : public RGWLibRequest,
+ public RGWRMAttrs /* RGWOp */
+{
+public:
+ const std::string& bucket_name;
+ const std::string& obj_name;
+
+ RGWRMAttrsRequest(CephContext* _cct,
+ std::unique_ptr<rgw::sal::RGWUser> _user,
+ const std::string& _bname, const std::string& _oname)
+ : RGWLibRequest(_cct, std::move(_user)), RGWRMAttrs(),
+ bucket_name(_bname), obj_name(_oname) {
+ op = this;
+ }
+
+ const rgw::sal::RGWAttrs& get_attrs() {
+ return attrs;
+ }
+
+ virtual bool only_bucket() { return false; }
+
+ virtual int op_init() {
+ // assign store, s, and dialect_handler
+ RGWObjectCtx* rados_ctx
+ = static_cast<RGWObjectCtx*>(get_state()->obj_ctx);
+ // framework promises to call op_init after parent init
+ assert(rados_ctx);
+ RGWOp::init(rados_ctx->get_store(), get_state(), this);
+ op = this; // assign self as op: REQUIRED
+ return 0;
+ }
+
+ virtual int header_init() {
+
+ struct req_state* s = get_state();
+ s->info.method = "DELETE";
+ s->op = OP_PUT;
+
+ std::string uri = make_uri(bucket_name, obj_name);
+ s->relative_uri = uri;
+ s->info.request_uri = uri;
+ s->info.effective_uri = uri;
+ s->info.request_params = "";
+ s->info.domain = ""; /* XXX ? */
+
+ return 0;
+ }
+
+ virtual int get_params() {
+ return 0;
+ }
+
+ virtual void send_response() {}
+
+}; /* RGWRMAttrsRequest */
+
/*
* Send request to get the rados cluster stats
*/
s->object->set_atomic(s->obj_ctx);
- op_ret = s->object->get_obj_attrs(s->obj_ctx, s->yield);
+ op_ret = s->object->get_obj_attrs(s->obj_ctx, y);
if (op_ret < 0) {
ldpp_dout(this, 0) << "ERROR: failed to get obj attrs, obj=" << s->object
<< " ret=" << op_ret << dendl;
}
s->object->set_atomic(s->obj_ctx);
- op_ret = s->object->modify_obj_attrs(s->obj_ctx, RGW_ATTR_TAGS, tags_bl, s->yield);
+ op_ret = s->object->modify_obj_attrs(s->obj_ctx, RGW_ATTR_TAGS, tags_bl, y);
if (op_ret == -ECANCELED){
op_ret = -ERR_TAG_CONFLICT;
}
if (rgw::sal::RGWObject::empty(s->object.get()))
return;
- op_ret = s->object->delete_obj_attrs(s->obj_ctx, RGW_ATTR_TAGS, s->yield);
+ op_ret = s->object->delete_obj_attrs(s->obj_ctx, RGW_ATTR_TAGS, y);
}
int RGWGetBucketTags::verify_permission(optional_yield y)
ldpp_dout(this, 0) << "forward_request_to_master returned ret=" << op_ret << dendl;
}
- op_ret = retry_raced_bucket_write(s->bucket.get(), [this] {
+ op_ret = retry_raced_bucket_write(s->bucket.get(), [this, y] {
rgw::sal::RGWAttrs attrs = s->bucket->get_attrs();
attrs[RGW_ATTR_TAGS] = tags_bl;
- return s->bucket->set_instance_attrs(attrs, s->yield);
+ return s->bucket->set_instance_attrs(attrs, y);
});
}
return;
}
- op_ret = retry_raced_bucket_write(s->bucket.get(), [this] {
+ op_ret = retry_raced_bucket_write(s->bucket.get(), [this, y] {
rgw::sal::RGWAttrs attrs = s->bucket->get_attrs();
attrs.erase(RGW_ATTR_TAGS);
- op_ret = s->bucket->set_instance_attrs(attrs, s->yield);
+ op_ret = s->bucket->set_instance_attrs(attrs, y);
if (op_ret < 0) {
ldpp_dout(this, 0) << "RGWDeleteBucketTags() failed to remove RGW_ATTR_TAGS on bucket="
<< s->bucket->get_name()
rgw::sal::RGWBucket::ListResults results;
- op_ret = s->bucket->list(params, max, results, s->yield);
+ op_ret = s->bucket->list(params, max, results, y);
if (op_ret >= 0) {
next_marker = results.next_marker;
is_truncated = results.is_truncated;
}
op_ret = store->ctl()->bucket->link_bucket(s->user->get_id(), s->bucket->get_key(),
- s->bucket->get_creation_time(), s->yield, false);
+ s->bucket->get_creation_time(), y, false);
if (op_ret && !existed && op_ret != -EEXIST) {
/* if it exists (or previously existed), don't remove it! */
- op_ret = store->ctl()->bucket->unlink_bucket(s->user->get_id(), s->bucket->get_key(), s->yield);
+ op_ret = store->ctl()->bucket->unlink_bucket(s->user->get_id(), s->bucket->get_key(), y);
if (op_ret < 0) {
ldpp_dout(this, 0) << "WARNING: failed to unlink bucket: ret=" << op_ret
<< dendl;
do {
map<string, bufferlist> battrs;
- op_ret = s->bucket->get_bucket_info(s->yield);
+ op_ret = s->bucket->get_bucket_info(y);
if (op_ret < 0) {
return;
} else if (!s->bucket->is_owner(s->user.get())) {
/* This will also set the quota on the bucket. */
op_ret = store->ctl()->bucket->set_bucket_instance_attrs(s->bucket->get_info(), attrs,
&s->bucket->get_info().objv_tracker,
- s->yield);
+ y);
} while (op_ret == -ECANCELED && tries++ < 20);
/* Restore the proper return code. */
ldpp_dout(this, 1) << "WARNING: failed to sync user stats before bucket delete: op_ret= " << op_ret << dendl;
}
- op_ret = s->bucket->check_empty(s->yield);
+ op_ret = s->bucket->check_empty(y);
if (op_ret < 0) {
return;
}
}
}
- op_ret = s->bucket->remove_bucket(false, prefix, delimiter, false, nullptr, s->yield);
-
+ op_ret = s->bucket->remove_bucket(false, prefix, delimiter, false, nullptr,
+ y);
if (op_ret < 0 && op_ret == -ECANCELED) {
// lost a race, either with mdlog sync or another delete bucket operation.
// in either case, we've already called ctl.bucket->unlink_bucket()
}
std::unique_ptr<rgw::sal::RGWBucket> bucket;
ret = store->get_bucket(s->user.get(), copy_source_tenant_name, copy_source_bucket_name,
- &bucket, s->yield);
+ &bucket, y);
if (ret < 0) {
ldpp_dout(this, 5) << __func__ << "(): get_bucket() returned ret=" << ret << dendl;
return ret;
}
- ret = bucket->get_bucket_info(s->yield);
+ ret = bucket->get_bucket_info(y);
if (ret < 0) {
ldpp_dout(this, 5) << __func__ << "(): get_bucket_info() returned ret=" << ret << dendl;
return ret;
return len;
}
+int RGWGetAttrs::verify_permission(optional_yield y)
+{
+ s->object->set_atomic(s->obj_ctx);
+
+ auto iam_action = s->object->get_instance().empty() ?
+ rgw::IAM::s3GetObject :
+ rgw::IAM::s3GetObjectVersion;
+
+ if (!verify_object_permission(this, s, iam_action)) {
+ return -EACCES;
+ }
+
+ return 0;
+}
+
+void RGWGetAttrs::pre_exec()
+{
+ rgw_bucket_object_pre_exec(s);
+}
+
+void RGWGetAttrs::execute(optional_yield y)
+{
+ op_ret = get_params();
+ if (op_ret < 0)
+ return;
+
+ s->object->set_atomic(s->obj_ctx);
+
+ op_ret = s->object->get_obj_attrs(s->obj_ctx, s->yield);
+ if (op_ret < 0) {
+ ldpp_dout(this, 0) << "ERROR: failed to get obj attrs, obj=" << s->object
+ << " ret=" << op_ret << dendl;
+ return;
+ }
+
+ /* XXX RGWObject::get_obj_attrs() does not support filtering (yet) */
+ auto& obj_attrs = s->object->get_attrs();
+ if (attrs.size() != 0) {
+ /* return only attrs requested */
+ for (auto& att : attrs) {
+ auto iter = obj_attrs.find(att.first);
+ if (iter != obj_attrs.end()) {
+ att.second = iter->second;
+ }
+ }
+ } else {
+ /* return all attrs */
+ for (auto& att : obj_attrs) {
+ attrs.insert(get_attrs_t::value_type(att.first, att.second));;
+ }
+ }
+
+ return;
+ }
+
+int RGWRMAttrs::verify_permission(optional_yield y)
+{
+ // This looks to be part of the RGW-NFS machinery and has no S3 or
+ // Swift equivalent.
+ bool perm;
+ if (!rgw::sal::RGWObject::empty(s->object.get())) {
+ perm = verify_object_permission_no_policy(this, s, RGW_PERM_WRITE);
+ } else {
+ perm = verify_bucket_permission_no_policy(this, s, RGW_PERM_WRITE);
+ }
+ if (!perm)
+ return -EACCES;
+
+ return 0;
+}
+
+void RGWRMAttrs::pre_exec()
+{
+ rgw_bucket_object_pre_exec(s);
+}
+
+void RGWRMAttrs::execute(optional_yield y)
+{
+ op_ret = get_params();
+ if (op_ret < 0)
+ return;
+
+ s->object->set_atomic(s->obj_ctx);
+
+ op_ret = s->object->set_obj_attrs(s->obj_ctx, nullptr, &attrs, y);
+ if (op_ret < 0) {
+ ldpp_dout(this, 0) << "ERROR: failed to delete obj attrs, obj=" << s->object
+ << " ret=" << op_ret << dendl;
+ }
+ return;
+}
+
int RGWSetAttrs::verify_permission(optional_yield y)
{
// This looks to be part of the RGW-NFS machinery and has no S3 or
if (!rgw::sal::RGWObject::empty(s->object.get())) {
rgw::sal::RGWAttrs a(attrs);
- op_ret = s->object->set_obj_attrs(s->obj_ctx, &a, nullptr, s->yield);
+ op_ret = s->object->set_obj_attrs(s->obj_ctx, &a, nullptr, y);
} else {
for (auto& iter : attrs) {
s->bucket_attrs[iter.first] = std::move(iter.second);
}
- op_ret = store->ctl()->bucket->set_bucket_instance_attrs(s->bucket->get_info(), attrs,
- &s->bucket->get_info().objv_tracker,
- s->yield);
+ op_ret = store->ctl()->bucket->set_bucket_instance_attrs(
+ s->bucket->get_info(), attrs, &s->bucket->get_info().objv_tracker,
+ s->yield);
}
-}
+
+} /* RGWSetAttrs::execute() */
void RGWGetObjLayout::pre_exec()
{
std::unique_ptr<rgw::sal::RGWObject::ReadOp> stat_op(s->object->get_read_op(s->obj_ctx));
- op_ret = stat_op->prepare(s->yield);
+ op_ret = stat_op->prepare(y);
if (op_ret < 0) {
return;
}
head_obj = stat_op->result.head_obj;
- op_ret = stat_op->get_manifest(&manifest, s->yield);
+ op_ret = stat_op->get_manifest(&manifest, y);
}
#include <boost/optional.hpp>
#include <boost/utility/in_place_factory.hpp>
#include <boost/function.hpp>
+#include <boost/container/flat_map.hpp>
#include "common/armor.h"
#include "common/mime.h"
*etag = etag_buf_str;
} /* complete_etag */
+using boost::container::flat_map;
+
+class RGWGetAttrs : public RGWOp {
+public:
+ using get_attrs_t = flat_map<std::string, std::optional<buffer::list>>;
+protected:
+ get_attrs_t attrs;
+
+public:
+ RGWGetAttrs()
+ {}
+
+ virtual ~RGWGetAttrs() {}
+
+ void emplace_key(std::string&& key) {
+ attrs.emplace(std::move(key), std::nullopt);
+ }
+
+ int verify_permission(optional_yield y);
+ void pre_exec();
+ void execute(optional_yield y);
+
+ virtual int get_params() = 0;
+ virtual void send_response() = 0;
+ virtual const char* name() const { return "get_attrs"; }
+ virtual RGWOpType get_type() { return RGW_OP_GET_ATTRS; }
+ virtual uint32_t op_mask() { return RGW_OP_TYPE_READ; }
+}; /* RGWGetAttrs */
+
class RGWSetAttrs : public RGWOp {
protected:
- map<string, buffer::list> attrs;
+ map<std::string, buffer::list> attrs;
public:
RGWSetAttrs() {}
uint32_t op_mask() override { return RGW_OP_TYPE_WRITE; }
};
+class RGWRMAttrs : public RGWOp {
+protected:
+ rgw::sal::RGWAttrs attrs;
+
+public:
+ RGWRMAttrs()
+ {}
+
+ virtual ~RGWRMAttrs() {}
+
+ void emplace_key(std::string&& key) {
+ attrs.emplace(std::move(key), buffer::list());
+ }
+
+ int verify_permission(optional_yield y);
+ void pre_exec();
+ void execute(optional_yield y);
+
+ virtual int get_params() = 0;
+ virtual void send_response() = 0;
+ virtual const char* name() const { return "rm_attrs"; }
+ virtual RGWOpType get_type() { return RGW_OP_DELETE_ATTRS; }
+ virtual uint32_t op_mask() { return RGW_OP_TYPE_DELETE; }
+}; /* RGWRMAttrs */
+
class RGWGetObjLayout : public RGWOp {
protected:
RGWObjManifest *manifest{nullptr};
RGW_OP_LIST_BUCKET_MULTIPARTS,
RGW_OP_DELETE_MULTI_OBJ,
RGW_OP_BULK_DELETE,
+ RGW_OP_GET_KEYS,
+ RGW_OP_GET_ATTRS,
+ RGW_OP_DELETE_ATTRS,
RGW_OP_SET_ATTRS,
RGW_OP_GET_CROSS_DOMAIN_POLICY,
RGW_OP_GET_HEALTH_CHECK,
)
target_link_libraries(ceph_test_librgw_file_marker spawn)
+# ceph_test_librgw_file_xattr (attribute ops)
+add_executable(ceph_test_librgw_file_xattr
+ librgw_file_xattr.cc
+ )
+target_include_directories(ceph_test_librgw_file_xattr SYSTEM PRIVATE "${CMAKE_SOURCE_DIR}/src/rgw")
+target_link_libraries(ceph_test_librgw_file_xattr
+ rgw
+ librados
+ ceph-common
+ ${UNITTEST_LIBS}
+ ${EXTRALIBS}
+ )
+target_link_libraries(ceph_test_librgw_file_xattr spawn)
+
# ceph_test_rgw_token
add_executable(ceph_test_rgw_token
test_rgw_token.cc
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <stdint.h>
+#include <tuple>
+#include <iostream>
+#include <vector>
+#include <map>
+#include <random>
+#include <boost/algorithm/string.hpp>
+#include "xxhash.h"
+
+#include "include/rados/librgw.h"
+#include "include/rados/rgw_file.h"
+#include "rgw/rgw_file.h"
+
+#include "gtest/gtest.h"
+#include "common/ceph_argparse.h"
+#include "common/errno.h"
+#include "common/debug.h"
+#include "global/global_init.h"
+#include "include/ceph_assert.h"
+
+#define dout_context g_ceph_context
+#define dout_subsys ceph_subsys_rgw
+
+namespace {
+
+ using namespace rgw;
+
+ string uid("testuser");
+ string access_key("");
+ string secret_key("");
+
+ librgw_t rgw_h = nullptr;
+ struct rgw_fs *fs = nullptr;
+
+ uint32_t owner_uid = 867;
+ uint32_t owner_gid = 5309;
+ uint32_t create_mask = RGW_SETATTR_UID | RGW_SETATTR_GID | RGW_SETATTR_MODE;
+
+ string bucket_name{"v4recov"};
+ string object_path{"node0/clientids"};
+
+ string key1{"black"};
+ string val1{"metallic"};
+
+ struct rgw_file_handle *bucket_fh = nullptr;
+ struct rgw_file_handle *object_fh = nullptr;
+
+ typedef std::tuple<string,uint64_t, struct rgw_file_handle*> fid_type;
+ std::vector<fid_type> fids;
+
+ class obj_rec
+ {
+ public:
+ string name;
+ struct rgw_file_handle* fh;
+ struct rgw_file_handle* parent_fh;
+ RGWFileHandle* rgw_fh; // alias into fh
+
+ struct state {
+ bool readdir;
+ state() : readdir(false) {}
+ } state;
+
+ obj_rec(string _name, struct rgw_file_handle* _fh,
+ struct rgw_file_handle* _parent_fh, RGWFileHandle* _rgw_fh)
+ : name(std::move(_name)), fh(_fh), parent_fh(_parent_fh),
+ rgw_fh(_rgw_fh) {}
+
+ void clear() {
+ fh = nullptr;
+ rgw_fh = nullptr;
+ }
+
+ void sync() {
+ if (fh)
+ rgw_fh = get_rgwfh(fh);
+ }
+
+ friend ostream& operator<<(ostream& os, const obj_rec& rec);
+ };
+
+ ostream& operator<<(ostream& os, const obj_rec& rec)
+ {
+ RGWFileHandle* rgw_fh = rec.rgw_fh;
+ if (rgw_fh) {
+ const char* type = rgw_fh->is_dir() ? "DIR " : "FILE ";
+ os << rec.rgw_fh->full_object_name()
+ << " (" << rec.rgw_fh->object_name() << "): "
+ << type;
+ }
+ return os;
+ }
+
+ typedef std::vector<obj_rec> obj_vec;
+ obj_vec ovec;
+
+ bool do_stat = false;
+ bool do_create = false;
+ bool do_delete = false;
+ bool do_hexdump = false;
+ bool verbose = false;
+
+ struct {
+ int argc;
+ char **argv;
+ } saved_args;
+}
+
+TEST(LibRGW, INIT) {
+ int ret = librgw_create(&rgw_h, saved_args.argc, saved_args.argv);
+ ASSERT_EQ(ret, 0);
+ ASSERT_NE(rgw_h, nullptr);
+}
+
+TEST(LibRGW, MOUNT) {
+ int ret = rgw_mount(rgw_h, uid.c_str(), access_key.c_str(),
+ secret_key.c_str(), &fs, RGW_MOUNT_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+ ASSERT_NE(fs, nullptr);
+}
+
+TEST(LibRGW, LOOKUP_BUCKET) {
+ int ret = rgw_lookup(fs, fs->root_fh, bucket_name.c_str(), &bucket_fh,
+ nullptr, 0, RGW_LOOKUP_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+}
+
+TEST(LibRGW, CREATE_BUCKET) {
+ if ((! bucket_fh) && do_create) {
+ struct stat st;
+
+ st.st_uid = owner_uid;
+ st.st_gid = owner_gid;
+ st.st_mode = 755;
+
+ int ret = rgw_mkdir(fs, fs->root_fh, bucket_name.c_str(), &st, create_mask,
+ &bucket_fh, RGW_MKDIR_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+ }
+}
+
+TEST(LibRGW, CREATE_PATH) {
+
+ if (!bucket_fh)
+ return;
+
+ vector<string> segs;
+ boost::split(segs, object_path, boost::is_any_of("/"));
+
+ struct stat st;
+ st.st_uid = owner_uid;
+ st.st_gid = owner_gid;
+ st.st_mode = 755;
+
+ int ix, ret, sz = segs.size();
+ for (ix = 0; ix < sz; ++ix) {
+ auto& seg = segs[ix];
+ struct rgw_file_handle* parent_fh = (ix > 0) ? ovec[ix-1].fh : bucket_fh;
+ obj_rec dir{seg, nullptr, parent_fh, nullptr};
+ if (do_create) {
+ ret = rgw_mkdir(fs, dir.parent_fh, dir.name.c_str(), &st, create_mask,
+ &dir.fh, RGW_MKDIR_FLAG_NONE);
+ } else {
+ ret = rgw_lookup(fs, dir.parent_fh, dir.name.c_str(), &dir.fh,
+ nullptr, 0, RGW_LOOKUP_FLAG_NONE);
+ }
+ ASSERT_EQ(ret, 0);
+ dir.sync();
+ ovec.push_back(dir);
+ if (verbose) {
+ std::cout << "create: " << dir.name << std::endl;
+ }
+ }
+}
+
+TEST(LibRGW, CHECK_PATH_REFS) {
+
+ if (!bucket_fh)
+ return;
+
+ int ix, sz = ovec.size();
+ for (ix = 0; ix < sz; ++ix) {
+ auto& dir = ovec[ix];
+ if (verbose) {
+ std::cout << "name: " << dir.name
+ << " refcnt: " << dir.rgw_fh->get_refcnt()
+ << std::endl;
+ }
+ if (ix == 0) {
+ // sentinel, +1 parent, +1 path
+ ASSERT_EQ(dir.rgw_fh->get_refcnt(), 3U);
+ }
+ if (ix == 1) {
+ // sentinel, +1 path
+ ASSERT_EQ(dir.rgw_fh->get_refcnt(), 2U);
+ }
+ }
+}
+
+TEST(LibRGW, SETXATTR1) {
+
+ if (!bucket_fh)
+ return;
+
+ auto& dir = ovec[ovec.size()-1];
+ rgw_xattrstr xattr_k = { const_cast<char*>(key1.c_str()),
+ uint32_t(key1.length()) };
+ rgw_xattrstr xattr_v = { const_cast<char*>(val1.c_str()),
+ uint32_t(val1.length()) };
+
+ rgw_xattr xattr = { xattr_k, xattr_v };
+ rgw_xattrlist xattrlist = { &xattr, 1 };
+
+ int ret = rgw_setxattrs(fs, dir.fh, &xattrlist, RGW_SETXATTR_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+}
+
+extern "C" {
+ static int getattr_cb(rgw_xattrlist *attrs, void *arg, uint32_t flags)
+ {
+ auto& attrmap =
+ *(static_cast<std::map<std::string, std::string>*>(arg));
+ for (uint32_t ix = 0; ix < attrs->xattr_cnt; ++ix) {
+ auto& xattr = attrs->xattrs[ix];
+ string k{xattr.key.val, xattr.key.len};
+ string v{xattr.val.val, xattr.val.len};
+ if (verbose) {
+ std::cout << __func__
+ << " attr k: " << k << " v: " << v
+ << std::endl;
+ }
+ attrmap.insert(std::map<std::string, std::string>::value_type(k, v));
+ }
+ return 0;
+ }
+}
+
+TEST(LibRGW, GETXATTR1) {
+
+ if (!bucket_fh)
+ return;
+
+ using std::get;
+ auto& dir = ovec[ovec.size()-1];
+
+ rgw_xattrstr xattr_k1 =
+ {const_cast<char*>(key1.c_str()), uint32_t(key1.length())};
+ rgw_xattrstr xattr_v1 = {nullptr, 0};
+
+ std::string key2 = "user.rgw.etag";
+ rgw_xattrstr xattr_k2 =
+ {const_cast<char*>(key2.c_str()), uint32_t(key2.length())};
+ rgw_xattrstr xattr_v2 = {nullptr, 0};
+
+ rgw_xattr xattrs[2] = {{xattr_k1, xattr_v1},
+ {xattr_k2, xattr_v2}};
+
+ /* XXX gcc won't accept static_cast here, don't see why */
+ rgw_xattrlist xattrlist = {reinterpret_cast<rgw_xattr*>(&xattrs), 2};
+
+ std::map<std::string, std::string> out_attrmap;
+
+ int ret = rgw_getxattrs(fs, dir.fh, &xattrlist, getattr_cb, &out_attrmap,
+ RGW_GETXATTR_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+ /* check exposed attrs */
+ ASSERT_TRUE(out_attrmap.find("user.rgw.etag") != out_attrmap.end());
+ /* check our user attr */
+ ASSERT_TRUE(out_attrmap.find(key1) != out_attrmap.end());
+}
+
+TEST(LibRGW, LSXATTR1) {
+
+ if (!bucket_fh)
+ return;
+
+ using std::get;
+ auto& dir = ovec[ovec.size()-1];
+
+ rgw_xattrstr filter_prefix = { nullptr, 0}; // XXX ignored, for now
+
+ std::map<std::string, std::string> out_attrmap;
+
+ int ret = rgw_lsxattrs(fs, dir.fh, &filter_prefix, getattr_cb,
+ &out_attrmap, RGW_LSXATTR_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+
+ /* check exposed attrs */
+ ASSERT_TRUE(out_attrmap.find("user.rgw.etag") != out_attrmap.end());
+}
+
+TEST(LibRGW, RMXATTR1) {
+
+ if (!bucket_fh)
+ return;
+
+ using std::get;
+ auto& dir = ovec[ovec.size()-1];
+
+ rgw_xattrstr xattr_k = { const_cast<char*>(key1.c_str()),
+ uint32_t(key1.length()) };
+ rgw_xattrstr xattr_v = { nullptr, 0 };
+
+ rgw_xattr xattr = { xattr_k, xattr_v };
+ rgw_xattrlist xattrlist = { &xattr, 1 };
+
+ int ret = rgw_rmxattrs(fs, dir.fh, &xattrlist, RGW_RMXATTR_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+}
+
+TEST(LibRGW, LSXATTR2) {
+
+ if (!bucket_fh)
+ return;
+
+ using std::get;
+ auto& dir = ovec[ovec.size()-1];
+
+ rgw_xattrstr filter_prefix = { nullptr, 0 }; // XXX ignored, for now
+
+ std::map<std::string, std::string> out_attrmap;
+
+ int ret = rgw_lsxattrs(fs, dir.fh, &filter_prefix, getattr_cb,
+ &out_attrmap, RGW_LSXATTR_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+ /* check exposed attrs */
+ ASSERT_TRUE(out_attrmap.find("user.rgw.etag") != out_attrmap.end());
+ /* verify deletion */
+ ASSERT_TRUE(out_attrmap.find(key1) == out_attrmap.end());
+}
+
+TEST(LibRGW, CLEANUP) {
+ int ret = 0;
+ if (object_fh) {
+ ret = rgw_fh_rele(fs, object_fh, 0 /* flags */);
+ ASSERT_EQ(ret, 0);
+ }
+ if (bucket_fh) {
+ ret = rgw_fh_rele(fs, bucket_fh, 0 /* flags */);
+ }
+ ASSERT_EQ(ret, 0);
+}
+
+TEST(LibRGW, UMOUNT) {
+ if (! fs)
+ return;
+
+ int ret = rgw_umount(fs, RGW_UMOUNT_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+}
+
+TEST(LibRGW, SHUTDOWN) {
+ librgw_shutdown(rgw_h);
+}
+
+int main(int argc, char *argv[])
+{
+ char *v{nullptr};
+ string val;
+ vector<const char*> args;
+
+ argv_to_vec(argc, const_cast<const char**>(argv), args);
+ env_to_vec(args);
+
+ v = getenv("AWS_ACCESS_KEY_ID");
+ if (v) {
+ access_key = v;
+ }
+
+ v = getenv("AWS_SECRET_ACCESS_KEY");
+ if (v) {
+ secret_key = v;
+ }
+
+ for (auto arg_iter = args.begin(); arg_iter != args.end();) {
+ if (ceph_argparse_witharg(args, arg_iter, &val, "--access",
+ (char*) nullptr)) {
+ access_key = val;
+ } else if (ceph_argparse_witharg(args, arg_iter, &val, "--secret",
+ (char*) nullptr)) {
+ secret_key = val;
+ } else if (ceph_argparse_witharg(args, arg_iter, &val, "--uid",
+ (char*) nullptr)) {
+ uid = val;
+ } else if (ceph_argparse_witharg(args, arg_iter, &val, "--bn",
+ (char*) nullptr)) {
+ bucket_name = val;
+ } else if (ceph_argparse_flag(args, arg_iter, "--stat",
+ (char*) nullptr)) {
+ do_stat = true;
+ } else if (ceph_argparse_flag(args, arg_iter, "--create",
+ (char*) nullptr)) {
+ do_create = true;
+ } else if (ceph_argparse_flag(args, arg_iter, "--delete",
+ (char*) nullptr)) {
+ do_delete = true;
+ } else if (ceph_argparse_flag(args, arg_iter, "--hexdump",
+ (char*) nullptr)) {
+ do_hexdump = true;
+ } else if (ceph_argparse_flag(args, arg_iter, "--verbose",
+ (char*) nullptr)) {
+ verbose = true;
+ } else {
+ ++arg_iter;
+ }
+ }
+
+ /* dont accidentally run as anonymous */
+ if ((access_key == "") ||
+ (secret_key == "")) {
+ std::cout << argv[0] << " no AWS credentials, exiting" << std::endl;
+ return EPERM;
+ }
+
+ saved_args.argc = argc;
+ saved_args.argv = argv;
+
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}