drbd: Also need to check for DRBD_GENLA_F_MANDATORY flags before nla_find_nested()
This is done by introducing drbd_nla_find_nested() which handles the flag before calling nla_find_nested(). Signed-off-by: Philipp Reisner <philipp.reisner@linbit.com> Signed-off-by: Lars Ellenberg <lars.ellenberg@linbit.com>
This commit is contained in:
parent
789c1b626c
commit
7c3063cc6f
4 changed files with 83 additions and 58 deletions
|
@ -1407,6 +1407,12 @@ extern bool conn_try_outdate_peer(struct drbd_tconn *tconn);
|
||||||
extern void conn_try_outdate_peer_async(struct drbd_tconn *tconn);
|
extern void conn_try_outdate_peer_async(struct drbd_tconn *tconn);
|
||||||
extern int drbd_khelper(struct drbd_conf *mdev, char *cmd);
|
extern int drbd_khelper(struct drbd_conf *mdev, char *cmd);
|
||||||
|
|
||||||
|
struct nla_policy;
|
||||||
|
extern int drbd_nla_check_mandatory(int maxtype, struct nlattr *nla);
|
||||||
|
extern int drbd_nla_parse_nested(struct nlattr *tb[], int maxtype, struct nlattr *nla,
|
||||||
|
const struct nla_policy *policy);
|
||||||
|
extern struct nlattr *drbd_nla_find_nested(int maxtype, struct nlattr *nla, int attrtype);
|
||||||
|
|
||||||
/* drbd_worker.c */
|
/* drbd_worker.c */
|
||||||
extern int drbd_worker(struct drbd_thread *thi);
|
extern int drbd_worker(struct drbd_thread *thi);
|
||||||
enum drbd_ret_code drbd_resync_after_valid(struct drbd_conf *mdev, int o_minor);
|
enum drbd_ret_code drbd_resync_after_valid(struct drbd_conf *mdev, int o_minor);
|
||||||
|
|
|
@ -92,7 +92,7 @@ static struct drbd_config_context {
|
||||||
#define VOLUME_UNSPECIFIED (-1U)
|
#define VOLUME_UNSPECIFIED (-1U)
|
||||||
/* pointer into the request skb,
|
/* pointer into the request skb,
|
||||||
* limited lifetime! */
|
* limited lifetime! */
|
||||||
char *conn_name;
|
char *resource_name;
|
||||||
|
|
||||||
/* reply buffer */
|
/* reply buffer */
|
||||||
struct sk_buff *reply_skb;
|
struct sk_buff *reply_skb;
|
||||||
|
@ -191,15 +191,15 @@ static int drbd_adm_prepare(struct sk_buff *skb, struct genl_info *info,
|
||||||
/* and assign stuff to the global adm_ctx */
|
/* and assign stuff to the global adm_ctx */
|
||||||
nla = nested_attr_tb[__nla_type(T_ctx_volume)];
|
nla = nested_attr_tb[__nla_type(T_ctx_volume)];
|
||||||
adm_ctx.volume = nla ? nla_get_u32(nla) : VOLUME_UNSPECIFIED;
|
adm_ctx.volume = nla ? nla_get_u32(nla) : VOLUME_UNSPECIFIED;
|
||||||
nla = nested_attr_tb[__nla_type(T_ctx_conn_name)];
|
nla = nested_attr_tb[__nla_type(T_ctx_resource_name)];
|
||||||
if (nla)
|
if (nla)
|
||||||
adm_ctx.conn_name = nla_data(nla);
|
adm_ctx.resource_name = nla_data(nla);
|
||||||
} else
|
} else
|
||||||
adm_ctx.volume = VOLUME_UNSPECIFIED;
|
adm_ctx.volume = VOLUME_UNSPECIFIED;
|
||||||
|
|
||||||
adm_ctx.minor = d_in->minor;
|
adm_ctx.minor = d_in->minor;
|
||||||
adm_ctx.mdev = minor_to_mdev(d_in->minor);
|
adm_ctx.mdev = minor_to_mdev(d_in->minor);
|
||||||
adm_ctx.tconn = conn_get_by_name(adm_ctx.conn_name);
|
adm_ctx.tconn = conn_get_by_name(adm_ctx.resource_name);
|
||||||
|
|
||||||
if (!adm_ctx.mdev && (flags & DRBD_ADM_NEED_MINOR)) {
|
if (!adm_ctx.mdev && (flags & DRBD_ADM_NEED_MINOR)) {
|
||||||
drbd_msg_put_info("unknown minor");
|
drbd_msg_put_info("unknown minor");
|
||||||
|
@ -214,7 +214,8 @@ static int drbd_adm_prepare(struct sk_buff *skb, struct genl_info *info,
|
||||||
if (adm_ctx.mdev && adm_ctx.tconn &&
|
if (adm_ctx.mdev && adm_ctx.tconn &&
|
||||||
adm_ctx.mdev->tconn != adm_ctx.tconn) {
|
adm_ctx.mdev->tconn != adm_ctx.tconn) {
|
||||||
pr_warning("request: minor=%u, conn=%s; but that minor belongs to connection %s\n",
|
pr_warning("request: minor=%u, conn=%s; but that minor belongs to connection %s\n",
|
||||||
adm_ctx.minor, adm_ctx.conn_name, adm_ctx.mdev->tconn->name);
|
adm_ctx.minor, adm_ctx.resource_name,
|
||||||
|
adm_ctx.mdev->tconn->name);
|
||||||
drbd_msg_put_info("minor exists in different connection");
|
drbd_msg_put_info("minor exists in different connection");
|
||||||
return ERR_INVALID_REQUEST;
|
return ERR_INVALID_REQUEST;
|
||||||
}
|
}
|
||||||
|
@ -239,7 +240,7 @@ static int drbd_adm_prepare(struct sk_buff *skb, struct genl_info *info,
|
||||||
static int drbd_adm_finish(struct genl_info *info, int retcode)
|
static int drbd_adm_finish(struct genl_info *info, int retcode)
|
||||||
{
|
{
|
||||||
struct nlattr *nla;
|
struct nlattr *nla;
|
||||||
const char *conn_name = NULL;
|
const char *resource_name = NULL;
|
||||||
|
|
||||||
if (adm_ctx.tconn) {
|
if (adm_ctx.tconn) {
|
||||||
kref_put(&adm_ctx.tconn->kref, &conn_destroy);
|
kref_put(&adm_ctx.tconn->kref, &conn_destroy);
|
||||||
|
@ -253,9 +254,10 @@ static int drbd_adm_finish(struct genl_info *info, int retcode)
|
||||||
|
|
||||||
nla = info->attrs[DRBD_NLA_CFG_CONTEXT];
|
nla = info->attrs[DRBD_NLA_CFG_CONTEXT];
|
||||||
if (nla) {
|
if (nla) {
|
||||||
nla = nla_find_nested(nla, __nla_type(T_ctx_conn_name));
|
int maxtype = ARRAY_SIZE(drbd_cfg_context_nl_policy) - 1;
|
||||||
if (nla)
|
nla = drbd_nla_find_nested(maxtype, nla, __nla_type(T_ctx_resource_name));
|
||||||
conn_name = nla_data(nla);
|
if (nla && !IS_ERR(nla))
|
||||||
|
resource_name = nla_data(nla);
|
||||||
}
|
}
|
||||||
|
|
||||||
drbd_adm_send_reply(adm_ctx.reply_skb, info);
|
drbd_adm_send_reply(adm_ctx.reply_skb, info);
|
||||||
|
@ -2526,7 +2528,7 @@ int drbd_adm_outdate(struct sk_buff *skb, struct genl_info *info)
|
||||||
return drbd_adm_simple_request_state(skb, info, NS(disk, D_OUTDATED));
|
return drbd_adm_simple_request_state(skb, info, NS(disk, D_OUTDATED));
|
||||||
}
|
}
|
||||||
|
|
||||||
int nla_put_drbd_cfg_context(struct sk_buff *skb, const char *conn_name, unsigned vnr)
|
int nla_put_drbd_cfg_context(struct sk_buff *skb, const char *resource_name, unsigned vnr)
|
||||||
{
|
{
|
||||||
struct nlattr *nla;
|
struct nlattr *nla;
|
||||||
nla = nla_nest_start(skb, DRBD_NLA_CFG_CONTEXT);
|
nla = nla_nest_start(skb, DRBD_NLA_CFG_CONTEXT);
|
||||||
|
@ -2534,7 +2536,7 @@ int nla_put_drbd_cfg_context(struct sk_buff *skb, const char *conn_name, unsigne
|
||||||
goto nla_put_failure;
|
goto nla_put_failure;
|
||||||
if (vnr != VOLUME_UNSPECIFIED)
|
if (vnr != VOLUME_UNSPECIFIED)
|
||||||
NLA_PUT_U32(skb, T_ctx_volume, vnr);
|
NLA_PUT_U32(skb, T_ctx_volume, vnr);
|
||||||
NLA_PUT_STRING(skb, T_ctx_conn_name, conn_name);
|
NLA_PUT_STRING(skb, T_ctx_resource_name, resource_name);
|
||||||
nla_nest_end(skb, nla);
|
nla_nest_end(skb, nla);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
@ -2778,8 +2780,9 @@ int drbd_adm_get_status_all(struct sk_buff *skb, struct netlink_callback *cb)
|
||||||
{
|
{
|
||||||
const unsigned hdrlen = GENL_HDRLEN + GENL_MAGIC_FAMILY_HDRSZ;
|
const unsigned hdrlen = GENL_HDRLEN + GENL_MAGIC_FAMILY_HDRSZ;
|
||||||
struct nlattr *nla;
|
struct nlattr *nla;
|
||||||
const char *conn_name;
|
const char *resource_name;
|
||||||
struct drbd_tconn *tconn;
|
struct drbd_tconn *tconn;
|
||||||
|
int maxtype;
|
||||||
|
|
||||||
/* Is this a followup call? */
|
/* Is this a followup call? */
|
||||||
if (cb->args[0]) {
|
if (cb->args[0]) {
|
||||||
|
@ -2799,12 +2802,15 @@ int drbd_adm_get_status_all(struct sk_buff *skb, struct netlink_callback *cb)
|
||||||
/* No explicit context given. Dump all. */
|
/* No explicit context given. Dump all. */
|
||||||
if (!nla)
|
if (!nla)
|
||||||
goto dump;
|
goto dump;
|
||||||
nla = nla_find_nested(nla, __nla_type(T_ctx_conn_name));
|
maxtype = ARRAY_SIZE(drbd_cfg_context_nl_policy) - 1;
|
||||||
|
nla = drbd_nla_find_nested(maxtype, nla, __nla_type(T_ctx_resource_name));
|
||||||
|
if (IS_ERR(nla))
|
||||||
|
return PTR_ERR(nla);
|
||||||
/* context given, but no name present? */
|
/* context given, but no name present? */
|
||||||
if (!nla)
|
if (!nla)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
conn_name = nla_data(nla);
|
resource_name = nla_data(nla);
|
||||||
tconn = conn_get_by_name(conn_name);
|
tconn = conn_get_by_name(resource_name);
|
||||||
|
|
||||||
if (!tconn)
|
if (!tconn)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
@ -2957,16 +2963,16 @@ int drbd_adm_new_c_uuid(struct sk_buff *skb, struct genl_info *info)
|
||||||
}
|
}
|
||||||
|
|
||||||
static enum drbd_ret_code
|
static enum drbd_ret_code
|
||||||
drbd_check_conn_name(const char *name)
|
drbd_check_resource_name(const char *name)
|
||||||
{
|
{
|
||||||
if (!name || !name[0]) {
|
if (!name || !name[0]) {
|
||||||
drbd_msg_put_info("connection name missing");
|
drbd_msg_put_info("resource name missing");
|
||||||
return ERR_MANDATORY_TAG;
|
return ERR_MANDATORY_TAG;
|
||||||
}
|
}
|
||||||
/* if we want to use these in sysfs/configfs/debugfs some day,
|
/* if we want to use these in sysfs/configfs/debugfs some day,
|
||||||
* we must not allow slashes */
|
* we must not allow slashes */
|
||||||
if (strchr(name, '/')) {
|
if (strchr(name, '/')) {
|
||||||
drbd_msg_put_info("invalid connection name");
|
drbd_msg_put_info("invalid resource name");
|
||||||
return ERR_INVALID_REQUEST;
|
return ERR_INVALID_REQUEST;
|
||||||
}
|
}
|
||||||
return NO_ERROR;
|
return NO_ERROR;
|
||||||
|
@ -2982,7 +2988,7 @@ int drbd_adm_new_resource(struct sk_buff *skb, struct genl_info *info)
|
||||||
if (retcode != NO_ERROR)
|
if (retcode != NO_ERROR)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
retcode = drbd_check_conn_name(adm_ctx.conn_name);
|
retcode = drbd_check_resource_name(adm_ctx.resource_name);
|
||||||
if (retcode != NO_ERROR)
|
if (retcode != NO_ERROR)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
@ -2995,7 +3001,7 @@ int drbd_adm_new_resource(struct sk_buff *skb, struct genl_info *info)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!conn_create(adm_ctx.conn_name))
|
if (!conn_create(adm_ctx.resource_name))
|
||||||
retcode = ERR_NOMEM;
|
retcode = ERR_NOMEM;
|
||||||
out:
|
out:
|
||||||
drbd_adm_finish(info, retcode);
|
drbd_adm_finish(info, retcode);
|
||||||
|
@ -3213,3 +3219,53 @@ void drbd_bcast_event(struct drbd_conf *mdev, const struct sib_info *sib)
|
||||||
"Event seq:%u sib_reason:%u\n",
|
"Event seq:%u sib_reason:%u\n",
|
||||||
err, seq, sib->sib_reason);
|
err, seq, sib->sib_reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int drbd_nla_check_mandatory(int maxtype, struct nlattr *nla)
|
||||||
|
{
|
||||||
|
struct nlattr *head = nla_data(nla);
|
||||||
|
int len = nla_len(nla);
|
||||||
|
int rem;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* validate_nla (called from nla_parse_nested) ignores attributes
|
||||||
|
* beyond maxtype, and does not understand the DRBD_GENLA_F_MANDATORY flag.
|
||||||
|
* In order to have it validate attributes with the DRBD_GENLA_F_MANDATORY
|
||||||
|
* flag set also, check and remove that flag before calling
|
||||||
|
* nla_parse_nested.
|
||||||
|
*/
|
||||||
|
|
||||||
|
nla_for_each_attr(nla, head, len, rem) {
|
||||||
|
if (nla->nla_type & DRBD_GENLA_F_MANDATORY) {
|
||||||
|
nla->nla_type &= ~DRBD_GENLA_F_MANDATORY;
|
||||||
|
if (nla_type(nla) > maxtype)
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int drbd_nla_parse_nested(struct nlattr *tb[], int maxtype, struct nlattr *nla,
|
||||||
|
const struct nla_policy *policy)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = drbd_nla_check_mandatory(maxtype, nla);
|
||||||
|
if (!err)
|
||||||
|
err = nla_parse_nested(tb, maxtype, nla, policy);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct nlattr *drbd_nla_find_nested(int maxtype, struct nlattr *nla, int attrtype)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
/*
|
||||||
|
* If any nested attribute has the DRBD_GENLA_F_MANDATORY flag set and
|
||||||
|
* we don't know about that attribute, reject all the nested
|
||||||
|
* attributes.
|
||||||
|
*/
|
||||||
|
err = drbd_nla_check_mandatory(maxtype, nla);
|
||||||
|
if (err)
|
||||||
|
return ERR_PTR(err);
|
||||||
|
return nla_find_nested(nla, attrtype);
|
||||||
|
}
|
||||||
|
|
|
@ -96,7 +96,7 @@ GENL_struct(DRBD_NLA_CFG_REPLY, 1, drbd_cfg_reply,
|
||||||
* and the volume id within the resource. */
|
* and the volume id within the resource. */
|
||||||
GENL_struct(DRBD_NLA_CFG_CONTEXT, 2, drbd_cfg_context,
|
GENL_struct(DRBD_NLA_CFG_CONTEXT, 2, drbd_cfg_context,
|
||||||
__u32_field(1, DRBD_GENLA_F_MANDATORY, ctx_volume)
|
__u32_field(1, DRBD_GENLA_F_MANDATORY, ctx_volume)
|
||||||
__str_field(2, DRBD_GENLA_F_MANDATORY, ctx_conn_name, 128)
|
__str_field(2, DRBD_GENLA_F_MANDATORY, ctx_resource_name, 128)
|
||||||
)
|
)
|
||||||
|
|
||||||
GENL_struct(DRBD_NLA_DISK_CONF, 3, disk_conf,
|
GENL_struct(DRBD_NLA_DISK_CONF, 3, disk_conf,
|
||||||
|
|
|
@ -142,43 +142,6 @@ static struct nlattr *nested_attr_tb[128];
|
||||||
#define BUILD_BUG_ON_NULL(e) ((void *)sizeof(struct { int:-!!(e); }))
|
#define BUILD_BUG_ON_NULL(e) ((void *)sizeof(struct { int:-!!(e); }))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static inline int drbd_nla_check_mandatory(int maxtype, struct nlattr *nla)
|
|
||||||
{
|
|
||||||
struct nlattr *head = nla_data(nla);
|
|
||||||
int len = nla_len(nla);
|
|
||||||
int rem;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* validate_nla (called from nla_parse_nested) ignores attributes
|
|
||||||
* beyond maxtype, and does not understand the DRBD_GENLA_F_MANDATORY flag.
|
|
||||||
* In order to have it validate attributes with the DRBD_GENLA_F_MANDATORY
|
|
||||||
* flag set also, check and remove that flag before calling
|
|
||||||
* nla_parse_nested.
|
|
||||||
*/
|
|
||||||
|
|
||||||
nla_for_each_attr(nla, head, len, rem) {
|
|
||||||
if (nla->nla_type & DRBD_GENLA_F_MANDATORY) {
|
|
||||||
nla->nla_type &= ~DRBD_GENLA_F_MANDATORY;
|
|
||||||
if (nla_type(nla) > maxtype)
|
|
||||||
return -EOPNOTSUPP;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int drbd_nla_parse_nested(struct nlattr *tb[], int maxtype,
|
|
||||||
struct nlattr *nla,
|
|
||||||
const struct nla_policy *policy)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
|
|
||||||
err = drbd_nla_check_mandatory(maxtype, nla);
|
|
||||||
if (!err)
|
|
||||||
err = nla_parse_nested(tb, maxtype, nla, policy);
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
#undef GENL_struct
|
#undef GENL_struct
|
||||||
#define GENL_struct(tag_name, tag_number, s_name, s_fields) \
|
#define GENL_struct(tag_name, tag_number, s_name, s_fields) \
|
||||||
/* *_from_attrs functions are static, but potentially unused */ \
|
/* *_from_attrs functions are static, but potentially unused */ \
|
||||||
|
|
Loading…
Reference in a new issue