diff options
author | Andreas Gruenbacher <agruen@linbit.com> | 2011-06-09 11:52:12 -0400 |
---|---|---|
committer | Philipp Reisner <philipp.reisner@linbit.com> | 2012-11-08 10:57:45 -0500 |
commit | 7c3063cc6f0e75cdf312f5f318f9a4c02e460397 (patch) | |
tree | ed54e6066aed1994577e20fc6d635d856627cbab /drivers/block/drbd/drbd_nl.c | |
parent | 789c1b626cb490acb36cf481b45040b324f60fde (diff) |
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>
Diffstat (limited to 'drivers/block/drbd/drbd_nl.c')
-rw-r--r-- | drivers/block/drbd/drbd_nl.c | 96 |
1 files changed, 76 insertions, 20 deletions
diff --git a/drivers/block/drbd/drbd_nl.c b/drivers/block/drbd/drbd_nl.c index 5b4090f52f5a..24187f1c93d5 100644 --- a/drivers/block/drbd/drbd_nl.c +++ b/drivers/block/drbd/drbd_nl.c | |||
@@ -92,7 +92,7 @@ static struct drbd_config_context { | |||
92 | #define VOLUME_UNSPECIFIED (-1U) | 92 | #define VOLUME_UNSPECIFIED (-1U) |
93 | /* pointer into the request skb, | 93 | /* pointer into the request skb, |
94 | * limited lifetime! */ | 94 | * limited lifetime! */ |
95 | char *conn_name; | 95 | char *resource_name; |
96 | 96 | ||
97 | /* reply buffer */ | 97 | /* reply buffer */ |
98 | struct sk_buff *reply_skb; | 98 | struct sk_buff *reply_skb; |
@@ -191,15 +191,15 @@ static int drbd_adm_prepare(struct sk_buff *skb, struct genl_info *info, | |||
191 | /* and assign stuff to the global adm_ctx */ | 191 | /* and assign stuff to the global adm_ctx */ |
192 | nla = nested_attr_tb[__nla_type(T_ctx_volume)]; | 192 | nla = nested_attr_tb[__nla_type(T_ctx_volume)]; |
193 | adm_ctx.volume = nla ? nla_get_u32(nla) : VOLUME_UNSPECIFIED; | 193 | adm_ctx.volume = nla ? nla_get_u32(nla) : VOLUME_UNSPECIFIED; |
194 | nla = nested_attr_tb[__nla_type(T_ctx_conn_name)]; | 194 | nla = nested_attr_tb[__nla_type(T_ctx_resource_name)]; |
195 | if (nla) | 195 | if (nla) |
196 | adm_ctx.conn_name = nla_data(nla); | 196 | adm_ctx.resource_name = nla_data(nla); |
197 | } else | 197 | } else |
198 | adm_ctx.volume = VOLUME_UNSPECIFIED; | 198 | adm_ctx.volume = VOLUME_UNSPECIFIED; |
199 | 199 | ||
200 | adm_ctx.minor = d_in->minor; | 200 | adm_ctx.minor = d_in->minor; |
201 | adm_ctx.mdev = minor_to_mdev(d_in->minor); | 201 | adm_ctx.mdev = minor_to_mdev(d_in->minor); |
202 | adm_ctx.tconn = conn_get_by_name(adm_ctx.conn_name); | 202 | adm_ctx.tconn = conn_get_by_name(adm_ctx.resource_name); |
203 | 203 | ||
204 | if (!adm_ctx.mdev && (flags & DRBD_ADM_NEED_MINOR)) { | 204 | if (!adm_ctx.mdev && (flags & DRBD_ADM_NEED_MINOR)) { |
205 | drbd_msg_put_info("unknown minor"); | 205 | drbd_msg_put_info("unknown minor"); |
@@ -214,7 +214,8 @@ static int drbd_adm_prepare(struct sk_buff *skb, struct genl_info *info, | |||
214 | if (adm_ctx.mdev && adm_ctx.tconn && | 214 | if (adm_ctx.mdev && adm_ctx.tconn && |
215 | adm_ctx.mdev->tconn != adm_ctx.tconn) { | 215 | adm_ctx.mdev->tconn != adm_ctx.tconn) { |
216 | pr_warning("request: minor=%u, conn=%s; but that minor belongs to connection %s\n", | 216 | pr_warning("request: minor=%u, conn=%s; but that minor belongs to connection %s\n", |
217 | adm_ctx.minor, adm_ctx.conn_name, adm_ctx.mdev->tconn->name); | 217 | adm_ctx.minor, adm_ctx.resource_name, |
218 | adm_ctx.mdev->tconn->name); | ||
218 | drbd_msg_put_info("minor exists in different connection"); | 219 | drbd_msg_put_info("minor exists in different connection"); |
219 | return ERR_INVALID_REQUEST; | 220 | return ERR_INVALID_REQUEST; |
220 | } | 221 | } |
@@ -239,7 +240,7 @@ fail: | |||
239 | static int drbd_adm_finish(struct genl_info *info, int retcode) | 240 | static int drbd_adm_finish(struct genl_info *info, int retcode) |
240 | { | 241 | { |
241 | struct nlattr *nla; | 242 | struct nlattr *nla; |
242 | const char *conn_name = NULL; | 243 | const char *resource_name = NULL; |
243 | 244 | ||
244 | if (adm_ctx.tconn) { | 245 | if (adm_ctx.tconn) { |
245 | kref_put(&adm_ctx.tconn->kref, &conn_destroy); | 246 | kref_put(&adm_ctx.tconn->kref, &conn_destroy); |
@@ -253,9 +254,10 @@ static int drbd_adm_finish(struct genl_info *info, int retcode) | |||
253 | 254 | ||
254 | nla = info->attrs[DRBD_NLA_CFG_CONTEXT]; | 255 | nla = info->attrs[DRBD_NLA_CFG_CONTEXT]; |
255 | if (nla) { | 256 | if (nla) { |
256 | nla = nla_find_nested(nla, __nla_type(T_ctx_conn_name)); | 257 | int maxtype = ARRAY_SIZE(drbd_cfg_context_nl_policy) - 1; |
257 | if (nla) | 258 | nla = drbd_nla_find_nested(maxtype, nla, __nla_type(T_ctx_resource_name)); |
258 | conn_name = nla_data(nla); | 259 | if (nla && !IS_ERR(nla)) |
260 | resource_name = nla_data(nla); | ||
259 | } | 261 | } |
260 | 262 | ||
261 | drbd_adm_send_reply(adm_ctx.reply_skb, info); | 263 | 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) | |||
2526 | return drbd_adm_simple_request_state(skb, info, NS(disk, D_OUTDATED)); | 2528 | return drbd_adm_simple_request_state(skb, info, NS(disk, D_OUTDATED)); |
2527 | } | 2529 | } |
2528 | 2530 | ||
2529 | int nla_put_drbd_cfg_context(struct sk_buff *skb, const char *conn_name, unsigned vnr) | 2531 | int nla_put_drbd_cfg_context(struct sk_buff *skb, const char *resource_name, unsigned vnr) |
2530 | { | 2532 | { |
2531 | struct nlattr *nla; | 2533 | struct nlattr *nla; |
2532 | nla = nla_nest_start(skb, DRBD_NLA_CFG_CONTEXT); | 2534 | 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 | |||
2534 | goto nla_put_failure; | 2536 | goto nla_put_failure; |
2535 | if (vnr != VOLUME_UNSPECIFIED) | 2537 | if (vnr != VOLUME_UNSPECIFIED) |
2536 | NLA_PUT_U32(skb, T_ctx_volume, vnr); | 2538 | NLA_PUT_U32(skb, T_ctx_volume, vnr); |
2537 | NLA_PUT_STRING(skb, T_ctx_conn_name, conn_name); | 2539 | NLA_PUT_STRING(skb, T_ctx_resource_name, resource_name); |
2538 | nla_nest_end(skb, nla); | 2540 | nla_nest_end(skb, nla); |
2539 | return 0; | 2541 | return 0; |
2540 | 2542 | ||
@@ -2778,8 +2780,9 @@ int drbd_adm_get_status_all(struct sk_buff *skb, struct netlink_callback *cb) | |||
2778 | { | 2780 | { |
2779 | const unsigned hdrlen = GENL_HDRLEN + GENL_MAGIC_FAMILY_HDRSZ; | 2781 | const unsigned hdrlen = GENL_HDRLEN + GENL_MAGIC_FAMILY_HDRSZ; |
2780 | struct nlattr *nla; | 2782 | struct nlattr *nla; |
2781 | const char *conn_name; | 2783 | const char *resource_name; |
2782 | struct drbd_tconn *tconn; | 2784 | struct drbd_tconn *tconn; |
2785 | int maxtype; | ||
2783 | 2786 | ||
2784 | /* Is this a followup call? */ | 2787 | /* Is this a followup call? */ |
2785 | if (cb->args[0]) { | 2788 | if (cb->args[0]) { |
@@ -2799,12 +2802,15 @@ int drbd_adm_get_status_all(struct sk_buff *skb, struct netlink_callback *cb) | |||
2799 | /* No explicit context given. Dump all. */ | 2802 | /* No explicit context given. Dump all. */ |
2800 | if (!nla) | 2803 | if (!nla) |
2801 | goto dump; | 2804 | goto dump; |
2802 | nla = nla_find_nested(nla, __nla_type(T_ctx_conn_name)); | 2805 | maxtype = ARRAY_SIZE(drbd_cfg_context_nl_policy) - 1; |
2806 | nla = drbd_nla_find_nested(maxtype, nla, __nla_type(T_ctx_resource_name)); | ||
2807 | if (IS_ERR(nla)) | ||
2808 | return PTR_ERR(nla); | ||
2803 | /* context given, but no name present? */ | 2809 | /* context given, but no name present? */ |
2804 | if (!nla) | 2810 | if (!nla) |
2805 | return -EINVAL; | 2811 | return -EINVAL; |
2806 | conn_name = nla_data(nla); | 2812 | resource_name = nla_data(nla); |
2807 | tconn = conn_get_by_name(conn_name); | 2813 | tconn = conn_get_by_name(resource_name); |
2808 | 2814 | ||
2809 | if (!tconn) | 2815 | if (!tconn) |
2810 | return -ENODEV; | 2816 | return -ENODEV; |
@@ -2957,16 +2963,16 @@ out_nolock: | |||
2957 | } | 2963 | } |
2958 | 2964 | ||
2959 | static enum drbd_ret_code | 2965 | static enum drbd_ret_code |
2960 | drbd_check_conn_name(const char *name) | 2966 | drbd_check_resource_name(const char *name) |
2961 | { | 2967 | { |
2962 | if (!name || !name[0]) { | 2968 | if (!name || !name[0]) { |
2963 | drbd_msg_put_info("connection name missing"); | 2969 | drbd_msg_put_info("resource name missing"); |
2964 | return ERR_MANDATORY_TAG; | 2970 | return ERR_MANDATORY_TAG; |
2965 | } | 2971 | } |
2966 | /* if we want to use these in sysfs/configfs/debugfs some day, | 2972 | /* if we want to use these in sysfs/configfs/debugfs some day, |
2967 | * we must not allow slashes */ | 2973 | * we must not allow slashes */ |
2968 | if (strchr(name, '/')) { | 2974 | if (strchr(name, '/')) { |
2969 | drbd_msg_put_info("invalid connection name"); | 2975 | drbd_msg_put_info("invalid resource name"); |
2970 | return ERR_INVALID_REQUEST; | 2976 | return ERR_INVALID_REQUEST; |
2971 | } | 2977 | } |
2972 | return NO_ERROR; | 2978 | return NO_ERROR; |
@@ -2982,7 +2988,7 @@ int drbd_adm_new_resource(struct sk_buff *skb, struct genl_info *info) | |||
2982 | if (retcode != NO_ERROR) | 2988 | if (retcode != NO_ERROR) |
2983 | goto out; | 2989 | goto out; |
2984 | 2990 | ||
2985 | retcode = drbd_check_conn_name(adm_ctx.conn_name); | 2991 | retcode = drbd_check_resource_name(adm_ctx.resource_name); |
2986 | if (retcode != NO_ERROR) | 2992 | if (retcode != NO_ERROR) |
2987 | goto out; | 2993 | goto out; |
2988 | 2994 | ||
@@ -2995,7 +3001,7 @@ int drbd_adm_new_resource(struct sk_buff *skb, struct genl_info *info) | |||
2995 | goto out; | 3001 | goto out; |
2996 | } | 3002 | } |
2997 | 3003 | ||
2998 | if (!conn_create(adm_ctx.conn_name)) | 3004 | if (!conn_create(adm_ctx.resource_name)) |
2999 | retcode = ERR_NOMEM; | 3005 | retcode = ERR_NOMEM; |
3000 | out: | 3006 | out: |
3001 | drbd_adm_finish(info, retcode); | 3007 | drbd_adm_finish(info, retcode); |
@@ -3213,3 +3219,53 @@ failed: | |||
3213 | "Event seq:%u sib_reason:%u\n", | 3219 | "Event seq:%u sib_reason:%u\n", |
3214 | err, seq, sib->sib_reason); | 3220 | err, seq, sib->sib_reason); |
3215 | } | 3221 | } |
3222 | |||
3223 | int drbd_nla_check_mandatory(int maxtype, struct nlattr *nla) | ||
3224 | { | ||
3225 | struct nlattr *head = nla_data(nla); | ||
3226 | int len = nla_len(nla); | ||
3227 | int rem; | ||
3228 | |||
3229 | /* | ||
3230 | * validate_nla (called from nla_parse_nested) ignores attributes | ||
3231 | * beyond maxtype, and does not understand the DRBD_GENLA_F_MANDATORY flag. | ||
3232 | * In order to have it validate attributes with the DRBD_GENLA_F_MANDATORY | ||
3233 | * flag set also, check and remove that flag before calling | ||
3234 | * nla_parse_nested. | ||
3235 | */ | ||
3236 | |||
3237 | nla_for_each_attr(nla, head, len, rem) { | ||
3238 | if (nla->nla_type & DRBD_GENLA_F_MANDATORY) { | ||
3239 | nla->nla_type &= ~DRBD_GENLA_F_MANDATORY; | ||
3240 | if (nla_type(nla) > maxtype) | ||
3241 | return -EOPNOTSUPP; | ||
3242 | } | ||
3243 | } | ||
3244 | return 0; | ||
3245 | } | ||
3246 | |||
3247 | int drbd_nla_parse_nested(struct nlattr *tb[], int maxtype, struct nlattr *nla, | ||
3248 | const struct nla_policy *policy) | ||
3249 | { | ||
3250 | int err; | ||
3251 | |||
3252 | err = drbd_nla_check_mandatory(maxtype, nla); | ||
3253 | if (!err) | ||
3254 | err = nla_parse_nested(tb, maxtype, nla, policy); | ||
3255 | |||
3256 | return err; | ||
3257 | } | ||
3258 | |||
3259 | struct nlattr *drbd_nla_find_nested(int maxtype, struct nlattr *nla, int attrtype) | ||
3260 | { | ||
3261 | int err; | ||
3262 | /* | ||
3263 | * If any nested attribute has the DRBD_GENLA_F_MANDATORY flag set and | ||
3264 | * we don't know about that attribute, reject all the nested | ||
3265 | * attributes. | ||
3266 | */ | ||
3267 | err = drbd_nla_check_mandatory(maxtype, nla); | ||
3268 | if (err) | ||
3269 | return ERR_PTR(err); | ||
3270 | return nla_find_nested(nla, attrtype); | ||
3271 | } | ||