netconsole: take care of NETDEV_UNREGISTER event
[linux-2.6] / drivers / staging / pohmelfs / config.c
1 /*
2  * 2007+ Copyright (c) Evgeniy Polyakov <zbr@ioremap.net>
3  * All rights reserved.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  */
15
16 #include <linux/kernel.h>
17 #include <linux/connector.h>
18 #include <linux/crypto.h>
19 #include <linux/list.h>
20 #include <linux/mutex.h>
21 #include <linux/string.h>
22 #include <linux/in.h>
23
24 #include "netfs.h"
25
26 /*
27  * Global configuration list.
28  * Each client can be asked to get one of them.
29  *
30  * Allows to provide remote server address (ipv4/v6/whatever), port
31  * and so on via kernel connector.
32  */
33
34 static struct cb_id pohmelfs_cn_id = {.idx = POHMELFS_CN_IDX, .val = POHMELFS_CN_VAL};
35 static LIST_HEAD(pohmelfs_config_list);
36 static DEFINE_MUTEX(pohmelfs_config_lock);
37
38 static inline int pohmelfs_config_eql(struct pohmelfs_ctl *sc, struct pohmelfs_ctl *ctl)
39 {
40         if (sc->idx == ctl->idx && sc->type == ctl->type &&
41                         sc->proto == ctl->proto &&
42                         sc->addrlen == ctl->addrlen &&
43                         !memcmp(&sc->addr, &ctl->addr, ctl->addrlen))
44                 return 1;
45
46         return 0;
47 }
48
49 static struct pohmelfs_config_group *pohmelfs_find_config_group(unsigned int idx)
50 {
51         struct pohmelfs_config_group *g, *group = NULL;
52
53         list_for_each_entry(g, &pohmelfs_config_list, group_entry) {
54                 if (g->idx == idx) {
55                         group = g;
56                         break;
57                 }
58         }
59
60         return group;
61 }
62
63 static struct pohmelfs_config_group *pohmelfs_find_create_config_group(unsigned int idx)
64 {
65         struct pohmelfs_config_group *g;
66
67         g = pohmelfs_find_config_group(idx);
68         if (g)
69                 return g;
70
71         g = kzalloc(sizeof(struct pohmelfs_config_group), GFP_KERNEL);
72         if (!g)
73                 return NULL;
74
75         INIT_LIST_HEAD(&g->config_list);
76         g->idx = idx;
77         g->num_entry = 0;
78
79         list_add_tail(&g->group_entry, &pohmelfs_config_list);
80
81         return g;
82 }
83
84 int pohmelfs_copy_config(struct pohmelfs_sb *psb)
85 {
86         struct pohmelfs_config_group *g;
87         struct pohmelfs_config *c, *dst;
88         int err = -ENODEV;
89
90         mutex_lock(&pohmelfs_config_lock);
91
92         g = pohmelfs_find_config_group(psb->idx);
93         if (!g)
94                 goto out_unlock;
95
96         /*
97          * Run over all entries in given config group and try to crate and
98          * initialize those, which do not exist in superblock list.
99          * Skip all existing entries.
100          */
101
102         list_for_each_entry(c, &g->config_list, config_entry) {
103                 err = 0;
104                 list_for_each_entry(dst, &psb->state_list, config_entry) {
105                         if (pohmelfs_config_eql(&dst->state.ctl, &c->state.ctl)) {
106                                 err = -EEXIST;
107                                 break;
108                         }
109                 }
110
111                 if (err)
112                         continue;
113
114                 dst = kzalloc(sizeof(struct pohmelfs_config), GFP_KERNEL);
115                 if (!dst) {
116                         err = -ENOMEM;
117                         break;
118                 }
119
120                 memcpy(&dst->state.ctl, &c->state.ctl, sizeof(struct pohmelfs_ctl));
121
122                 list_add_tail(&dst->config_entry, &psb->state_list);
123
124                 err = pohmelfs_state_init_one(psb, dst);
125                 if (err) {
126                         list_del(&dst->config_entry);
127                         kfree(dst);
128                 }
129
130                 err = 0;
131         }
132
133 out_unlock:
134         mutex_unlock(&pohmelfs_config_lock);
135
136         return err;
137 }
138
139 int pohmelfs_copy_crypto(struct pohmelfs_sb *psb)
140 {
141         struct pohmelfs_config_group *g;
142         int err = -ENOENT;
143
144         mutex_lock(&pohmelfs_config_lock);
145         g = pohmelfs_find_config_group(psb->idx);
146         if (!g)
147                 goto err_out_exit;
148
149         if (g->hash_string) {
150                 err = -ENOMEM;
151                 psb->hash_string = kstrdup(g->hash_string, GFP_KERNEL);
152                 if (!psb->hash_string)
153                         goto err_out_exit;
154                 psb->hash_strlen = g->hash_strlen;
155         }
156
157         if (g->cipher_string) {
158                 psb->cipher_string = kstrdup(g->cipher_string, GFP_KERNEL);
159                 if (!psb->cipher_string)
160                         goto err_out_free_hash_string;
161                 psb->cipher_strlen = g->cipher_strlen;
162         }
163
164         if (g->hash_keysize) {
165                 psb->hash_key = kmalloc(g->hash_keysize, GFP_KERNEL);
166                 if (!psb->hash_key)
167                         goto err_out_free_cipher_string;
168                 memcpy(psb->hash_key, g->hash_key, g->hash_keysize);
169                 psb->hash_keysize = g->hash_keysize;
170         }
171
172         if (g->cipher_keysize) {
173                 psb->cipher_key = kmalloc(g->cipher_keysize, GFP_KERNEL);
174                 if (!psb->cipher_key)
175                         goto err_out_free_hash;
176                 memcpy(psb->cipher_key, g->cipher_key, g->cipher_keysize);
177                 psb->cipher_keysize = g->cipher_keysize;
178         }
179
180         mutex_unlock(&pohmelfs_config_lock);
181
182         return 0;
183
184 err_out_free_hash:
185         kfree(psb->hash_key);
186 err_out_free_cipher_string:
187         kfree(psb->cipher_string);
188 err_out_free_hash_string:
189         kfree(psb->hash_string);
190 err_out_exit:
191         mutex_unlock(&pohmelfs_config_lock);
192         return err;
193 }
194
195 static int pohmelfs_send_reply(int err, int msg_num, int action, struct cn_msg *msg, struct pohmelfs_ctl *ctl)
196 {
197         struct pohmelfs_cn_ack *ack;
198
199         ack = kmalloc(sizeof(struct pohmelfs_cn_ack), GFP_KERNEL);
200         if (!ack)
201                 return -ENOMEM;
202
203         memset(ack, 0, sizeof(struct pohmelfs_cn_ack));
204         memcpy(&ack->msg, msg, sizeof(struct cn_msg));
205
206         if (action == POHMELFS_CTLINFO_ACK)
207                 memcpy(&ack->ctl, ctl, sizeof(struct pohmelfs_ctl));
208
209         ack->msg.len = sizeof(struct pohmelfs_cn_ack) - sizeof(struct cn_msg);
210         ack->msg.ack = msg->ack + 1;
211         ack->error = err;
212         ack->msg_num = msg_num;
213
214         cn_netlink_send(&ack->msg, 0, GFP_KERNEL);
215         kfree(ack);
216         return 0;
217 }
218
219 static int pohmelfs_cn_disp(struct cn_msg *msg)
220 {
221         struct pohmelfs_config_group *g;
222         struct pohmelfs_ctl *ctl = (struct pohmelfs_ctl *)msg->data;
223         struct pohmelfs_config *c, *tmp;
224         int err = 0, i = 1;
225
226         if (msg->len != sizeof(struct pohmelfs_ctl))
227                 return -EBADMSG;
228
229         mutex_lock(&pohmelfs_config_lock);
230
231         g = pohmelfs_find_config_group(ctl->idx);
232         if (!g) {
233                 pohmelfs_send_reply(err, 0, POHMELFS_NOINFO_ACK, msg, NULL);
234                 goto out_unlock;
235         }
236
237         list_for_each_entry_safe(c, tmp, &g->config_list, config_entry) {
238                 struct pohmelfs_ctl *sc = &c->state.ctl;
239                 if (pohmelfs_send_reply(err, g->num_entry - i, POHMELFS_CTLINFO_ACK, msg, sc)) {
240                         err = -ENOMEM;
241                         goto out_unlock;
242                 }
243                 i += 1;
244         }
245
246 out_unlock:
247         mutex_unlock(&pohmelfs_config_lock);
248         return err;
249 }
250
251 static int pohmelfs_cn_ctl(struct cn_msg *msg, int action)
252 {
253         struct pohmelfs_config_group *g;
254         struct pohmelfs_ctl *ctl = (struct pohmelfs_ctl *)msg->data;
255         struct pohmelfs_config *c, *tmp;
256         int err = 0;
257
258         if (msg->len != sizeof(struct pohmelfs_ctl))
259                 return -EBADMSG;
260
261         mutex_lock(&pohmelfs_config_lock);
262
263         g = pohmelfs_find_create_config_group(ctl->idx);
264         if (!g) {
265                 err = -ENOMEM;
266                 goto out_unlock;
267         }
268
269         list_for_each_entry_safe(c, tmp, &g->config_list, config_entry) {
270                 struct pohmelfs_ctl *sc = &c->state.ctl;
271
272                 if (pohmelfs_config_eql(sc, ctl)) {
273                         if (action == POHMELFS_FLAGS_ADD) {
274                                 err = -EEXIST;
275                                 goto out_unlock;
276                         } else if (action == POHMELFS_FLAGS_DEL) {
277                                 list_del(&c->config_entry);
278                                 g->num_entry--;
279                                 kfree(c);
280                                 goto out_unlock;
281                         } else {
282                                 err = -EEXIST;
283                                 goto out_unlock;
284                         }
285                 }
286         }
287         if (action == POHMELFS_FLAGS_DEL) {
288                 err = -EBADMSG;
289                 goto out_unlock;
290         }
291
292         c = kzalloc(sizeof(struct pohmelfs_config), GFP_KERNEL);
293         if (!c) {
294                 err = -ENOMEM;
295                 goto out_unlock;
296         }
297         memcpy(&c->state.ctl, ctl, sizeof(struct pohmelfs_ctl));
298         g->num_entry++;
299         list_add_tail(&c->config_entry, &g->config_list);
300
301 out_unlock:
302         mutex_unlock(&pohmelfs_config_lock);
303         if (pohmelfs_send_reply(err, 0, POHMELFS_NOINFO_ACK, msg, NULL))
304                 err = -ENOMEM;
305
306         return err;
307 }
308
309 static int pohmelfs_crypto_hash_init(struct pohmelfs_config_group *g, struct pohmelfs_crypto *c)
310 {
311         char *algo = (char *)c->data;
312         u8 *key = (u8 *)(algo + c->strlen);
313
314         if (g->hash_string)
315                 return -EEXIST;
316
317         g->hash_string = kstrdup(algo, GFP_KERNEL);
318         if (!g->hash_string)
319                 return -ENOMEM;
320         g->hash_strlen = c->strlen;
321         g->hash_keysize = c->keysize;
322
323         g->hash_key = kmalloc(c->keysize, GFP_KERNEL);
324         if (!g->hash_key) {
325                 kfree(g->hash_string);
326                 return -ENOMEM;
327         }
328
329         memcpy(g->hash_key, key, c->keysize);
330
331         return 0;
332 }
333
334 static int pohmelfs_crypto_cipher_init(struct pohmelfs_config_group *g, struct pohmelfs_crypto *c)
335 {
336         char *algo = (char *)c->data;
337         u8 *key = (u8 *)(algo + c->strlen);
338
339         if (g->cipher_string)
340                 return -EEXIST;
341
342         g->cipher_string = kstrdup(algo, GFP_KERNEL);
343         if (!g->cipher_string)
344                 return -ENOMEM;
345         g->cipher_strlen = c->strlen;
346         g->cipher_keysize = c->keysize;
347
348         g->cipher_key = kmalloc(c->keysize, GFP_KERNEL);
349         if (!g->cipher_key) {
350                 kfree(g->cipher_string);
351                 return -ENOMEM;
352         }
353
354         memcpy(g->cipher_key, key, c->keysize);
355
356         return 0;
357 }
358
359
360 static int pohmelfs_cn_crypto(struct cn_msg *msg)
361 {
362         struct pohmelfs_crypto *crypto = (struct pohmelfs_crypto *)msg->data;
363         struct pohmelfs_config_group *g;
364         int err = 0;
365
366         dprintk("%s: idx: %u, strlen: %u, type: %u, keysize: %u, algo: %s.\n",
367                         __func__, crypto->idx, crypto->strlen, crypto->type,
368                         crypto->keysize, (char *)crypto->data);
369
370         mutex_lock(&pohmelfs_config_lock);
371         g = pohmelfs_find_create_config_group(crypto->idx);
372         if (!g) {
373                 err = -ENOMEM;
374                 goto out_unlock;
375         }
376
377         switch (crypto->type) {
378                 case POHMELFS_CRYPTO_HASH:
379                         err = pohmelfs_crypto_hash_init(g, crypto);
380                         break;
381                 case POHMELFS_CRYPTO_CIPHER:
382                         err = pohmelfs_crypto_cipher_init(g, crypto);
383                         break;
384                 default:
385                         err = -ENOTSUPP;
386                         break;
387         }
388
389 out_unlock:
390         mutex_unlock(&pohmelfs_config_lock);
391         if (pohmelfs_send_reply(err, 0, POHMELFS_NOINFO_ACK, msg, NULL))
392                 err = -ENOMEM;
393
394         return err;
395 }
396
397 static void pohmelfs_cn_callback(void *data)
398 {
399         struct cn_msg *msg = data;
400         int err;
401
402         switch (msg->flags) {
403                 case POHMELFS_FLAGS_ADD:
404                         err = pohmelfs_cn_ctl(msg, POHMELFS_FLAGS_ADD);
405                         break;
406                 case POHMELFS_FLAGS_DEL:
407                         err = pohmelfs_cn_ctl(msg, POHMELFS_FLAGS_DEL);
408                         break;
409                 case POHMELFS_FLAGS_SHOW:
410                         err = pohmelfs_cn_disp(msg);
411                         break;
412                 case POHMELFS_FLAGS_CRYPTO:
413                         err = pohmelfs_cn_crypto(msg);
414                         break;
415                 default:
416                         err = -ENOSYS;
417                         break;
418         }
419 }
420
421 int pohmelfs_config_check(struct pohmelfs_config *config, int idx)
422 {
423         struct pohmelfs_ctl *ctl = &config->state.ctl;
424         struct pohmelfs_config *tmp;
425         int err = -ENOENT;
426         struct pohmelfs_ctl *sc;
427         struct pohmelfs_config_group *g;
428
429         mutex_lock(&pohmelfs_config_lock);
430
431         g = pohmelfs_find_config_group(ctl->idx);
432         if (g) {
433                 list_for_each_entry(tmp, &g->config_list, config_entry) {
434                         sc = &tmp->state.ctl;
435
436                         if (pohmelfs_config_eql(sc, ctl)) {
437                                 err = 0;
438                                 break;
439                         }
440                 }
441         }
442
443         mutex_unlock(&pohmelfs_config_lock);
444
445         return err;
446 }
447
448 int __init pohmelfs_config_init(void)
449 {
450         return cn_add_callback(&pohmelfs_cn_id, "pohmelfs", pohmelfs_cn_callback);
451 }
452
453 void pohmelfs_config_exit(void)
454 {
455         struct pohmelfs_config *c, *tmp;
456         struct pohmelfs_config_group *g, *gtmp;
457
458         cn_del_callback(&pohmelfs_cn_id);
459
460         mutex_lock(&pohmelfs_config_lock);
461         list_for_each_entry_safe(g, gtmp, &pohmelfs_config_list, group_entry) {
462                 list_for_each_entry_safe(c, tmp, &g->config_list, config_entry) {
463                         list_del(&c->config_entry);
464                         kfree(c);
465                 }
466
467                 list_del(&g->group_entry);
468
469                 if (g->hash_string)
470                         kfree(g->hash_string);
471
472                 if (g->cipher_string)
473                         kfree(g->cipher_string);
474
475                 kfree(g);
476         }
477         mutex_unlock(&pohmelfs_config_lock);
478 }