add missing null pointer check after malloc
[project/uci.git] / ucimap.c
1 /*
2  * ucimap - library for mapping uci sections into data structures
3  * Copyright (C) 2008 Felix Fietkau <nbd@openwrt.org>
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 version 2
7  * as published by the Free Software Foundation
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  */
14 #include <strings.h>
15 #include <stdbool.h>
16 #include <string.h>
17 #include <stdlib.h>
18 #include <unistd.h>
19 #include "ucimap.h"
20
21 struct uci_alloc {
22         enum ucimap_type type;
23         union {
24                 void **ptr;
25         } data;
26 };
27
28 struct uci_fixup {
29         struct list_head list;
30         struct uci_sectionmap *sm;
31         const char *name;
32         enum ucimap_type type;
33         union ucimap_data *data;
34 };
35
36 #define ucimap_foreach_option(_sm, _o) \
37         if (!(_sm)->options_size) \
38                 (_sm)->options_size = sizeof(struct uci_optmap); \
39         for (_o = &(_sm)->options[0]; \
40                  ((char *)(_o)) < ((char *) &(_sm)->options[0] + \
41                         (_sm)->options_size * (_sm)->n_options); \
42                  _o = (struct uci_optmap *) ((char *)(_o) + \
43                         (_sm)->options_size))
44
45
46 static inline bool
47 ucimap_is_alloc(enum ucimap_type type)
48 {
49         switch(type & UCIMAP_SUBTYPE) {
50         case UCIMAP_STRING:
51                 return true;
52         default:
53                 return false;
54         }
55 }
56
57 static inline bool
58 ucimap_is_fixup(enum ucimap_type type)
59 {
60         switch(type & UCIMAP_SUBTYPE) {
61         case UCIMAP_SECTION:
62                 return true;
63         default:
64                 return false;
65         }
66 }
67
68 static inline bool
69 ucimap_is_simple(enum ucimap_type type)
70 {
71         return ((type & UCIMAP_TYPE) == UCIMAP_SIMPLE);
72 }
73
74 static inline bool
75 ucimap_is_list(enum ucimap_type type)
76 {
77         return ((type & UCIMAP_TYPE) == UCIMAP_LIST);
78 }
79
80 static inline bool
81 ucimap_is_custom(enum ucimap_type type)
82 {
83         return ((type & UCIMAP_SUBTYPE) == UCIMAP_CUSTOM);
84 }
85
86 static inline void *
87 ucimap_section_ptr(struct ucimap_section_data *sd)
88 {
89         return ((char *) sd - sd->sm->smap_offset);
90 }
91
92 static inline union ucimap_data *
93 ucimap_get_data(struct ucimap_section_data *sd, struct uci_optmap *om)
94 {
95         void *data;
96
97         data = (char *) ucimap_section_ptr(sd) + om->offset;
98         return data;
99 }
100
101 int
102 ucimap_init(struct uci_map *map)
103 {
104         INIT_LIST_HEAD(&map->sdata);
105         INIT_LIST_HEAD(&map->fixup);
106         return 0;
107 }
108
109 static void
110 ucimap_free_item(struct uci_alloc *a)
111 {
112         switch(a->type & UCIMAP_TYPE) {
113         case UCIMAP_SIMPLE:
114         case UCIMAP_LIST:
115                 free(a->data.ptr);
116                 break;
117         }
118 }
119
120 static void
121 ucimap_add_alloc(struct ucimap_section_data *sd, void *ptr)
122 {
123         struct uci_alloc *a = &sd->allocmap[sd->allocmap_len++];
124         a->type = UCIMAP_SIMPLE;
125         a->data.ptr = ptr;
126 }
127
128 static void
129 ucimap_free_section(struct uci_map *map, struct ucimap_section_data *sd)
130 {
131         void *section;
132         int i;
133
134         section = ucimap_section_ptr(sd);
135         if (!list_empty(&sd->list))
136                 list_del(&sd->list);
137
138         if (sd->sm->free)
139                 sd->sm->free(map, section);
140
141         for (i = 0; i < sd->allocmap_len; i++) {
142                 ucimap_free_item(&sd->allocmap[i]);
143         }
144
145         free(sd->allocmap);
146         free(sd);
147 }
148
149 void
150 ucimap_cleanup(struct uci_map *map)
151 {
152         struct list_head *ptr, *tmp;
153
154         list_for_each_safe(ptr, tmp, &map->sdata) {
155                 struct ucimap_section_data *sd = list_entry(ptr, struct ucimap_section_data, list);
156                 ucimap_free_section(map, sd);
157         }
158 }
159
160 static void
161 ucimap_add_fixup(struct uci_map *map, union ucimap_data *data, struct uci_optmap *om, const char *str)
162 {
163         struct uci_fixup *f;
164
165         f = malloc(sizeof(struct uci_fixup));
166         if (!f)
167                 return;
168
169         INIT_LIST_HEAD(&f->list);
170         f->sm = om->data.sm;
171         f->name = str;
172         f->type = om->type;
173         f->data = data;
174         list_add(&f->list, &map->fixup);
175 }
176
177 static void
178 ucimap_add_value(union ucimap_data *data, struct uci_optmap *om, struct ucimap_section_data *sd, const char *str)
179 {
180         union ucimap_data tdata = *data;
181         char *eptr = NULL;
182         char *s;
183         int val;
184
185         if (ucimap_is_list(om->type) && !ucimap_is_fixup(om->type))
186                 data = &data->list->item[data->list->n_items++];
187
188         switch(om->type & UCIMAP_SUBTYPE) {
189         case UCIMAP_STRING:
190                 if ((om->data.s.maxlen > 0) &&
191                         (strlen(str) > om->data.s.maxlen))
192                         return;
193
194                 s = strdup(str);
195                 tdata.s = s;
196                 ucimap_add_alloc(sd, s);
197                 break;
198         case UCIMAP_BOOL:
199                 val = -1;
200                 if (strcmp(str, "on"))
201                         val = true;
202                 else if (strcmp(str, "1"))
203                         val = true;
204                 else if (strcmp(str, "enabled"))
205                         val = true;
206                 else if (strcmp(str, "off"))
207                         val = false;
208                 else if (strcmp(str, "0"))
209                         val = false;
210                 else if (strcmp(str, "disabled"))
211                         val = false;
212                 if (val == -1)
213                         return;
214
215                 tdata.b = val;
216                 break;
217         case UCIMAP_INT:
218                 val = strtol(str, &eptr, om->data.i.base);
219                 if (!eptr || *eptr == '\0')
220                         tdata.i = val;
221                 else
222                         return;
223                 break;
224         case UCIMAP_SECTION:
225                 ucimap_add_fixup(sd->map, data, om, str);
226                 return;
227         case UCIMAP_CUSTOM:
228                 tdata.s = (char *) data;
229                 break;
230         }
231         if (om->parse) {
232                 if (om->parse(ucimap_section_ptr(sd), om, &tdata, str) < 0)
233                         return;
234         }
235         if (ucimap_is_custom(om->type))
236                 return;
237         memcpy(data, &tdata, sizeof(union ucimap_data));
238 }
239
240
241 static int
242 ucimap_parse_options(struct uci_map *map, struct uci_sectionmap *sm, struct ucimap_section_data *sd, struct uci_section *s)
243 {
244         struct uci_element *e, *l;
245         struct uci_option *o;
246         union ucimap_data *data;
247
248         uci_foreach_element(&s->options, e) {
249                 struct uci_optmap *om = NULL, *tmp;
250
251                 ucimap_foreach_option(sm, tmp) {
252                         if (strcmp(e->name, tmp->name) == 0) {
253                                 om = tmp;
254                                 break;
255                         }
256                 }
257                 if (!om)
258                         continue;
259
260                 data = ucimap_get_data(sd, om);
261                 o = uci_to_option(e);
262                 if ((o->type == UCI_TYPE_STRING) && ucimap_is_simple(om->type)) {
263                         ucimap_add_value(data, om, sd, o->v.string);
264                 } else if ((o->type == UCI_TYPE_LIST) && ucimap_is_list(om->type)) {
265                         uci_foreach_element(&o->v.list, l) {
266                                 ucimap_add_value(data, om, sd, l->name);
267                         }
268                 }
269         }
270
271         return 0;
272 }
273
274
275 static int
276 ucimap_parse_section(struct uci_map *map, struct uci_sectionmap *sm, struct uci_section *s)
277 {
278         struct ucimap_section_data *sd = NULL;
279         struct uci_optmap *om;
280         char *section_name;
281         void *section;
282         int n_alloc = 2;
283         int err;
284
285         if (sm->alloc) {
286                 sd = sm->alloc(map, sm, s);
287                 memset(sd, 0, sizeof(struct ucimap_section_data));
288         } else {
289                 sd = malloc(sm->alloc_len);
290                 memset(sd, 0, sm->alloc_len);
291         }
292
293         if (!sd)
294                 return UCI_ERR_MEM;
295
296         INIT_LIST_HEAD(&sd->list);
297         sd->map = map;
298         sd->sm = sm;
299
300         ucimap_foreach_option(sm, om) {
301                 if (ucimap_is_list(om->type)) {
302                         union ucimap_data *data;
303                         struct uci_element *e;
304                         int n_elements = 0;
305                         int size;
306
307                         data = ucimap_get_data(sd, om);
308                         uci_foreach_element(&s->options, e) {
309                                 struct uci_option *o = uci_to_option(e);
310                                 struct uci_element *tmp;
311
312                                 if (strcmp(e->name, om->name) != 0)
313                                         continue;
314
315                                 uci_foreach_element(&o->v.list, tmp) {
316                                         n_elements++;
317                                 }
318                                 break;
319                         }
320                         n_alloc += n_elements + 1;
321                         size = sizeof(struct ucimap_list) +
322                                 n_elements * sizeof(union ucimap_data);
323                         data->list = malloc(size);
324                         memset(data->list, 0, size);
325                 } else if (ucimap_is_alloc(om->type)) {
326                         n_alloc++;
327                 }
328         }
329
330         sd->allocmap = malloc(n_alloc * sizeof(struct uci_alloc));
331         if (!sd->allocmap)
332                 goto error_mem;
333
334         section_name = strdup(s->e.name);
335         if (!section_name)
336                 goto error_mem;
337
338         sd->section_name = section_name;
339
340         sd->cmap = malloc(BITFIELD_SIZE(sm->n_options));
341         if (!sd->cmap)
342                 goto error_mem;
343
344         memset(sd->cmap, 0, BITFIELD_SIZE(sm->n_options));
345         ucimap_add_alloc(sd, (void *)section_name);
346         ucimap_add_alloc(sd, (void *)sd->cmap);
347         ucimap_foreach_option(sm, om) {
348                 if (!ucimap_is_list(om->type))
349                         continue;
350
351                 ucimap_add_alloc(sd, ucimap_get_data(sd, om)->list);
352         }
353
354         section = ucimap_section_ptr(sd);
355         err = sm->init(map, section, s);
356         if (err)
357                 goto error;
358
359         list_add(&sd->list, &map->sdata);
360         err = ucimap_parse_options(map, sm, sd, s);
361         if (err)
362                 goto error;
363
364         return 0;
365
366 error_mem:
367         if (sd->allocmap)
368                 free(sd->allocmap);
369         free(sd);
370         return UCI_ERR_MEM;
371
372 error:
373         ucimap_free_section(map, sd);
374         return err;
375 }
376
377 static int
378 ucimap_fill_ptr(struct uci_ptr *ptr, struct uci_section *s, const char *option)
379 {
380         struct uci_package *p = s->package;
381
382         memset(ptr, 0, sizeof(struct uci_ptr));
383
384         ptr->package = p->e.name;
385         ptr->p = p;
386
387         ptr->section = s->e.name;
388         ptr->s = s;
389
390         ptr->option = option;
391         return uci_lookup_ptr(p->ctx, ptr, NULL, false);
392 }
393
394 void
395 ucimap_set_changed(struct ucimap_section_data *sd, void *field)
396 {
397         void *section = ucimap_section_ptr(sd);
398         struct uci_sectionmap *sm = sd->sm;
399         struct uci_optmap *om;
400         int ofs = (char *)field - (char *)section;
401         int i = 0;
402
403         ucimap_foreach_option(sm, om) {
404                 if (om->offset == ofs) {
405                         SET_BIT(sd->cmap, i);
406                         break;
407                 }
408                 i++;
409         }
410 }
411
412 int
413 ucimap_store_section(struct uci_map *map, struct uci_package *p, struct ucimap_section_data *sd)
414 {
415         struct uci_sectionmap *sm = sd->sm;
416         struct uci_section *s = NULL;
417         struct uci_optmap *om;
418         struct uci_element *e;
419         struct uci_ptr ptr;
420         int i = 0;
421         int ret;
422
423         uci_foreach_element(&p->sections, e) {
424                 if (!strcmp(e->name, sd->section_name)) {
425                         s = uci_to_section(e);
426                         break;
427                 }
428         }
429         if (!s)
430                 return UCI_ERR_NOTFOUND;
431
432         ucimap_foreach_option(sm, om) {
433                 union ucimap_data *data;
434                 static char buf[32];
435                 char *str = NULL;
436
437                 i++;
438                 if (ucimap_is_list(om->type))
439                         continue;
440
441                 data = ucimap_get_data(sd, om);
442                 if (!TEST_BIT(sd->cmap, i - 1))
443                         continue;
444
445                 ucimap_fill_ptr(&ptr, s, om->name);
446                 switch(om->type & UCIMAP_SUBTYPE) {
447                 case UCIMAP_STRING:
448                         str = data->s;
449                         break;
450                 case UCIMAP_INT:
451                         sprintf(buf, "%d", data->i);
452                         str = buf;
453                         break;
454                 case UCIMAP_BOOL:
455                         sprintf(buf, "%d", !!data->b);
456                         str = buf;
457                         break;
458                 case UCIMAP_CUSTOM:
459                         break;
460                 default:
461                         continue;
462                 }
463                 if (om->format) {
464                         union ucimap_data tdata, *data;
465
466                         data = ucimap_get_data(sd, om);
467                         if (ucimap_is_custom(om->type)) {
468                                 tdata.s = (char *)data;
469                                 data = &tdata;
470                         }
471
472                         if (om->format(ucimap_section_ptr(sd), om, data, &str) < 0)
473                                 continue;
474                 }
475                 if (!str)
476                         continue;
477                 ptr.value = str;
478
479                 ret = uci_set(s->package->ctx, &ptr);
480                 if (ret)
481                         return ret;
482
483                 CLR_BIT(sd->cmap, i - 1);
484         }
485
486         return 0;
487 }
488
489 void *
490 ucimap_find_section(struct uci_map *map, struct uci_fixup *f)
491 {
492         struct ucimap_section_data *sd;
493         struct list_head *p;
494
495         list_for_each(p, &map->sdata) {
496                 sd = list_entry(p, struct ucimap_section_data, list);
497                 if (sd->sm != f->sm)
498                         continue;
499                 if (strcmp(f->name, sd->section_name) != 0)
500                         continue;
501                 return ucimap_section_ptr(sd);
502         }
503         return NULL;
504 }
505
506 void
507 ucimap_parse(struct uci_map *map, struct uci_package *pkg)
508 {
509         struct uci_element *e;
510         struct list_head *p, *tmp;
511         int i;
512
513         INIT_LIST_HEAD(&map->fixup);
514         uci_foreach_element(&pkg->sections, e) {
515                 struct uci_section *s = uci_to_section(e);
516
517                 for (i = 0; i < map->n_sections; i++) {
518                         if (strcmp(s->type, map->sections[i]->type) != 0)
519                                 continue;
520                         ucimap_parse_section(map, map->sections[i], s);
521                 }
522         }
523         list_for_each_safe(p, tmp, &map->fixup) {
524                 struct uci_fixup *f = list_entry(p, struct uci_fixup, list);
525                 void *ptr = ucimap_find_section(map, f);
526                 struct ucimap_list *list;
527
528                 if (!ptr)
529                         continue;
530
531                 switch(f->type & UCIMAP_TYPE) {
532                 case UCIMAP_SIMPLE:
533                         f->data->section = ptr;
534                         break;
535                 case UCIMAP_LIST:
536                         list = f->data->list;
537                         list->item[list->n_items++].section = ptr;
538                         break;
539                 }
540                 free(f);
541         }
542         list_for_each_safe(p, tmp, &map->sdata) {
543                 struct ucimap_section_data *sd = list_entry(p, struct ucimap_section_data, list);
544                 void *section;
545
546                 if (sd->done)
547                         continue;
548
549                 section = ucimap_section_ptr(sd);
550                 if (sd->sm->add(map, section) != 0)
551                         ucimap_free_section(map, sd);
552         }
553 }