file: remove unnecessary uci_fixup_section calls
[project/uci.git] / delta.c
1 /*
2  * libuci - Library for the Unified Configuration Interface
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 Lesser General Public License version 2.1
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 Lesser General Public License for more details.
13  */
14
15 /*
16  * This file contains the code for handling uci config delta files
17  */
18
19 #define _GNU_SOURCE
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include <sys/file.h>
23 #include <stdbool.h>
24 #include <unistd.h>
25 #include <fcntl.h>
26 #include <stdio.h>
27 #include <ctype.h>
28 #include <string.h>
29 #include <stdlib.h>
30
31 #include "uci.h"
32 #include "uci_internal.h"
33
34 /* record a change that was done to a package */
35 void
36 uci_add_delta(struct uci_context *ctx, struct uci_list *list, int cmd, const char *section, const char *option, const char *value)
37 {
38         struct uci_delta *h;
39         int size = strlen(section) + 1;
40         char *ptr;
41
42         if (value)
43                 size += strlen(value) + 1;
44
45         h = uci_alloc_element(ctx, delta, option, size);
46         ptr = uci_dataptr(h);
47         h->cmd = cmd;
48         h->section = strcpy(ptr, section);
49         if (value) {
50                 ptr += strlen(ptr) + 1;
51                 h->value = strcpy(ptr, value);
52         }
53         uci_list_add(list, &h->e.list);
54 }
55
56 void
57 uci_free_delta(struct uci_delta *h)
58 {
59         if (!h)
60                 return;
61         if ((h->section != NULL) &&
62                 (h->section != uci_dataptr(h))) {
63                 free(h->section);
64                 free(h->value);
65         }
66         uci_free_element(&h->e);
67 }
68
69 static void uci_delta_save(struct uci_context *ctx, FILE *f,
70                         const char *name, const struct uci_delta *h)
71 {
72         const struct uci_element *e = &h->e;
73         char prefix[2] = {0, 0};
74
75         if (h->cmd <= __UCI_CMD_LAST)
76                 prefix[0] = uci_command_char[h->cmd];
77
78         fprintf(f, "%s%s.%s", prefix, name, h->section);
79         if (e->name)
80                 fprintf(f, ".%s", e->name);
81
82         if (h->cmd == UCI_CMD_REMOVE && !h->value)
83                 fprintf(f, "\n");
84         else {
85                 int i;
86
87                 fprintf(f, "='");
88                 for (i = 0; h->value[i]; i++) {
89                         unsigned char c = h->value[i];
90                         if (c != '\'')
91                                 fputc(c, f);
92                         else
93                                 fprintf(f, "'\\''");
94                 }
95                 fprintf(f, "'\n");
96         }
97 }
98
99 int uci_set_savedir(struct uci_context *ctx, const char *dir)
100 {
101         char *sdir;
102         struct uci_element *e, *tmp;
103         bool exists = false;
104
105         UCI_HANDLE_ERR(ctx);
106         UCI_ASSERT(ctx, dir != NULL);
107
108         /* Move dir to the end of ctx->delta_path */
109         uci_foreach_element_safe(&ctx->delta_path, tmp, e) {
110                 if (!strcmp(e->name, dir)) {
111                         exists = true;
112                         uci_list_del(&e->list);
113                         break;
114                 }
115         }
116         if (!exists)
117                 e = uci_alloc_generic(ctx, UCI_TYPE_PATH, dir, sizeof(struct uci_element));
118         uci_list_add(&ctx->delta_path, &e->list);
119
120         sdir = uci_strdup(ctx, dir);
121         if (ctx->savedir != uci_savedir)
122                 free(ctx->savedir);
123         ctx->savedir = sdir;
124         return 0;
125 }
126
127 int uci_add_delta_path(struct uci_context *ctx, const char *dir)
128 {
129         struct uci_element *e;
130         struct uci_list *savedir;
131
132         UCI_HANDLE_ERR(ctx);
133         UCI_ASSERT(ctx, dir != NULL);
134
135         /* Duplicate delta path is not allowed */
136         uci_foreach_element(&ctx->delta_path, e) {
137                 if (!strcmp(e->name, dir))
138                         UCI_THROW(ctx, UCI_ERR_DUPLICATE);
139         }
140
141         e = uci_alloc_generic(ctx, UCI_TYPE_PATH, dir, sizeof(struct uci_element));
142         /* Keep savedir at the end of ctx->delta_path list */
143         savedir = ctx->delta_path.prev;
144         uci_list_insert(savedir->prev, &e->list);
145
146         return 0;
147 }
148
149 char const uci_command_char[] = {
150         [UCI_CMD_ADD] = '+',
151         [UCI_CMD_REMOVE] = '-',
152         [UCI_CMD_CHANGE] = 0,
153         [UCI_CMD_RENAME] = '@',
154         [UCI_CMD_REORDER] = '^',
155         [UCI_CMD_LIST_ADD] = '|',
156         [UCI_CMD_LIST_DEL] = '~'
157 };
158
159 static inline int uci_parse_delta_tuple(struct uci_context *ctx, struct uci_ptr *ptr)
160 {
161         struct uci_parse_context *pctx = ctx->pctx;
162         char *str = pctx_cur_str(pctx), *arg;
163         int c;
164
165         UCI_INTERNAL(uci_parse_argument, ctx, ctx->pctx->file, &str, &arg);
166         for (c = 0; c <= __UCI_CMD_LAST; c++) {
167                 if (uci_command_char[c] == *arg)
168                         break;
169         }
170         if (c > __UCI_CMD_LAST)
171                 c = UCI_CMD_CHANGE;
172
173         if (c != UCI_CMD_CHANGE)
174                 arg += 1;
175
176         UCI_INTERNAL(uci_parse_ptr, ctx, ptr, arg);
177
178         if (!ptr->section)
179                 goto error;
180         if (ptr->flags & UCI_LOOKUP_EXTENDED)
181                 goto error;
182
183         switch(c) {
184         case UCI_CMD_REORDER:
185                 if (!ptr->value || ptr->option)
186                         goto error;
187                 break;
188         case UCI_CMD_RENAME:
189                 if (!ptr->value || !uci_validate_name(ptr->value))
190                         goto error;
191                 break;
192         case UCI_CMD_LIST_ADD:
193                 if (!ptr->option)
194                         goto error;
195         case UCI_CMD_LIST_DEL:
196                 if (!ptr->option)
197                         goto error;
198         }
199
200         return c;
201
202 error:
203         UCI_THROW(ctx, UCI_ERR_INVAL);
204         return 0;
205 }
206
207 static void uci_parse_delta_line(struct uci_context *ctx, struct uci_package *p)
208 {
209         struct uci_element *e = NULL;
210         struct uci_ptr ptr;
211         int cmd;
212
213         cmd = uci_parse_delta_tuple(ctx, &ptr);
214         if (strcmp(ptr.package, p->e.name) != 0)
215                 goto error;
216
217         if (ctx->flags & UCI_FLAG_SAVED_DELTA)
218                 uci_add_delta(ctx, &p->saved_delta, cmd, ptr.section, ptr.option, ptr.value);
219
220         switch(cmd) {
221         case UCI_CMD_REORDER:
222                 uci_expand_ptr(ctx, &ptr, true);
223                 if (!ptr.s)
224                         UCI_THROW(ctx, UCI_ERR_NOTFOUND);
225                 UCI_INTERNAL(uci_reorder_section, ctx, ptr.s, strtoul(ptr.value, NULL, 10));
226                 break;
227         case UCI_CMD_RENAME:
228                 UCI_INTERNAL(uci_rename, ctx, &ptr);
229                 break;
230         case UCI_CMD_REMOVE:
231                 UCI_INTERNAL(uci_delete, ctx, &ptr);
232                 break;
233         case UCI_CMD_LIST_ADD:
234                 UCI_INTERNAL(uci_add_list, ctx, &ptr);
235                 break;
236         case UCI_CMD_LIST_DEL:
237                 UCI_INTERNAL(uci_del_list, ctx, &ptr);
238                 break;
239         case UCI_CMD_ADD:
240         case UCI_CMD_CHANGE:
241                 UCI_INTERNAL(uci_set, ctx, &ptr);
242                 e = ptr.last;
243                 if (!ptr.option && e && (cmd == UCI_CMD_ADD))
244                         uci_to_section(e)->anonymous = true;
245                 break;
246         }
247         return;
248 error:
249         UCI_THROW(ctx, UCI_ERR_PARSE);
250 }
251
252 /* returns the number of changes that were successfully parsed */
253 static int uci_parse_delta(struct uci_context *ctx, FILE *stream, struct uci_package *p)
254 {
255         struct uci_parse_context *pctx;
256         int changes = 0;
257
258         /* make sure no memory from previous parse attempts is leaked */
259         uci_cleanup(ctx);
260
261         pctx = (struct uci_parse_context *) uci_malloc(ctx, sizeof(struct uci_parse_context));
262         ctx->pctx = pctx;
263         pctx->file = stream;
264
265         while (!feof(pctx->file)) {
266                 pctx->pos = 0;
267                 uci_getln(ctx, 0);
268                 if (!pctx->buf[0])
269                         continue;
270
271                 /*
272                  * ignore parse errors in single lines, we want to preserve as much
273                  * delta as possible
274                  */
275                 UCI_TRAP_SAVE(ctx, error);
276                 uci_parse_delta_line(ctx, p);
277                 UCI_TRAP_RESTORE(ctx);
278                 changes++;
279 error:
280                 continue;
281         }
282
283         /* no error happened, we can get rid of the parser context now */
284         uci_cleanup(ctx);
285         return changes;
286 }
287
288 /* returns the number of changes that were successfully parsed */
289 static int uci_load_delta_file(struct uci_context *ctx, struct uci_package *p, char *filename, FILE **f, bool flush)
290 {
291         FILE *stream = NULL;
292         int changes = 0;
293
294         UCI_TRAP_SAVE(ctx, done);
295         stream = uci_open_stream(ctx, filename, NULL, SEEK_SET, flush, false);
296         UCI_TRAP_RESTORE(ctx);
297
298         if (p)
299                 changes = uci_parse_delta(ctx, stream, p);
300
301 done:
302         if (f)
303                 *f = stream;
304         else
305                 uci_close_stream(stream);
306         return changes;
307 }
308
309 /* returns the number of changes that were successfully parsed */
310 __private int uci_load_delta(struct uci_context *ctx, struct uci_package *p, bool flush)
311 {
312         struct uci_element *e;
313         char *filename = NULL;
314         FILE *f = NULL;
315         int changes = 0;
316
317         if (!p->has_delta)
318                 return 0;
319
320         uci_foreach_element(&ctx->delta_path, e) {
321                 if ((asprintf(&filename, "%s/%s", e->name, p->e.name) < 0) || !filename)
322                         UCI_THROW(ctx, UCI_ERR_MEM);
323
324                 changes += uci_load_delta_file(ctx, p, filename, NULL, false);
325                 free(filename);
326         }
327
328         if ((asprintf(&filename, "%s/%s", ctx->savedir, p->e.name) < 0) || !filename)
329                 UCI_THROW(ctx, UCI_ERR_MEM);
330         UCI_TRAP_SAVE(ctx, done);
331         f = uci_open_stream(ctx, filename, NULL, SEEK_SET, flush, false);
332         UCI_TRAP_RESTORE(ctx);
333
334         if (flush && f && (changes > 0)) {
335                 if (ftruncate(fileno(f), 0) < 0) {
336                         free(filename);
337                         uci_close_stream(f);
338                         UCI_THROW(ctx, UCI_ERR_IO);
339                 }
340         }
341
342 done:
343         free(filename);
344         uci_close_stream(f);
345         ctx->err = 0;
346         return changes;
347 }
348
349 static void uci_filter_delta(struct uci_context *ctx, const char *name, const char *section, const char *option)
350 {
351         struct uci_parse_context *pctx;
352         struct uci_element *e, *tmp;
353         struct uci_list list;
354         char *filename = NULL;
355         struct uci_ptr ptr;
356         FILE *f = NULL;
357
358         uci_list_init(&list);
359         uci_alloc_parse_context(ctx);
360         pctx = ctx->pctx;
361
362         if ((asprintf(&filename, "%s/%s", ctx->savedir, name) < 0) || !filename)
363                 UCI_THROW(ctx, UCI_ERR_MEM);
364
365         UCI_TRAP_SAVE(ctx, done);
366         f = uci_open_stream(ctx, filename, NULL, SEEK_SET, true, false);
367         pctx->file = f;
368         while (!feof(f)) {
369                 enum uci_command c;
370                 bool match;
371
372                 pctx->pos = 0;
373                 uci_getln(ctx, 0);
374                 if (!pctx->buf[0])
375                         continue;
376
377                 c = uci_parse_delta_tuple(ctx, &ptr);
378                 match = true;
379                 if (section) {
380                         if (!ptr.section || (strcmp(section, ptr.section) != 0))
381                                 match = false;
382                 }
383                 if (match && option) {
384                         if (!ptr.option || (strcmp(option, ptr.option) != 0))
385                                 match = false;
386                 }
387
388                 if (!match) {
389                         uci_add_delta(ctx, &list, c,
390                                 ptr.section, ptr.option, ptr.value);
391                 }
392         }
393
394         /* rebuild the delta file */
395         rewind(f);
396         if (ftruncate(fileno(f), 0) < 0)
397                 UCI_THROW(ctx, UCI_ERR_IO);
398         uci_foreach_element_safe(&list, tmp, e) {
399                 struct uci_delta *h = uci_to_delta(e);
400                 uci_delta_save(ctx, f, name, h);
401                 uci_free_delta(h);
402         }
403         UCI_TRAP_RESTORE(ctx);
404
405 done:
406         free(filename);
407         uci_close_stream(pctx->file);
408         uci_foreach_element_safe(&list, tmp, e) {
409                 uci_free_delta(uci_to_delta(e));
410         }
411         uci_cleanup(ctx);
412 }
413
414 int uci_revert(struct uci_context *ctx, struct uci_ptr *ptr)
415 {
416         char *package = NULL;
417         char *section = NULL;
418         char *option = NULL;
419
420         UCI_HANDLE_ERR(ctx);
421         uci_expand_ptr(ctx, ptr, false);
422         UCI_ASSERT(ctx, ptr->p->has_delta);
423
424         /*
425          * - flush unwritten changes
426          * - save the package name
427          * - unload the package
428          * - filter the delta
429          * - reload the package
430          */
431         UCI_TRAP_SAVE(ctx, error);
432         UCI_INTERNAL(uci_save, ctx, ptr->p);
433
434         /* NB: need to clone package, section and option names,
435          * as they may get freed on uci_free_package() */
436         package = uci_strdup(ctx, ptr->p->e.name);
437         if (ptr->section)
438                 section = uci_strdup(ctx, ptr->section);
439         if (ptr->option)
440                 option = uci_strdup(ctx, ptr->option);
441
442         uci_free_package(&ptr->p);
443         uci_filter_delta(ctx, package, section, option);
444
445         UCI_INTERNAL(uci_load, ctx, package, &ptr->p);
446         UCI_TRAP_RESTORE(ctx);
447         ctx->err = 0;
448
449 error:
450         free(package);
451         free(section);
452         free(option);
453         if (ctx->err)
454                 UCI_THROW(ctx, ctx->err);
455         return 0;
456 }
457
458 int uci_save(struct uci_context *ctx, struct uci_package *p)
459 {
460         FILE *f = NULL;
461         char *filename = NULL;
462         struct uci_element *e, *tmp;
463         struct stat statbuf;
464
465         UCI_HANDLE_ERR(ctx);
466         UCI_ASSERT(ctx, p != NULL);
467
468         /*
469          * if the config file was outside of the /etc/config path,
470          * don't save the delta to a file, update the real file
471          * directly.
472          * does not modify the uci_package pointer
473          */
474         if (!p->has_delta)
475                 return uci_commit(ctx, &p, false);
476
477         if (uci_list_empty(&p->delta))
478                 return 0;
479
480         if (stat(ctx->savedir, &statbuf) < 0) {
481                 if (stat(ctx->confdir, &statbuf) == 0) {
482                         mkdir(ctx->savedir, statbuf.st_mode);
483                 } else {
484                         mkdir(ctx->savedir, UCI_DIRMODE);
485                 }
486         } else if ((statbuf.st_mode & S_IFMT) != S_IFDIR) {
487                 UCI_THROW(ctx, UCI_ERR_IO);
488         }
489
490         if ((asprintf(&filename, "%s/%s", ctx->savedir, p->e.name) < 0) || !filename)
491                 UCI_THROW(ctx, UCI_ERR_MEM);
492
493         ctx->err = 0;
494         UCI_TRAP_SAVE(ctx, done);
495         f = uci_open_stream(ctx, filename, NULL, SEEK_END, true, true);
496         UCI_TRAP_RESTORE(ctx);
497
498         uci_foreach_element_safe(&p->delta, tmp, e) {
499                 struct uci_delta *h = uci_to_delta(e);
500                 uci_delta_save(ctx, f, p->e.name, h);
501                 uci_free_delta(h);
502         }
503
504 done:
505         uci_close_stream(f);
506         free(filename);
507         if (ctx->err)
508                 UCI_THROW(ctx, ctx->err);
509
510         return 0;
511 }
512
513