#include <list>
#include <iostream>
#include <string_view>
+#include <functional>
#include "common/config.h"
respond_to_request(mdr, -ENODATA);
}
+const Server::XattrHandler Server::xattr_handlers[] = {
+ {
+ xattr_name: Server::DEFAULT_HANDLER,
+ description: "default xattr handler",
+ validate: &Server::default_xattr_validate,
+ setxattr: &Server::default_setxattr_handler,
+ removexattr: &Server::default_removexattr_handler,
+ },
+};
+
+const Server::XattrHandler* Server::get_xattr_or_default_handler(std::string_view xattr_name) {
+ const XattrHandler *default_xattr_handler = nullptr;
+
+ for (auto &handler : xattr_handlers) {
+ if (handler.xattr_name == Server::DEFAULT_HANDLER) {
+ ceph_assert(default_xattr_handler == nullptr);
+ default_xattr_handler = &handler;
+ }
+ if (handler.xattr_name == xattr_name) {
+ dout(20) << "handler=" << handler.description << dendl;
+ return &handler;
+ }
+ }
+
+ ceph_assert(default_xattr_handler != nullptr);
+ dout(20) << "handler=" << default_xattr_handler->description << dendl;
+ return default_xattr_handler;
+}
+
+int Server::xattr_validate(CInode *cur, const InodeStoreBase::xattr_map_const_ptr xattrs,
+ const std::string &xattr_name, int op, int flags) {
+ if (op == CEPH_MDS_OP_SETXATTR) {
+ if (xattrs) {
+ if ((flags & CEPH_XATTR_CREATE) && xattrs->count(mempool::mds_co::string(xattr_name))) {
+ dout(10) << "setxattr '" << xattr_name << "' XATTR_CREATE and EEXIST on " << *cur << dendl;
+ return -EEXIST;
+ }
+ }
+ if ((flags & CEPH_XATTR_REPLACE) && !(xattrs && xattrs->count(mempool::mds_co::string(xattr_name)))) {
+ dout(10) << "setxattr '" << xattr_name << "' XATTR_REPLACE and ENODATA on " << *cur << dendl;
+ return -ENODATA;
+ }
+
+ return 0;
+ }
+
+ if (op == CEPH_MDS_OP_RMXATTR) {
+ if (xattrs && xattrs->count(mempool::mds_co::string(xattr_name)) == 0) {
+ dout(10) << "removexattr '" << xattr_name << "' and ENODATA on " << *cur << dendl;
+ return -ENODATA;
+ }
+
+ return 0;
+ }
+
+ derr << ": unhandled validation for: " << xattr_name << dendl;
+ return -EINVAL;
+}
+
+void Server::xattr_set(InodeStoreBase::xattr_map_ptr xattrs, const std::string &xattr_name,
+ const bufferlist &xattr_value) {
+ size_t len = xattr_value.length();
+ bufferptr b = buffer::create(len);
+ if (len) {
+ xattr_value.begin().copy(len, b.c_str());
+ }
+ auto em = xattrs->emplace(std::piecewise_construct,
+ std::forward_as_tuple(mempool::mds_co::string(xattr_name)),
+ std::forward_as_tuple(b));
+ if (!em.second) {
+ em.first->second = b;
+ }
+}
+
+void Server::xattr_rm(InodeStoreBase::xattr_map_ptr xattrs, const std::string &xattr_name) {
+ xattrs->erase(mempool::mds_co::string(xattr_name));
+}
+
+int Server::default_xattr_validate(CInode *cur, const InodeStoreBase::xattr_map_const_ptr xattrs,
+ XattrOp *xattr_op) {
+ return xattr_validate(cur, xattrs, xattr_op->xattr_name, xattr_op->op, xattr_op->flags);
+}
+
+void Server::default_setxattr_handler(CInode *cur, InodeStoreBase::xattr_map_ptr xattrs,
+ const XattrOp &xattr_op) {
+ xattr_set(xattrs, xattr_op.xattr_name, xattr_op.xattr_value);
+}
+
+void Server::default_removexattr_handler(CInode *cur, InodeStoreBase::xattr_map_ptr xattrs,
+ const XattrOp &xattr_op) {
+ xattr_rm(xattrs, xattr_op.xattr_name);
+}
+
void Server::handle_client_setxattr(MDRequestRef& mdr)
{
const cref_t<MClientRequest> &req = mdr->client_request;
size_t len = req->get_data().length();
size_t inc = len + name.length();
+ auto handler = Server::get_xattr_or_default_handler(name);
const auto& pxattrs = cur->get_projected_xattrs();
if (pxattrs) {
// check xattrs kv pairs size
respond_to_request(mdr, -ENOSPC);
return;
}
-
- if ((flags & CEPH_XATTR_CREATE) && pxattrs->count(mempool::mds_co::string(name))) {
- dout(10) << "setxattr '" << name << "' XATTR_CREATE and EEXIST on " << *cur << dendl;
- respond_to_request(mdr, -EEXIST);
- return;
- }
}
- if ((flags & CEPH_XATTR_REPLACE) &&
- !(pxattrs && pxattrs->count(mempool::mds_co::string(name)))) {
- dout(10) << "setxattr '" << name << "' XATTR_REPLACE and ENODATA on " << *cur << dendl;
- respond_to_request(mdr, -ENODATA);
+ XattrOp xattr_op(CEPH_MDS_OP_SETXATTR, name, req->get_data(), flags);
+ int r = std::invoke(handler->validate, this, cur, pxattrs, &xattr_op);
+ if (r < 0) {
+ respond_to_request(mdr, r);
return;
}
pi.inode->rstat.rctime = mdr->get_op_stamp();
pi.inode->change_attr++;
pi.inode->xattr_version++;
+
if ((flags & CEPH_XATTR_REMOVE)) {
- pi.xattrs->erase(mempool::mds_co::string(name));
+ std::invoke(handler->removexattr, this, cur, pi.xattrs, xattr_op);
} else {
- bufferptr b = buffer::create(len);
- if (len)
- req->get_data().begin().copy(len, b.c_str());
- auto em = pi.xattrs->emplace(std::piecewise_construct, std::forward_as_tuple(mempool::mds_co::string(name)), std::forward_as_tuple(b));
- if (!em.second)
- em.first->second = b;
+ std::invoke(handler->setxattr, this, cur, pi.xattrs, xattr_op);
}
// log + wait
if (!mds->locker->acquire_locks(mdr, lov))
return;
+
+ auto handler = Server::get_xattr_or_default_handler(name);
+ bufferlist bl;
+ XattrOp xattr_op(CEPH_MDS_OP_RMXATTR, name, bl, 0);
+
const auto& pxattrs = cur->get_projected_xattrs();
- if (pxattrs && pxattrs->count(mempool::mds_co::string(name)) == 0) {
- dout(10) << "removexattr '" << name << "' and ENODATA on " << *cur << dendl;
- respond_to_request(mdr, -ENODATA);
+ int r = std::invoke(handler->validate, this, cur, pxattrs, &xattr_op);
+ if (r < 0) {
+ respond_to_request(mdr, r);
return;
}
// project update
auto pi = cur->project_inode(mdr, true);
- auto &px = *pi.xattrs;
pi.inode->version = cur->pre_dirty();
pi.inode->ctime = mdr->get_op_stamp();
if (mdr->get_op_stamp() > pi.inode->rstat.rctime)
pi.inode->rstat.rctime = mdr->get_op_stamp();
pi.inode->change_attr++;
pi.inode->xattr_version++;
- px.erase(mempool::mds_co::string(name));
+ std::invoke(handler->removexattr, this, cur, pi.xattrs, xattr_op);
// log + wait
mdr->ls = mdlog->get_current_segment();
#include "messages/MClientReclaimReply.h"
#include "messages/MLock.h"
+#include "CInode.h"
#include "MDSRank.h"
#include "Mutation.h"
#include "MDSContext.h"
friend class ServerLogContext;
friend class Batch_Getattr_Lookup;
+ // placeholder for validation handler to store xattr specific
+ // data
+ struct XattrInfo {
+ virtual ~XattrInfo() {
+ }
+ };
+
+ struct XattrOp {
+ int op;
+ std::string xattr_name;
+ const bufferlist &xattr_value;
+ int flags = 0;
+
+ std::unique_ptr<XattrInfo> xinfo;
+
+ XattrOp(int op, std::string_view xattr_name, const bufferlist &xattr_value, int flags)
+ : op(op),
+ xattr_name(xattr_name),
+ xattr_value(xattr_value),
+ flags (flags) {
+ }
+ };
+
+ struct XattrHandler {
+ const std::string xattr_name;
+ const std::string description;
+
+ // basic checks are to be done in this handler. return -errno to
+ // reject xattr request (set or remove), zero to proceed. handlers
+ // may parse xattr value for verification if needed and have an
+ // option to store custom data in XattrOp::xinfo.
+ int (Server::*validate)(CInode *cur, const InodeStoreBase::xattr_map_const_ptr xattrs,
+ XattrOp *xattr_op);
+
+ // set xattr for an inode in xattr_map
+ void (Server::*setxattr)(CInode *cur, InodeStoreBase::xattr_map_ptr xattrs,
+ const XattrOp &xattr_op);
+
+ // remove xattr for an inode from xattr_map
+ void (Server::*removexattr)(CInode *cur, InodeStoreBase::xattr_map_ptr xattrs,
+ const XattrOp &xattr_op);
+ };
+
+ inline static const std::string DEFAULT_HANDLER = "<default>";
+ static const XattrHandler xattr_handlers[];
+
+ const XattrHandler* get_xattr_or_default_handler(std::string_view xattr_name);
+
+ // generic variant to set/remove xattr in/from xattr_map
+ int xattr_validate(CInode *cur, const InodeStoreBase::xattr_map_const_ptr xattrs,
+ const std::string &xattr_name, int op, int flags);
+ void xattr_set(InodeStoreBase::xattr_map_ptr xattrs, const std::string &xattr_name,
+ const bufferlist &xattr_value);
+ void xattr_rm(InodeStoreBase::xattr_map_ptr xattrs, const std::string &xattr_name);
+
+ // default xattr handlers
+ int default_xattr_validate(CInode *cur, const InodeStoreBase::xattr_map_const_ptr xattrs,
+ XattrOp *xattr_op);
+ void default_setxattr_handler(CInode *cur, InodeStoreBase::xattr_map_ptr xattrs,
+ const XattrOp &xattr_op);
+ void default_removexattr_handler(CInode *cur, InodeStoreBase::xattr_map_ptr xattrs,
+ const XattrOp &xattr_op);
+
static bool is_ceph_vxattr(std::string_view xattr_name) {
return xattr_name.rfind("ceph.dir.layout", 0) == 0 ||
xattr_name.rfind("ceph.file.layout", 0) == 0 ||