mirror of
https://github.com/ceph/ceph
synced 2025-04-07 18:17:22 +00:00
This is part of a series of commits to clean up using namespace at top level in headers. Signed-off-by: Adam C. Emerson <aemerson@redhat.com>
531 lines
15 KiB
C++
531 lines
15 KiB
C++
// -*- 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) 2009-2011 New Dream Network
|
|
*
|
|
* 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 <boost/config/warning_disable.hpp>
|
|
#include <boost/spirit/include/qi.hpp>
|
|
#include <boost/spirit/include/phoenix_operator.hpp>
|
|
#include <boost/spirit/include/phoenix.hpp>
|
|
#include <boost/algorithm/string/predicate.hpp>
|
|
|
|
#include "OSDCap.h"
|
|
#include "common/config.h"
|
|
#include "common/debug.h"
|
|
#include "include/ipaddr.h"
|
|
|
|
using std::ostream;
|
|
using std::string;
|
|
using std::vector;
|
|
|
|
ostream& operator<<(ostream& out, const osd_rwxa_t& p)
|
|
{
|
|
if (p == OSD_CAP_ANY)
|
|
return out << "*";
|
|
|
|
if (p & OSD_CAP_R)
|
|
out << "r";
|
|
if (p & OSD_CAP_W)
|
|
out << "w";
|
|
if ((p & OSD_CAP_X) == OSD_CAP_X) {
|
|
out << "x";
|
|
} else {
|
|
if (p & OSD_CAP_CLS_R)
|
|
out << " class-read";
|
|
if (p & OSD_CAP_CLS_W)
|
|
out << " class-write";
|
|
}
|
|
return out;
|
|
}
|
|
|
|
ostream& operator<<(ostream& out, const OSDCapSpec& s)
|
|
{
|
|
if (s.allow)
|
|
return out << s.allow;
|
|
if (s.class_name.length()) {
|
|
out << "class '" << s.class_name << "'";
|
|
if (!s.method_name.empty()) {
|
|
out << " '" << s.method_name << "'";
|
|
}
|
|
}
|
|
return out;
|
|
}
|
|
|
|
ostream& operator<<(ostream& out, const OSDCapPoolNamespace& pns)
|
|
{
|
|
if (!pns.pool_name.empty()) {
|
|
out << "pool " << pns.pool_name << " ";
|
|
}
|
|
if (pns.nspace) {
|
|
out << "namespace ";
|
|
if (pns.nspace->empty()) {
|
|
out << "\"\"";
|
|
} else {
|
|
out << *pns.nspace;
|
|
}
|
|
out << " ";
|
|
}
|
|
return out;
|
|
}
|
|
|
|
ostream& operator<<(ostream &out, const OSDCapPoolTag &pt)
|
|
{
|
|
out << "app " << pt.application << " key " << pt.key << " val " << pt.value
|
|
<< " ";
|
|
return out;
|
|
}
|
|
|
|
ostream& operator<<(ostream& out, const OSDCapMatch& m)
|
|
{
|
|
if (!m.pool_namespace.pool_name.empty() || m.pool_namespace.nspace) {
|
|
out << m.pool_namespace;
|
|
}
|
|
|
|
if (!m.pool_tag.application.empty()) {
|
|
out << m.pool_tag;
|
|
}
|
|
|
|
if (m.object_prefix.length()) {
|
|
out << "object_prefix " << m.object_prefix << " ";
|
|
}
|
|
return out;
|
|
}
|
|
|
|
ostream& operator<<(ostream& out, const OSDCapProfile& m)
|
|
{
|
|
out << "profile " << m.name;
|
|
out << m.pool_namespace;
|
|
return out;
|
|
}
|
|
|
|
bool OSDCapPoolNamespace::is_match(const std::string& pn,
|
|
const std::string& ns) const
|
|
{
|
|
if (!pool_name.empty()) {
|
|
if (pool_name != pn) {
|
|
return false;
|
|
}
|
|
}
|
|
if (nspace) {
|
|
if (!nspace->empty() && nspace->back() == '*' &&
|
|
boost::starts_with(ns, nspace->substr(0, nspace->length() - 1))) {
|
|
return true;
|
|
}
|
|
|
|
if (*nspace != ns) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool OSDCapPoolNamespace::is_match_all() const
|
|
{
|
|
if (!pool_name.empty())
|
|
return false;
|
|
if (nspace)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
bool OSDCapPoolTag::is_match(const app_map_t& app_map) const
|
|
{
|
|
if (application.empty()) {
|
|
return true;
|
|
}
|
|
auto kv_map = app_map.find(application);
|
|
if (kv_map == app_map.end()) {
|
|
return false;
|
|
}
|
|
if (!key.compare("*") && !value.compare("*")) {
|
|
return true;
|
|
}
|
|
if (!key.compare("*")) {
|
|
for (auto it : kv_map->second) {
|
|
if (it.second == value) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
auto kv_val = kv_map->second.find(key);
|
|
if (kv_val == kv_map->second.end()) {
|
|
return false;
|
|
}
|
|
if (!value.compare("*")) {
|
|
return true;
|
|
}
|
|
return kv_val->second == value;
|
|
}
|
|
|
|
bool OSDCapPoolTag::is_match_all() const {
|
|
return application.empty();
|
|
}
|
|
|
|
bool OSDCapMatch::is_match(const string& pn, const string& ns,
|
|
const OSDCapPoolTag::app_map_t& app_map,
|
|
const string& object) const
|
|
{
|
|
if (!pool_namespace.is_match(pn, ns)) {
|
|
return false;
|
|
} else if (!pool_tag.is_match(app_map)) {
|
|
return false;
|
|
}
|
|
|
|
if (object_prefix.length()) {
|
|
if (object.find(object_prefix) != 0)
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool OSDCapMatch::is_match_all() const
|
|
{
|
|
if (!pool_namespace.is_match_all()) {
|
|
return false;
|
|
} else if (!pool_tag.is_match_all()) {
|
|
return false;
|
|
}
|
|
|
|
if (object_prefix.length()) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
ostream& operator<<(ostream& out, const OSDCapGrant& g)
|
|
{
|
|
out << "grant(";
|
|
if (g.profile.is_valid()) {
|
|
out << g.profile << " [";
|
|
for (auto it = g.profile_grants.cbegin();
|
|
it != g.profile_grants.cend(); ++it) {
|
|
if (it != g.profile_grants.cbegin()) {
|
|
out << ",";
|
|
}
|
|
out << *it;
|
|
}
|
|
out << "]";
|
|
} else {
|
|
out << g.match << g.spec;
|
|
}
|
|
if (g.network.size()) {
|
|
out << " network " << g.network;
|
|
}
|
|
out << ")";
|
|
return out;
|
|
}
|
|
|
|
void OSDCapGrant::set_network(const string& n)
|
|
{
|
|
network = n;
|
|
network_valid = ::parse_network(n.c_str(), &network_parsed, &network_prefix);
|
|
}
|
|
|
|
bool OSDCapGrant::allow_all() const
|
|
{
|
|
if (profile.is_valid()) {
|
|
return std::any_of(profile_grants.cbegin(), profile_grants.cend(),
|
|
[](const OSDCapGrant& grant) {
|
|
return grant.allow_all();
|
|
});
|
|
}
|
|
|
|
return (match.is_match_all() && spec.allow_all());
|
|
}
|
|
|
|
bool OSDCapGrant::is_capable(
|
|
const string& pool_name,
|
|
const string& ns,
|
|
const OSDCapPoolTag::app_map_t& application_metadata,
|
|
const string& object,
|
|
bool op_may_read,
|
|
bool op_may_write,
|
|
const std::vector<OpInfo::ClassInfo>& classes,
|
|
const entity_addr_t& addr,
|
|
std::vector<bool>* class_allowed) const
|
|
{
|
|
osd_rwxa_t allow = 0;
|
|
|
|
if (network.size() &&
|
|
(!network_valid ||
|
|
!network_contains(network_parsed,
|
|
network_prefix,
|
|
addr))) {
|
|
return false;
|
|
}
|
|
|
|
if (profile.is_valid()) {
|
|
return std::any_of(profile_grants.cbegin(), profile_grants.cend(),
|
|
[&](const OSDCapGrant& grant) {
|
|
return grant.is_capable(pool_name, ns,
|
|
application_metadata,
|
|
object, op_may_read,
|
|
op_may_write, classes, addr,
|
|
class_allowed);
|
|
});
|
|
} else {
|
|
if (match.is_match(pool_name, ns, application_metadata, object)) {
|
|
allow = allow | spec.allow;
|
|
if ((op_may_read && !(allow & OSD_CAP_R)) ||
|
|
(op_may_write && !(allow & OSD_CAP_W))) {
|
|
return false;
|
|
}
|
|
if (!classes.empty()) {
|
|
// check 'allow *'
|
|
if (spec.allow_all()) {
|
|
return true;
|
|
}
|
|
|
|
// compare this grant to each class in the operation
|
|
for (size_t i = 0; i < classes.size(); ++i) {
|
|
// check 'allow class foo [method_name]'
|
|
if (!spec.class_name.empty() &&
|
|
classes[i].class_name == spec.class_name &&
|
|
(spec.method_name.empty() ||
|
|
classes[i].method_name == spec.method_name)) {
|
|
(*class_allowed)[i] = true;
|
|
continue;
|
|
}
|
|
// check 'allow x | class-{rw}': must be on whitelist
|
|
if (!classes[i].whitelisted) {
|
|
continue;
|
|
}
|
|
if ((classes[i].read && !(allow & OSD_CAP_CLS_R)) ||
|
|
(classes[i].write && !(allow & OSD_CAP_CLS_W))) {
|
|
continue;
|
|
}
|
|
(*class_allowed)[i] = true;
|
|
}
|
|
if (!std::all_of(class_allowed->cbegin(), class_allowed->cend(),
|
|
[](bool v) { return v; })) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void OSDCapGrant::expand_profile()
|
|
{
|
|
if (profile.name == "read-only") {
|
|
// grants READ-ONLY caps to the OSD
|
|
profile_grants.emplace_back(OSDCapMatch(profile.pool_namespace),
|
|
OSDCapSpec(osd_rwxa_t(OSD_CAP_R)));
|
|
return;
|
|
}
|
|
if (profile.name == "read-write") {
|
|
// grants READ-WRITE caps to the OSD
|
|
profile_grants.emplace_back(OSDCapMatch(profile.pool_namespace),
|
|
OSDCapSpec(osd_rwxa_t(OSD_CAP_R | OSD_CAP_W)));
|
|
}
|
|
|
|
if (profile.name == "rbd") {
|
|
// RBD read-write grant
|
|
profile_grants.emplace_back(OSDCapMatch(string(), "rbd_children"),
|
|
OSDCapSpec(osd_rwxa_t(OSD_CAP_CLS_R)));
|
|
profile_grants.emplace_back(OSDCapMatch(string(), "rbd_mirroring"),
|
|
OSDCapSpec(osd_rwxa_t(OSD_CAP_CLS_R)));
|
|
profile_grants.emplace_back(OSDCapMatch(profile.pool_namespace.pool_name),
|
|
OSDCapSpec("rbd", "metadata_list"));
|
|
profile_grants.emplace_back(OSDCapMatch(profile.pool_namespace),
|
|
OSDCapSpec(osd_rwxa_t(OSD_CAP_R |
|
|
OSD_CAP_W |
|
|
OSD_CAP_X)));
|
|
}
|
|
if (profile.name == "rbd-read-only") {
|
|
// RBD read-only grant
|
|
profile_grants.emplace_back(OSDCapMatch(profile.pool_namespace),
|
|
OSDCapSpec(osd_rwxa_t(OSD_CAP_R |
|
|
OSD_CAP_CLS_R)));
|
|
profile_grants.emplace_back(OSDCapMatch(profile.pool_namespace,
|
|
"rbd_header."),
|
|
OSDCapSpec("rbd", "child_attach"));
|
|
profile_grants.emplace_back(OSDCapMatch(profile.pool_namespace,
|
|
"rbd_header."),
|
|
OSDCapSpec("rbd", "child_detach"));
|
|
}
|
|
}
|
|
|
|
bool OSDCap::allow_all() const
|
|
{
|
|
for (auto &grant : grants) {
|
|
if (grant.allow_all()) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void OSDCap::set_allow_all()
|
|
{
|
|
grants.clear();
|
|
grants.push_back(OSDCapGrant(OSDCapMatch(), OSDCapSpec(OSD_CAP_ANY)));
|
|
}
|
|
|
|
bool OSDCap::is_capable(const string& pool_name, const string& ns,
|
|
const OSDCapPoolTag::app_map_t& application_metadata,
|
|
const string& object,
|
|
bool op_may_read, bool op_may_write,
|
|
const std::vector<OpInfo::ClassInfo>& classes,
|
|
const entity_addr_t& addr) const
|
|
{
|
|
std::vector<bool> class_allowed(classes.size(), false);
|
|
for (auto &grant : grants) {
|
|
if (grant.is_capable(pool_name, ns, application_metadata,
|
|
object, op_may_read, op_may_write, classes, addr,
|
|
&class_allowed)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
// grammar
|
|
namespace qi = boost::spirit::qi;
|
|
namespace ascii = boost::spirit::ascii;
|
|
namespace phoenix = boost::phoenix;
|
|
|
|
template <typename Iterator>
|
|
struct OSDCapParser : qi::grammar<Iterator, OSDCap()>
|
|
{
|
|
OSDCapParser() : OSDCapParser::base_type(osdcap)
|
|
{
|
|
using qi::char_;
|
|
using qi::int_;
|
|
using qi::lexeme;
|
|
using qi::alnum;
|
|
using qi::_val;
|
|
using qi::_1;
|
|
using qi::_2;
|
|
using qi::_3;
|
|
using qi::eps;
|
|
using qi::lit;
|
|
|
|
quoted_string %=
|
|
lexeme['"' >> +(char_ - '"') >> '"'] |
|
|
lexeme['\'' >> +(char_ - '\'') >> '\''];
|
|
equoted_string %=
|
|
lexeme['"' >> *(char_ - '"') >> '"'] |
|
|
lexeme['\'' >> *(char_ - '\'') >> '\''];
|
|
unquoted_word %= +char_("a-zA-Z0-9_./-");
|
|
str %= quoted_string | unquoted_word;
|
|
estr %= equoted_string | unquoted_word;
|
|
network_str %= +char_("/.:a-fA-F0-9][");
|
|
|
|
spaces = +ascii::space;
|
|
|
|
wildcard = (lit('*') | lit("all")) [_val = "*"];
|
|
|
|
pool_name %= -(spaces >> lit("pool") >> (lit('=') | spaces) >> str);
|
|
nspace %= (spaces >> lit("namespace")
|
|
>> (lit('=') | spaces)
|
|
>> estr >> -char_('*'));
|
|
|
|
// match := [pool[=]<poolname> [namespace[=]<namespace>]] [object_prefix <prefix>]
|
|
object_prefix %= -(spaces >> lit("object_prefix") >> spaces >> str);
|
|
pooltag %= (spaces >> lit("tag")
|
|
>> spaces >> str // application
|
|
>> spaces >> (wildcard | str) // key
|
|
>> -spaces >> lit('=') >> -spaces >> (wildcard | str)); // value
|
|
|
|
match = (
|
|
pooltag [_val = phoenix::construct<OSDCapMatch>(_1)] |
|
|
(nspace >> pooltag) [_val = phoenix::construct<OSDCapMatch>(_1, _2)] |
|
|
(pool_name >> nspace >> object_prefix) [_val = phoenix::construct<OSDCapMatch>(_1, _2, _3)] |
|
|
(pool_name >> object_prefix) [_val = phoenix::construct<OSDCapMatch>(_1, _2)]
|
|
);
|
|
|
|
// rwxa := * | [r][w][x] [class-read] [class-write]
|
|
rwxa =
|
|
(spaces >> wildcard[_val = OSD_CAP_ANY]) |
|
|
( eps[_val = 0] >>
|
|
(
|
|
spaces >>
|
|
( lit('r')[_val |= OSD_CAP_R] ||
|
|
lit('w')[_val |= OSD_CAP_W] ||
|
|
lit('x')[_val |= OSD_CAP_X] )) ||
|
|
( (spaces >> lit("class-read")[_val |= OSD_CAP_CLS_R]) ||
|
|
(spaces >> lit("class-write")[_val |= OSD_CAP_CLS_W]) ));
|
|
|
|
// capspec := * | rwx | class <name> [<method name>]
|
|
class_name %= (spaces >> lit("class") >> spaces >> str);
|
|
method_name %= -(spaces >> str);
|
|
capspec = (
|
|
(rwxa) [_val = phoenix::construct<OSDCapSpec>(_1)] |
|
|
(class_name >> method_name) [_val = phoenix::construct<OSDCapSpec>(_1, _2)]);
|
|
|
|
// profile := profile <name> [pool[=]<pool> [namespace[=]<namespace>]]
|
|
profile_name %= (lit("profile") >> (lit('=') | spaces) >> str);
|
|
profile = (
|
|
(profile_name >> pool_name >> nspace) [_val = phoenix::construct<OSDCapProfile>(_1, _2, _3)] |
|
|
(profile_name >> pool_name) [_val = phoenix::construct<OSDCapProfile>(_1, _2)]);
|
|
|
|
// grant := allow match capspec
|
|
grant = (*ascii::blank >>
|
|
((lit("allow") >> capspec >> match >>
|
|
-(spaces >> lit("network") >> spaces >> network_str))
|
|
[_val = phoenix::construct<OSDCapGrant>(_2, _1, _3)] |
|
|
(lit("allow") >> match >> capspec >>
|
|
-(spaces >> lit("network") >> spaces >> network_str))
|
|
[_val = phoenix::construct<OSDCapGrant>(_1, _2, _3)] |
|
|
(profile >> -(spaces >> lit("network") >> spaces >> network_str))
|
|
[_val = phoenix::construct<OSDCapGrant>(_1, _2)]
|
|
) >> *ascii::blank);
|
|
// osdcap := grant [grant ...]
|
|
grants %= (grant % (lit(';') | lit(',')));
|
|
osdcap = grants [_val = phoenix::construct<OSDCap>(_1)];
|
|
}
|
|
qi::rule<Iterator> spaces;
|
|
qi::rule<Iterator, unsigned()> rwxa;
|
|
qi::rule<Iterator, string()> quoted_string, equoted_string;
|
|
qi::rule<Iterator, string()> unquoted_word;
|
|
qi::rule<Iterator, string()> str, estr, network_str;
|
|
qi::rule<Iterator, string()> wildcard;
|
|
qi::rule<Iterator, string()> class_name;
|
|
qi::rule<Iterator, string()> method_name;
|
|
qi::rule<Iterator, OSDCapSpec()> capspec;
|
|
qi::rule<Iterator, string()> pool_name;
|
|
qi::rule<Iterator, string()> nspace;
|
|
qi::rule<Iterator, string()> object_prefix;
|
|
qi::rule<Iterator, OSDCapPoolTag()> pooltag;
|
|
qi::rule<Iterator, OSDCapMatch()> match;
|
|
qi::rule<Iterator, string()> profile_name;
|
|
qi::rule<Iterator, OSDCapProfile()> profile;
|
|
qi::rule<Iterator, OSDCapGrant()> grant;
|
|
qi::rule<Iterator, std::vector<OSDCapGrant>()> grants;
|
|
qi::rule<Iterator, OSDCap()> osdcap;
|
|
};
|
|
|
|
bool OSDCap::parse(const string& str, ostream *err)
|
|
{
|
|
OSDCapParser<string::const_iterator> g;
|
|
string::const_iterator iter = str.begin();
|
|
string::const_iterator end = str.end();
|
|
|
|
bool r = qi::phrase_parse(iter, end, g, ascii::space, *this);
|
|
if (r && iter == end)
|
|
return true;
|
|
|
|
// Make sure no grants are kept after parsing failed!
|
|
grants.clear();
|
|
|
|
if (err)
|
|
*err << "osd capability parse failed, stopped at '" << std::string(iter, end)
|
|
<< "' of '" << str << "'";
|
|
|
|
return false;
|
|
}
|