Merge branch 'linus' of master.kernel.org:/pub/scm/linux/kernel/git/perex/alsa
[sfrench/cifs-2.6.git] / net / netlink / genetlink.c
index a298f77cc3e38dc2d4d7997d1ae977622ec6c746..49bc2db7982b656096f219d670e4a74c344834a7 100644 (file)
@@ -387,7 +387,10 @@ static void genl_rcv(struct sock *sk, int len)
 static int ctrl_fill_info(struct genl_family *family, u32 pid, u32 seq,
                          u32 flags, struct sk_buff *skb, u8 cmd)
 {
+       struct nlattr *nla_ops;
+       struct genl_ops *ops;
        void *hdr;
+       int idx = 1;
 
        hdr = genlmsg_put(skb, pid, seq, GENL_ID_CTRL, 0, flags, cmd,
                          family->version);
@@ -396,6 +399,37 @@ static int ctrl_fill_info(struct genl_family *family, u32 pid, u32 seq,
 
        NLA_PUT_STRING(skb, CTRL_ATTR_FAMILY_NAME, family->name);
        NLA_PUT_U16(skb, CTRL_ATTR_FAMILY_ID, family->id);
+       NLA_PUT_U32(skb, CTRL_ATTR_VERSION, family->version);
+       NLA_PUT_U32(skb, CTRL_ATTR_HDRSIZE, family->hdrsize);
+       NLA_PUT_U32(skb, CTRL_ATTR_MAXATTR, family->maxattr);
+
+       nla_ops = nla_nest_start(skb, CTRL_ATTR_OPS);
+       if (nla_ops == NULL)
+               goto nla_put_failure;
+
+       list_for_each_entry(ops, &family->ops_list, ops_list) {
+               struct nlattr *nest;
+
+               nest = nla_nest_start(skb, idx++);
+               if (nest == NULL)
+                       goto nla_put_failure;
+
+               NLA_PUT_U32(skb, CTRL_ATTR_OP_ID, ops->cmd);
+               NLA_PUT_U32(skb, CTRL_ATTR_OP_FLAGS, ops->flags);
+
+               if (ops->policy)
+                       NLA_PUT_FLAG(skb, CTRL_ATTR_OP_POLICY);
+
+               if (ops->doit)
+                       NLA_PUT_FLAG(skb, CTRL_ATTR_OP_DOIT);
+
+               if (ops->dumpit)
+                       NLA_PUT_FLAG(skb, CTRL_ATTR_OP_DUMPIT);
+
+               nla_nest_end(skb, nest);
+       }
+
+       nla_nest_end(skb, nla_ops);
 
        return genlmsg_end(skb, hdr);
 
@@ -411,6 +445,9 @@ static int ctrl_dumpfamily(struct sk_buff *skb, struct netlink_callback *cb)
        int chains_to_skip = cb->args[0];
        int fams_to_skip = cb->args[1];
 
+       if (chains_to_skip != 0)
+               genl_lock();
+
        for (i = 0; i < GENL_FAM_TAB_SIZE; i++) {
                if (i < chains_to_skip)
                        continue;
@@ -428,6 +465,9 @@ static int ctrl_dumpfamily(struct sk_buff *skb, struct netlink_callback *cb)
        }
 
 errout:
+       if (chains_to_skip != 0)
+               genl_unlock();
+
        cb->args[0] = i;
        cb->args[1] = n;
 
@@ -440,7 +480,7 @@ static struct sk_buff *ctrl_build_msg(struct genl_family *family, u32 pid,
        struct sk_buff *skb;
        int err;
 
-       skb = nlmsg_new(NLMSG_GOODSIZE);
+       skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
        if (skb == NULL)
                return ERR_PTR(-ENOBUFS);
 
@@ -455,7 +495,8 @@ static struct sk_buff *ctrl_build_msg(struct genl_family *family, u32 pid,
 
 static struct nla_policy ctrl_policy[CTRL_ATTR_MAX+1] __read_mostly = {
        [CTRL_ATTR_FAMILY_ID]   = { .type = NLA_U16 },
-       [CTRL_ATTR_FAMILY_NAME] = { .type = NLA_STRING },
+       [CTRL_ATTR_FAMILY_NAME] = { .type = NLA_NUL_STRING,
+                                   .len = GENL_NAMSIZ - 1 },
 };
 
 static int ctrl_getfamily(struct sk_buff *skb, struct genl_info *info)
@@ -470,12 +511,9 @@ static int ctrl_getfamily(struct sk_buff *skb, struct genl_info *info)
        }
 
        if (info->attrs[CTRL_ATTR_FAMILY_NAME]) {
-               char name[GENL_NAMSIZ];
-
-               if (nla_strlcpy(name, info->attrs[CTRL_ATTR_FAMILY_NAME],
-                               GENL_NAMSIZ) >= GENL_NAMSIZ)
-                       goto errout;
+               char *name;
 
+               name = nla_data(info->attrs[CTRL_ATTR_FAMILY_NAME]);
                res = genl_family_find_byname(name);
        }
 
@@ -510,7 +548,7 @@ static int genl_ctrl_event(int event, void *data)
                if (IS_ERR(msg))
                        return PTR_ERR(msg);
 
-               genlmsg_multicast(msg, 0, GENL_ID_CTRL);
+               genlmsg_multicast(msg, 0, GENL_ID_CTRL, GFP_KERNEL);
                break;
        }