854 lines
19 KiB
C
854 lines
19 KiB
C
/*
|
|
* GPL HEADER START
|
|
*
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 only,
|
|
* as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License version 2 for more details (a copy is included
|
|
* in the LICENSE file that accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* version 2 along with this program; If not, see
|
|
* http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
|
|
*
|
|
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
|
* CA 95054 USA or visit www.sun.com if you need additional information or
|
|
* have any questions.
|
|
*
|
|
* GPL HEADER END
|
|
*/
|
|
/*
|
|
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
|
|
* Use is subject to license terms.
|
|
*
|
|
* Copyright (c) 2011, 2015, Intel Corporation.
|
|
*/
|
|
/*
|
|
* This file is part of Lustre, http://www.lustre.org/
|
|
* Lustre is a trademark of Sun Microsystems, Inc.
|
|
*/
|
|
|
|
#define DEBUG_SUBSYSTEM S_SEC
|
|
|
|
#include "../../include/linux/libcfs/libcfs.h"
|
|
#include <linux/crypto.h>
|
|
#include <linux/key.h>
|
|
|
|
#include "../include/obd.h"
|
|
#include "../include/obd_support.h"
|
|
#include "../include/lustre_import.h"
|
|
#include "../include/lustre_param.h"
|
|
#include "../include/lustre_sec.h"
|
|
|
|
#include "ptlrpc_internal.h"
|
|
|
|
enum lustre_sec_part sptlrpc_target_sec_part(struct obd_device *obd)
|
|
{
|
|
const char *type = obd->obd_type->typ_name;
|
|
|
|
if (!strcmp(type, LUSTRE_MDT_NAME))
|
|
return LUSTRE_SP_MDT;
|
|
if (!strcmp(type, LUSTRE_OST_NAME))
|
|
return LUSTRE_SP_OST;
|
|
if (!strcmp(type, LUSTRE_MGS_NAME))
|
|
return LUSTRE_SP_MGS;
|
|
|
|
CERROR("unknown target %p(%s)\n", obd, type);
|
|
return LUSTRE_SP_ANY;
|
|
}
|
|
EXPORT_SYMBOL(sptlrpc_target_sec_part);
|
|
|
|
/****************************************
|
|
* user supplied flavor string parsing *
|
|
****************************************/
|
|
|
|
/*
|
|
* format: <base_flavor>[-<bulk_type:alg_spec>]
|
|
*/
|
|
int sptlrpc_parse_flavor(const char *str, struct sptlrpc_flavor *flvr)
|
|
{
|
|
char buf[32];
|
|
char *bulk, *alg;
|
|
|
|
memset(flvr, 0, sizeof(*flvr));
|
|
|
|
if (!str || str[0] == '\0') {
|
|
flvr->sf_rpc = SPTLRPC_FLVR_INVALID;
|
|
return 0;
|
|
}
|
|
|
|
strlcpy(buf, str, sizeof(buf));
|
|
|
|
bulk = strchr(buf, '-');
|
|
if (bulk)
|
|
*bulk++ = '\0';
|
|
|
|
flvr->sf_rpc = sptlrpc_name2flavor_base(buf);
|
|
if (flvr->sf_rpc == SPTLRPC_FLVR_INVALID)
|
|
goto err_out;
|
|
|
|
/*
|
|
* currently only base flavor "plain" can have bulk specification.
|
|
*/
|
|
if (flvr->sf_rpc == SPTLRPC_FLVR_PLAIN) {
|
|
flvr->u_bulk.hash.hash_alg = BULK_HASH_ALG_ADLER32;
|
|
if (bulk) {
|
|
/*
|
|
* format: plain-hash:<hash_alg>
|
|
*/
|
|
alg = strchr(bulk, ':');
|
|
if (!alg)
|
|
goto err_out;
|
|
*alg++ = '\0';
|
|
|
|
if (strcmp(bulk, "hash"))
|
|
goto err_out;
|
|
|
|
flvr->u_bulk.hash.hash_alg = sptlrpc_get_hash_alg(alg);
|
|
if (flvr->u_bulk.hash.hash_alg >= BULK_HASH_ALG_MAX)
|
|
goto err_out;
|
|
}
|
|
|
|
if (flvr->u_bulk.hash.hash_alg == BULK_HASH_ALG_NULL)
|
|
flvr_set_bulk_svc(&flvr->sf_rpc, SPTLRPC_BULK_SVC_NULL);
|
|
else
|
|
flvr_set_bulk_svc(&flvr->sf_rpc, SPTLRPC_BULK_SVC_INTG);
|
|
} else {
|
|
if (bulk)
|
|
goto err_out;
|
|
}
|
|
|
|
flvr->sf_flags = 0;
|
|
return 0;
|
|
|
|
err_out:
|
|
CERROR("invalid flavor string: %s\n", str);
|
|
return -EINVAL;
|
|
}
|
|
EXPORT_SYMBOL(sptlrpc_parse_flavor);
|
|
|
|
/****************************************
|
|
* configure rules *
|
|
****************************************/
|
|
|
|
static void get_default_flavor(struct sptlrpc_flavor *sf)
|
|
{
|
|
memset(sf, 0, sizeof(*sf));
|
|
|
|
sf->sf_rpc = SPTLRPC_FLVR_NULL;
|
|
sf->sf_flags = 0;
|
|
}
|
|
|
|
static void sptlrpc_rule_init(struct sptlrpc_rule *rule)
|
|
{
|
|
rule->sr_netid = LNET_NIDNET(LNET_NID_ANY);
|
|
rule->sr_from = LUSTRE_SP_ANY;
|
|
rule->sr_to = LUSTRE_SP_ANY;
|
|
rule->sr_padding = 0;
|
|
|
|
get_default_flavor(&rule->sr_flvr);
|
|
}
|
|
|
|
/*
|
|
* format: network[.direction]=flavor
|
|
*/
|
|
static int sptlrpc_parse_rule(char *param, struct sptlrpc_rule *rule)
|
|
{
|
|
char *flavor, *dir;
|
|
int rc;
|
|
|
|
sptlrpc_rule_init(rule);
|
|
|
|
flavor = strchr(param, '=');
|
|
if (!flavor) {
|
|
CERROR("invalid param, no '='\n");
|
|
return -EINVAL;
|
|
}
|
|
*flavor++ = '\0';
|
|
|
|
dir = strchr(param, '.');
|
|
if (dir)
|
|
*dir++ = '\0';
|
|
|
|
/* 1.1 network */
|
|
if (strcmp(param, "default")) {
|
|
rule->sr_netid = libcfs_str2net(param);
|
|
if (rule->sr_netid == LNET_NIDNET(LNET_NID_ANY)) {
|
|
CERROR("invalid network name: %s\n", param);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
/* 1.2 direction */
|
|
if (dir) {
|
|
if (!strcmp(dir, "mdt2ost")) {
|
|
rule->sr_from = LUSTRE_SP_MDT;
|
|
rule->sr_to = LUSTRE_SP_OST;
|
|
} else if (!strcmp(dir, "mdt2mdt")) {
|
|
rule->sr_from = LUSTRE_SP_MDT;
|
|
rule->sr_to = LUSTRE_SP_MDT;
|
|
} else if (!strcmp(dir, "cli2ost")) {
|
|
rule->sr_from = LUSTRE_SP_CLI;
|
|
rule->sr_to = LUSTRE_SP_OST;
|
|
} else if (!strcmp(dir, "cli2mdt")) {
|
|
rule->sr_from = LUSTRE_SP_CLI;
|
|
rule->sr_to = LUSTRE_SP_MDT;
|
|
} else {
|
|
CERROR("invalid rule dir segment: %s\n", dir);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
/* 2.1 flavor */
|
|
rc = sptlrpc_parse_flavor(flavor, &rule->sr_flvr);
|
|
if (rc)
|
|
return -EINVAL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void sptlrpc_rule_set_free(struct sptlrpc_rule_set *rset)
|
|
{
|
|
LASSERT(rset->srs_nslot ||
|
|
(rset->srs_nrule == 0 && !rset->srs_rules));
|
|
|
|
if (rset->srs_nslot) {
|
|
kfree(rset->srs_rules);
|
|
sptlrpc_rule_set_init(rset);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* return 0 if the rule set could accommodate one more rule.
|
|
*/
|
|
static int sptlrpc_rule_set_expand(struct sptlrpc_rule_set *rset)
|
|
{
|
|
struct sptlrpc_rule *rules;
|
|
int nslot;
|
|
|
|
might_sleep();
|
|
|
|
if (rset->srs_nrule < rset->srs_nslot)
|
|
return 0;
|
|
|
|
nslot = rset->srs_nslot + 8;
|
|
|
|
/* better use realloc() if available */
|
|
rules = kcalloc(nslot, sizeof(*rset->srs_rules), GFP_NOFS);
|
|
if (!rules)
|
|
return -ENOMEM;
|
|
|
|
if (rset->srs_nrule) {
|
|
LASSERT(rset->srs_nslot && rset->srs_rules);
|
|
memcpy(rules, rset->srs_rules,
|
|
rset->srs_nrule * sizeof(*rset->srs_rules));
|
|
|
|
kfree(rset->srs_rules);
|
|
}
|
|
|
|
rset->srs_rules = rules;
|
|
rset->srs_nslot = nslot;
|
|
return 0;
|
|
}
|
|
|
|
static inline int rule_spec_dir(struct sptlrpc_rule *rule)
|
|
{
|
|
return (rule->sr_from != LUSTRE_SP_ANY ||
|
|
rule->sr_to != LUSTRE_SP_ANY);
|
|
}
|
|
|
|
static inline int rule_spec_net(struct sptlrpc_rule *rule)
|
|
{
|
|
return (rule->sr_netid != LNET_NIDNET(LNET_NID_ANY));
|
|
}
|
|
|
|
static inline int rule_match_dir(struct sptlrpc_rule *r1,
|
|
struct sptlrpc_rule *r2)
|
|
{
|
|
return (r1->sr_from == r2->sr_from && r1->sr_to == r2->sr_to);
|
|
}
|
|
|
|
static inline int rule_match_net(struct sptlrpc_rule *r1,
|
|
struct sptlrpc_rule *r2)
|
|
{
|
|
return (r1->sr_netid == r2->sr_netid);
|
|
}
|
|
|
|
/*
|
|
* merge @rule into @rset.
|
|
* the @rset slots might be expanded.
|
|
*/
|
|
static int sptlrpc_rule_set_merge(struct sptlrpc_rule_set *rset,
|
|
struct sptlrpc_rule *rule)
|
|
{
|
|
struct sptlrpc_rule *p = rset->srs_rules;
|
|
int spec_dir, spec_net;
|
|
int rc, n, match = 0;
|
|
|
|
might_sleep();
|
|
|
|
spec_net = rule_spec_net(rule);
|
|
spec_dir = rule_spec_dir(rule);
|
|
|
|
for (n = 0; n < rset->srs_nrule; n++) {
|
|
p = &rset->srs_rules[n];
|
|
|
|
/* test network match, if failed:
|
|
* - spec rule: skip rules which is also spec rule match, until
|
|
* we hit a wild rule, which means no more chance
|
|
* - wild rule: skip until reach the one which is also wild
|
|
* and matches
|
|
*/
|
|
if (!rule_match_net(p, rule)) {
|
|
if (spec_net) {
|
|
if (rule_spec_net(p))
|
|
continue;
|
|
else
|
|
break;
|
|
} else {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
/* test dir match, same logic as net matching */
|
|
if (!rule_match_dir(p, rule)) {
|
|
if (spec_dir) {
|
|
if (rule_spec_dir(p))
|
|
continue;
|
|
else
|
|
break;
|
|
} else {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
/* find a match */
|
|
match = 1;
|
|
break;
|
|
}
|
|
|
|
if (match) {
|
|
LASSERT(n >= 0 && n < rset->srs_nrule);
|
|
|
|
if (rule->sr_flvr.sf_rpc == SPTLRPC_FLVR_INVALID) {
|
|
/* remove this rule */
|
|
if (n < rset->srs_nrule - 1)
|
|
memmove(&rset->srs_rules[n],
|
|
&rset->srs_rules[n + 1],
|
|
(rset->srs_nrule - n - 1) *
|
|
sizeof(*rule));
|
|
rset->srs_nrule--;
|
|
} else {
|
|
/* override the rule */
|
|
memcpy(&rset->srs_rules[n], rule, sizeof(*rule));
|
|
}
|
|
} else {
|
|
LASSERT(n >= 0 && n <= rset->srs_nrule);
|
|
|
|
if (rule->sr_flvr.sf_rpc != SPTLRPC_FLVR_INVALID) {
|
|
rc = sptlrpc_rule_set_expand(rset);
|
|
if (rc)
|
|
return rc;
|
|
|
|
if (n < rset->srs_nrule)
|
|
memmove(&rset->srs_rules[n + 1],
|
|
&rset->srs_rules[n],
|
|
(rset->srs_nrule - n) * sizeof(*rule));
|
|
memcpy(&rset->srs_rules[n], rule, sizeof(*rule));
|
|
rset->srs_nrule++;
|
|
} else {
|
|
CDEBUG(D_CONFIG, "ignore the unmatched deletion\n");
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* given from/to/nid, determine a matching flavor in ruleset.
|
|
* return 1 if a match found, otherwise return 0.
|
|
*/
|
|
static int sptlrpc_rule_set_choose(struct sptlrpc_rule_set *rset,
|
|
enum lustre_sec_part from,
|
|
enum lustre_sec_part to,
|
|
lnet_nid_t nid,
|
|
struct sptlrpc_flavor *sf)
|
|
{
|
|
struct sptlrpc_rule *r;
|
|
int n;
|
|
|
|
for (n = 0; n < rset->srs_nrule; n++) {
|
|
r = &rset->srs_rules[n];
|
|
|
|
if (LNET_NIDNET(nid) != LNET_NIDNET(LNET_NID_ANY) &&
|
|
r->sr_netid != LNET_NIDNET(LNET_NID_ANY) &&
|
|
LNET_NIDNET(nid) != r->sr_netid)
|
|
continue;
|
|
|
|
if (from != LUSTRE_SP_ANY && r->sr_from != LUSTRE_SP_ANY &&
|
|
from != r->sr_from)
|
|
continue;
|
|
|
|
if (to != LUSTRE_SP_ANY && r->sr_to != LUSTRE_SP_ANY &&
|
|
to != r->sr_to)
|
|
continue;
|
|
|
|
*sf = r->sr_flvr;
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**********************************
|
|
* sptlrpc configuration support *
|
|
**********************************/
|
|
|
|
struct sptlrpc_conf_tgt {
|
|
struct list_head sct_list;
|
|
char sct_name[MAX_OBD_NAME];
|
|
struct sptlrpc_rule_set sct_rset;
|
|
};
|
|
|
|
struct sptlrpc_conf {
|
|
struct list_head sc_list;
|
|
char sc_fsname[MTI_NAME_MAXLEN];
|
|
unsigned int sc_modified; /* modified during updating */
|
|
unsigned int sc_updated:1, /* updated copy from MGS */
|
|
sc_local:1; /* local copy from target */
|
|
struct sptlrpc_rule_set sc_rset; /* fs general rules */
|
|
struct list_head sc_tgts; /* target-specific rules */
|
|
};
|
|
|
|
static struct mutex sptlrpc_conf_lock;
|
|
static LIST_HEAD(sptlrpc_confs);
|
|
|
|
static inline int is_hex(char c)
|
|
{
|
|
return ((c >= '0' && c <= '9') ||
|
|
(c >= 'a' && c <= 'f'));
|
|
}
|
|
|
|
static void target2fsname(const char *tgt, char *fsname, int buflen)
|
|
{
|
|
const char *ptr;
|
|
int len;
|
|
|
|
ptr = strrchr(tgt, '-');
|
|
if (ptr) {
|
|
if ((strncmp(ptr, "-MDT", 4) != 0 &&
|
|
strncmp(ptr, "-OST", 4) != 0) ||
|
|
!is_hex(ptr[4]) || !is_hex(ptr[5]) ||
|
|
!is_hex(ptr[6]) || !is_hex(ptr[7]))
|
|
ptr = NULL;
|
|
}
|
|
|
|
/* if we didn't find the pattern, treat the whole string as fsname */
|
|
if (!ptr)
|
|
len = strlen(tgt);
|
|
else
|
|
len = ptr - tgt;
|
|
|
|
len = min(len, buflen - 1);
|
|
memcpy(fsname, tgt, len);
|
|
fsname[len] = '\0';
|
|
}
|
|
|
|
static void sptlrpc_conf_free_rsets(struct sptlrpc_conf *conf)
|
|
{
|
|
struct sptlrpc_conf_tgt *conf_tgt, *conf_tgt_next;
|
|
|
|
sptlrpc_rule_set_free(&conf->sc_rset);
|
|
|
|
list_for_each_entry_safe(conf_tgt, conf_tgt_next,
|
|
&conf->sc_tgts, sct_list) {
|
|
sptlrpc_rule_set_free(&conf_tgt->sct_rset);
|
|
list_del(&conf_tgt->sct_list);
|
|
kfree(conf_tgt);
|
|
}
|
|
LASSERT(list_empty(&conf->sc_tgts));
|
|
|
|
conf->sc_updated = 0;
|
|
conf->sc_local = 0;
|
|
}
|
|
|
|
static void sptlrpc_conf_free(struct sptlrpc_conf *conf)
|
|
{
|
|
CDEBUG(D_SEC, "free sptlrpc conf %s\n", conf->sc_fsname);
|
|
|
|
sptlrpc_conf_free_rsets(conf);
|
|
list_del(&conf->sc_list);
|
|
kfree(conf);
|
|
}
|
|
|
|
static
|
|
struct sptlrpc_conf_tgt *sptlrpc_conf_get_tgt(struct sptlrpc_conf *conf,
|
|
const char *name,
|
|
int create)
|
|
{
|
|
struct sptlrpc_conf_tgt *conf_tgt;
|
|
|
|
list_for_each_entry(conf_tgt, &conf->sc_tgts, sct_list) {
|
|
if (strcmp(conf_tgt->sct_name, name) == 0)
|
|
return conf_tgt;
|
|
}
|
|
|
|
if (!create)
|
|
return NULL;
|
|
|
|
conf_tgt = kzalloc(sizeof(*conf_tgt), GFP_NOFS);
|
|
if (conf_tgt) {
|
|
strlcpy(conf_tgt->sct_name, name, sizeof(conf_tgt->sct_name));
|
|
sptlrpc_rule_set_init(&conf_tgt->sct_rset);
|
|
list_add(&conf_tgt->sct_list, &conf->sc_tgts);
|
|
}
|
|
|
|
return conf_tgt;
|
|
}
|
|
|
|
static
|
|
struct sptlrpc_conf *sptlrpc_conf_get(const char *fsname,
|
|
int create)
|
|
{
|
|
struct sptlrpc_conf *conf;
|
|
size_t len;
|
|
|
|
list_for_each_entry(conf, &sptlrpc_confs, sc_list) {
|
|
if (strcmp(conf->sc_fsname, fsname) == 0)
|
|
return conf;
|
|
}
|
|
|
|
if (!create)
|
|
return NULL;
|
|
|
|
conf = kzalloc(sizeof(*conf), GFP_NOFS);
|
|
if (!conf)
|
|
return NULL;
|
|
|
|
len = strlcpy(conf->sc_fsname, fsname, sizeof(conf->sc_fsname));
|
|
if (len >= sizeof(conf->sc_fsname)) {
|
|
kfree(conf);
|
|
return NULL;
|
|
}
|
|
sptlrpc_rule_set_init(&conf->sc_rset);
|
|
INIT_LIST_HEAD(&conf->sc_tgts);
|
|
list_add(&conf->sc_list, &sptlrpc_confs);
|
|
|
|
CDEBUG(D_SEC, "create sptlrpc conf %s\n", conf->sc_fsname);
|
|
return conf;
|
|
}
|
|
|
|
/**
|
|
* caller must hold conf_lock already.
|
|
*/
|
|
static int sptlrpc_conf_merge_rule(struct sptlrpc_conf *conf,
|
|
const char *target,
|
|
struct sptlrpc_rule *rule)
|
|
{
|
|
struct sptlrpc_conf_tgt *conf_tgt;
|
|
struct sptlrpc_rule_set *rule_set;
|
|
|
|
/* fsname == target means general rules for the whole fs */
|
|
if (strcmp(conf->sc_fsname, target) == 0) {
|
|
rule_set = &conf->sc_rset;
|
|
} else {
|
|
conf_tgt = sptlrpc_conf_get_tgt(conf, target, 1);
|
|
if (conf_tgt) {
|
|
rule_set = &conf_tgt->sct_rset;
|
|
} else {
|
|
CERROR("out of memory, can't merge rule!\n");
|
|
return -ENOMEM;
|
|
}
|
|
}
|
|
|
|
return sptlrpc_rule_set_merge(rule_set, rule);
|
|
}
|
|
|
|
/**
|
|
* process one LCFG_SPTLRPC_CONF record. if \a conf is NULL, we
|
|
* find one through the target name in the record inside conf_lock;
|
|
* otherwise means caller already hold conf_lock.
|
|
*/
|
|
static int __sptlrpc_process_config(struct lustre_cfg *lcfg,
|
|
struct sptlrpc_conf *conf)
|
|
{
|
|
char *target, *param;
|
|
char fsname[MTI_NAME_MAXLEN];
|
|
struct sptlrpc_rule rule;
|
|
int rc;
|
|
|
|
target = lustre_cfg_string(lcfg, 1);
|
|
if (!target) {
|
|
CERROR("missing target name\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
param = lustre_cfg_string(lcfg, 2);
|
|
if (!param) {
|
|
CERROR("missing parameter\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
CDEBUG(D_SEC, "processing rule: %s.%s\n", target, param);
|
|
|
|
/* parse rule to make sure the format is correct */
|
|
if (strncmp(param, PARAM_SRPC_FLVR, sizeof(PARAM_SRPC_FLVR) - 1) != 0) {
|
|
CERROR("Invalid sptlrpc parameter: %s\n", param);
|
|
return -EINVAL;
|
|
}
|
|
param += sizeof(PARAM_SRPC_FLVR) - 1;
|
|
|
|
rc = sptlrpc_parse_rule(param, &rule);
|
|
if (rc)
|
|
return -EINVAL;
|
|
|
|
if (!conf) {
|
|
target2fsname(target, fsname, sizeof(fsname));
|
|
|
|
mutex_lock(&sptlrpc_conf_lock);
|
|
conf = sptlrpc_conf_get(fsname, 0);
|
|
if (!conf) {
|
|
CERROR("can't find conf\n");
|
|
rc = -ENOMEM;
|
|
} else {
|
|
rc = sptlrpc_conf_merge_rule(conf, target, &rule);
|
|
}
|
|
mutex_unlock(&sptlrpc_conf_lock);
|
|
} else {
|
|
LASSERT(mutex_is_locked(&sptlrpc_conf_lock));
|
|
rc = sptlrpc_conf_merge_rule(conf, target, &rule);
|
|
}
|
|
|
|
if (rc == 0)
|
|
conf->sc_modified++;
|
|
|
|
return rc;
|
|
}
|
|
|
|
int sptlrpc_process_config(struct lustre_cfg *lcfg)
|
|
{
|
|
return __sptlrpc_process_config(lcfg, NULL);
|
|
}
|
|
EXPORT_SYMBOL(sptlrpc_process_config);
|
|
|
|
static int logname2fsname(const char *logname, char *buf, int buflen)
|
|
{
|
|
char *ptr;
|
|
int len;
|
|
|
|
ptr = strrchr(logname, '-');
|
|
if (!ptr || strcmp(ptr, "-sptlrpc")) {
|
|
CERROR("%s is not a sptlrpc config log\n", logname);
|
|
return -EINVAL;
|
|
}
|
|
|
|
len = min((int) (ptr - logname), buflen - 1);
|
|
|
|
memcpy(buf, logname, len);
|
|
buf[len] = '\0';
|
|
return 0;
|
|
}
|
|
|
|
void sptlrpc_conf_log_update_begin(const char *logname)
|
|
{
|
|
struct sptlrpc_conf *conf;
|
|
char fsname[16];
|
|
|
|
if (logname2fsname(logname, fsname, sizeof(fsname)))
|
|
return;
|
|
|
|
mutex_lock(&sptlrpc_conf_lock);
|
|
|
|
conf = sptlrpc_conf_get(fsname, 0);
|
|
if (conf) {
|
|
if (conf->sc_local) {
|
|
LASSERT(conf->sc_updated == 0);
|
|
sptlrpc_conf_free_rsets(conf);
|
|
}
|
|
conf->sc_modified = 0;
|
|
}
|
|
|
|
mutex_unlock(&sptlrpc_conf_lock);
|
|
}
|
|
EXPORT_SYMBOL(sptlrpc_conf_log_update_begin);
|
|
|
|
/**
|
|
* mark a config log has been updated
|
|
*/
|
|
void sptlrpc_conf_log_update_end(const char *logname)
|
|
{
|
|
struct sptlrpc_conf *conf;
|
|
char fsname[16];
|
|
|
|
if (logname2fsname(logname, fsname, sizeof(fsname)))
|
|
return;
|
|
|
|
mutex_lock(&sptlrpc_conf_lock);
|
|
|
|
conf = sptlrpc_conf_get(fsname, 0);
|
|
if (conf) {
|
|
/*
|
|
* if original state is not updated, make sure the
|
|
* modified counter > 0 to enforce updating local copy.
|
|
*/
|
|
if (conf->sc_updated == 0)
|
|
conf->sc_modified++;
|
|
|
|
conf->sc_updated = 1;
|
|
}
|
|
|
|
mutex_unlock(&sptlrpc_conf_lock);
|
|
}
|
|
EXPORT_SYMBOL(sptlrpc_conf_log_update_end);
|
|
|
|
void sptlrpc_conf_log_start(const char *logname)
|
|
{
|
|
char fsname[16];
|
|
|
|
if (logname2fsname(logname, fsname, sizeof(fsname)))
|
|
return;
|
|
|
|
mutex_lock(&sptlrpc_conf_lock);
|
|
sptlrpc_conf_get(fsname, 1);
|
|
mutex_unlock(&sptlrpc_conf_lock);
|
|
}
|
|
EXPORT_SYMBOL(sptlrpc_conf_log_start);
|
|
|
|
void sptlrpc_conf_log_stop(const char *logname)
|
|
{
|
|
struct sptlrpc_conf *conf;
|
|
char fsname[16];
|
|
|
|
if (logname2fsname(logname, fsname, sizeof(fsname)))
|
|
return;
|
|
|
|
mutex_lock(&sptlrpc_conf_lock);
|
|
conf = sptlrpc_conf_get(fsname, 0);
|
|
if (conf)
|
|
sptlrpc_conf_free(conf);
|
|
mutex_unlock(&sptlrpc_conf_lock);
|
|
}
|
|
EXPORT_SYMBOL(sptlrpc_conf_log_stop);
|
|
|
|
static inline void flavor_set_flags(struct sptlrpc_flavor *sf,
|
|
enum lustre_sec_part from,
|
|
enum lustre_sec_part to,
|
|
unsigned int fl_udesc)
|
|
{
|
|
/*
|
|
* null flavor doesn't need to set any flavor, and in fact
|
|
* we'd better not do that because everybody share a single sec.
|
|
*/
|
|
if (sf->sf_rpc == SPTLRPC_FLVR_NULL)
|
|
return;
|
|
|
|
if (from == LUSTRE_SP_MDT) {
|
|
/* MDT->MDT; MDT->OST */
|
|
sf->sf_flags |= PTLRPC_SEC_FL_ROOTONLY;
|
|
} else if (from == LUSTRE_SP_CLI && to == LUSTRE_SP_OST) {
|
|
/* CLI->OST */
|
|
sf->sf_flags |= PTLRPC_SEC_FL_ROOTONLY | PTLRPC_SEC_FL_BULK;
|
|
} else if (from == LUSTRE_SP_CLI && to == LUSTRE_SP_MDT) {
|
|
/* CLI->MDT */
|
|
if (fl_udesc && sf->sf_rpc != SPTLRPC_FLVR_NULL)
|
|
sf->sf_flags |= PTLRPC_SEC_FL_UDESC;
|
|
}
|
|
}
|
|
|
|
void sptlrpc_conf_choose_flavor(enum lustre_sec_part from,
|
|
enum lustre_sec_part to,
|
|
struct obd_uuid *target,
|
|
lnet_nid_t nid,
|
|
struct sptlrpc_flavor *sf)
|
|
{
|
|
struct sptlrpc_conf *conf;
|
|
struct sptlrpc_conf_tgt *conf_tgt;
|
|
char name[MTI_NAME_MAXLEN];
|
|
int len, rc = 0;
|
|
|
|
target2fsname(target->uuid, name, sizeof(name));
|
|
|
|
mutex_lock(&sptlrpc_conf_lock);
|
|
|
|
conf = sptlrpc_conf_get(name, 0);
|
|
if (!conf)
|
|
goto out;
|
|
|
|
/* convert uuid name (supposed end with _UUID) to target name */
|
|
len = strlen(target->uuid);
|
|
LASSERT(len > 5);
|
|
memcpy(name, target->uuid, len - 5);
|
|
name[len - 5] = '\0';
|
|
|
|
conf_tgt = sptlrpc_conf_get_tgt(conf, name, 0);
|
|
if (conf_tgt) {
|
|
rc = sptlrpc_rule_set_choose(&conf_tgt->sct_rset,
|
|
from, to, nid, sf);
|
|
if (rc)
|
|
goto out;
|
|
}
|
|
|
|
rc = sptlrpc_rule_set_choose(&conf->sc_rset, from, to, nid, sf);
|
|
out:
|
|
mutex_unlock(&sptlrpc_conf_lock);
|
|
|
|
if (rc == 0)
|
|
get_default_flavor(sf);
|
|
|
|
flavor_set_flags(sf, from, to, 1);
|
|
}
|
|
|
|
#define SEC_ADAPT_DELAY (10)
|
|
|
|
/**
|
|
* called by client devices, notify the sptlrpc config has changed and
|
|
* do import_sec_adapt later.
|
|
*/
|
|
void sptlrpc_conf_client_adapt(struct obd_device *obd)
|
|
{
|
|
struct obd_import *imp;
|
|
|
|
LASSERT(strcmp(obd->obd_type->typ_name, LUSTRE_MDC_NAME) == 0 ||
|
|
strcmp(obd->obd_type->typ_name, LUSTRE_OSC_NAME) == 0);
|
|
CDEBUG(D_SEC, "obd %s\n", obd->u.cli.cl_target_uuid.uuid);
|
|
|
|
/* serialize with connect/disconnect import */
|
|
down_read(&obd->u.cli.cl_sem);
|
|
|
|
imp = obd->u.cli.cl_import;
|
|
if (imp) {
|
|
spin_lock(&imp->imp_lock);
|
|
if (imp->imp_sec)
|
|
imp->imp_sec_expire = ktime_get_real_seconds() +
|
|
SEC_ADAPT_DELAY;
|
|
spin_unlock(&imp->imp_lock);
|
|
}
|
|
|
|
up_read(&obd->u.cli.cl_sem);
|
|
}
|
|
EXPORT_SYMBOL(sptlrpc_conf_client_adapt);
|
|
|
|
int sptlrpc_conf_init(void)
|
|
{
|
|
mutex_init(&sptlrpc_conf_lock);
|
|
return 0;
|
|
}
|
|
|
|
void sptlrpc_conf_fini(void)
|
|
{
|
|
struct sptlrpc_conf *conf, *conf_next;
|
|
|
|
mutex_lock(&sptlrpc_conf_lock);
|
|
list_for_each_entry_safe(conf, conf_next, &sptlrpc_confs, sc_list) {
|
|
sptlrpc_conf_free(conf);
|
|
}
|
|
LASSERT(list_empty(&sptlrpc_confs));
|
|
mutex_unlock(&sptlrpc_conf_lock);
|
|
}
|