file: preserve original file mode after commit
[oweals/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         volatile 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         if (str && *str) {
167                 goto error;
168         }
169         for (c = 0; c <= __UCI_CMD_LAST; c++) {
170                 if (uci_command_char[c] == *arg)
171                         break;
172         }
173         if (c > __UCI_CMD_LAST)
174                 c = UCI_CMD_CHANGE;
175
176         if (c != UCI_CMD_CHANGE)
177                 arg += 1;
178
179         UCI_INTERNAL(uci_parse_ptr, ctx, ptr, arg);
180
181         if (!ptr->section)
182                 goto error;
183         if (ptr->flags & UCI_LOOKUP_EXTENDED)
184                 goto error;
185         if (c != UCI_CMD_REMOVE && !ptr->value) {
186                 goto error;
187         }
188
189         switch(c) {
190         case UCI_CMD_REORDER:
191                 if (!ptr->value || ptr->option)
192                         goto error;
193                 break;
194         case UCI_CMD_RENAME:
195                 if (!ptr->value || !uci_validate_name(ptr->value))
196                         goto error;
197                 break;
198         case UCI_CMD_LIST_ADD:
199                 if (!ptr->option)
200                         goto error;
201                 /* fall through */
202         case UCI_CMD_LIST_DEL:
203                 if (!ptr->option)
204                         goto error;
205         }
206
207         return c;
208
209 error:
210         UCI_THROW(ctx, UCI_ERR_INVAL);
211         return 0;
212 }
213
214 static void uci_parse_delta_line(struct uci_context *ctx, struct uci_package *p)
215 {
216         struct uci_element *e = NULL;
217         struct uci_ptr ptr;
218         int cmd;
219
220         cmd = uci_parse_delta_tuple(ctx, &ptr);
221         if (strcmp(ptr.package, p->e.name) != 0)
222                 goto error;
223
224         if (ctx->flags & UCI_FLAG_SAVED_DELTA)
225                 uci_add_delta(ctx, &p->saved_delta, cmd, ptr.section, ptr.option, ptr.value);
226
227         switch(cmd) {
228         case UCI_CMD_REORDER:
229                 uci_expand_ptr(ctx, &ptr, true);
230                 if (!ptr.s)
231                         UCI_THROW(ctx, UCI_ERR_NOTFOUND);
232                 UCI_INTERNAL(uci_reorder_section, ctx, ptr.s, strtoul(ptr.value, NULL, 10));
233                 break;
234         case UCI_CMD_RENAME:
235                 UCI_INTERNAL(uci_rename, ctx, &ptr);
236                 break;
237         case UCI_CMD_REMOVE:
238                 UCI_INTERNAL(uci_delete, ctx, &ptr);
239                 break;
240         case UCI_CMD_LIST_ADD:
241                 UCI_INTERNAL(uci_add_list, ctx, &ptr);
242                 break;
243         case UCI_CMD_LIST_DEL:
244                 UCI_INTERNAL(uci_del_list, ctx, &ptr);
245                 break;
246         case UCI_CMD_ADD:
247         case UCI_CMD_CHANGE:
248                 UCI_INTERNAL(uci_set, ctx, &ptr);
249                 e = ptr.last;
250                 if (!ptr.option && e && (cmd == UCI_CMD_ADD))
251                         uci_to_section(e)->anonymous = true;
252                 break;
253         }
254         return;
255 error:
256         UCI_THROW(ctx, UCI_ERR_PARSE);
257 }
258
259 /* returns the number of changes that were successfully parsed */
260 static int uci_parse_delta(struct uci_context *ctx, FILE *stream, struct uci_package *p)
261 {
262         struct uci_parse_context *pctx;
263         volatile int changes = 0;
264
265         /* make sure no memory from previous parse attempts is leaked */
266         uci_cleanup(ctx);
267
268         pctx = (struct uci_parse_context *) uci_malloc(ctx, sizeof(struct uci_parse_context));
269         ctx->pctx = pctx;
270         pctx->file = stream;
271
272         while (!feof(pctx->file)) {
273                 pctx->pos = 0;
274                 uci_getln(ctx, 0);
275                 if (!pctx->buf[0])
276                         continue;
277
278                 /*
279                  * ignore parse errors in single lines, we want to preserve as much
280                  * delta as possible
281                  */
282                 UCI_TRAP_SAVE(ctx, error);
283                 uci_parse_delta_line(ctx, p);
284                 UCI_TRAP_RESTORE(ctx);
285                 changes++;
286 error:
287                 continue;
288         }
289
290         /* no error happened, we can get rid of the parser context now */
291         uci_cleanup(ctx);
292         return changes;
293 }
294
295 /* returns the number of changes that were successfully parsed */
296 static int uci_load_delta_file(struct uci_context *ctx, struct uci_package *p, char *filename, FILE **f, bool flush)
297 {
298         FILE *volatile stream = NULL;
299         volatile int changes = 0;
300
301         UCI_TRAP_SAVE(ctx, done);
302         stream = uci_open_stream(ctx, filename, NULL, SEEK_SET, flush, false);
303         UCI_TRAP_RESTORE(ctx);
304
305         if (p)
306                 changes = uci_parse_delta(ctx, stream, p);
307
308 done:
309         if (f)
310                 *f = stream;
311         else
312                 uci_close_stream(stream);
313         return changes;
314 }
315
316 /* returns the number of changes that were successfully parsed */
317 __private int uci_load_delta(struct uci_context *ctx, struct uci_package *p, bool flush)
318 {
319         struct uci_element *e;
320         char *filename = NULL;
321         FILE *volatile f = NULL;
322         volatile int changes = 0;
323
324         if (!p->has_delta)
325                 return 0;
326
327         uci_foreach_element(&ctx->delta_path, e) {
328                 if ((asprintf(&filename, "%s/%s", e->name, p->e.name) < 0) || !filename)
329                         UCI_THROW(ctx, UCI_ERR_MEM);
330
331                 changes += uci_load_delta_file(ctx, p, filename, NULL, false);
332                 free(filename);
333         }
334
335         if ((asprintf(&filename, "%s/%s", ctx->savedir, p->e.name) < 0) || !filename)
336                 UCI_THROW(ctx, UCI_ERR_MEM);
337         UCI_TRAP_SAVE(ctx, done);
338         f = uci_open_stream(ctx, filename, NULL, SEEK_SET, flush, false);
339         UCI_TRAP_RESTORE(ctx);
340
341         if (flush && f && (changes > 0)) {
342                 if (ftruncate(fileno(f), 0) < 0) {
343                         free(filename);
344                         uci_close_stream(f);
345                         UCI_THROW(ctx, UCI_ERR_IO);
346                 }
347         }
348
349 done:
350         free(filename);
351         uci_close_stream(f);
352         ctx->err = 0;
353         return changes;
354 }
355
356 static void uci_filter_delta(struct uci_context *ctx, const char *name, const char *section, const char *option)
357 {
358         struct uci_parse_context *pctx;
359         struct uci_element *e, *tmp;
360         struct uci_list list;
361         char *filename = NULL;
362         struct uci_ptr ptr;
363         FILE *f = NULL;
364
365         uci_list_init(&list);
366         uci_alloc_parse_context(ctx);
367         pctx = ctx->pctx;
368
369         if ((asprintf(&filename, "%s/%s", ctx->savedir, name) < 0) || !filename)
370                 UCI_THROW(ctx, UCI_ERR_MEM);
371
372         UCI_TRAP_SAVE(ctx, done);
373         f = uci_open_stream(ctx, filename, NULL, SEEK_SET, true, false);
374         pctx->file = f;
375         while (!feof(f)) {
376                 enum uci_command c;
377                 bool match;
378
379                 pctx->pos = 0;
380                 uci_getln(ctx, 0);
381                 if (!pctx->buf[0])
382                         continue;
383
384                 c = uci_parse_delta_tuple(ctx, &ptr);
385                 match = true;
386                 if (section) {
387                         if (!ptr.section || (strcmp(section, ptr.section) != 0))
388                                 match = false;
389                 }
390                 if (match && option) {
391                         if (!ptr.option || (strcmp(option, ptr.option) != 0))
392                                 match = false;
393                 }
394
395                 if (!match && ptr.section) {
396                         uci_add_delta(ctx, &list, c,
397                                 ptr.section, ptr.option, ptr.value);
398                 }
399         }
400
401         /* rebuild the delta file */
402         rewind(f);
403         if (ftruncate(fileno(f), 0) < 0)
404                 UCI_THROW(ctx, UCI_ERR_IO);
405         uci_foreach_element_safe(&list, tmp, e) {
406                 struct uci_delta *h = uci_to_delta(e);
407                 uci_delta_save(ctx, f, name, h);
408                 uci_free_delta(h);
409         }
410         UCI_TRAP_RESTORE(ctx);
411
412 done:
413         free(filename);
414         uci_close_stream(pctx->file);
415         uci_foreach_element_safe(&list, tmp, e) {
416                 uci_free_delta(uci_to_delta(e));
417         }
418         uci_cleanup(ctx);
419 }
420
421 int uci_revert(struct uci_context *ctx, struct uci_ptr *ptr)
422 {
423         char *volatile package = NULL;
424         char *volatile section = NULL;
425         char *volatile option = NULL;
426
427         UCI_HANDLE_ERR(ctx);
428         uci_expand_ptr(ctx, ptr, false);
429         UCI_ASSERT(ctx, ptr->p->has_delta);
430
431         /*
432          * - flush unwritten changes
433          * - save the package name
434          * - unload the package
435          * - filter the delta
436          * - reload the package
437          */
438         UCI_TRAP_SAVE(ctx, error);
439         UCI_INTERNAL(uci_save, ctx, ptr->p);
440
441         /* NB: need to clone package, section and option names,
442          * as they may get freed on uci_free_package() */
443         package = uci_strdup(ctx, ptr->p->e.name);
444         if (ptr->section)
445                 section = uci_strdup(ctx, ptr->section);
446         if (ptr->option)
447                 option = uci_strdup(ctx, ptr->option);
448
449         uci_free_package(&ptr->p);
450         uci_filter_delta(ctx, package, section, option);
451
452         UCI_INTERNAL(uci_load, ctx, package, &ptr->p);
453         UCI_TRAP_RESTORE(ctx);
454         ctx->err = 0;
455
456 error:
457         free(package);
458         free(section);
459         free(option);
460         if (ctx->err)
461                 UCI_THROW(ctx, ctx->err);
462         return 0;
463 }
464
465 int uci_save(struct uci_context *ctx, struct uci_package *p)
466 {
467         FILE *volatile f = NULL;
468         char *filename = NULL;
469         struct uci_element *e, *tmp;
470         struct stat statbuf;
471
472         UCI_HANDLE_ERR(ctx);
473         UCI_ASSERT(ctx, p != NULL);
474
475         /*
476          * if the config file was outside of the /etc/config path,
477          * don't save the delta to a file, update the real file
478          * directly.
479          * does not modify the uci_package pointer
480          */
481         if (!p->has_delta)
482                 return uci_commit(ctx, &p, false);
483
484         if (uci_list_empty(&p->delta))
485                 return 0;
486
487         if (stat(ctx->savedir, &statbuf) < 0) {
488                 if (stat(ctx->confdir, &statbuf) == 0) {
489                         mkdir(ctx->savedir, statbuf.st_mode);
490                 } else {
491                         mkdir(ctx->savedir, UCI_DIRMODE);
492                 }
493         } else if ((statbuf.st_mode & S_IFMT) != S_IFDIR) {
494                 UCI_THROW(ctx, UCI_ERR_IO);
495         }
496
497         if ((asprintf(&filename, "%s/%s", ctx->savedir, p->e.name) < 0) || !filename)
498                 UCI_THROW(ctx, UCI_ERR_MEM);
499
500         ctx->err = 0;
501         UCI_TRAP_SAVE(ctx, done);
502         f = uci_open_stream(ctx, filename, NULL, SEEK_END, true, true);
503         UCI_TRAP_RESTORE(ctx);
504
505         uci_foreach_element_safe(&p->delta, tmp, e) {
506                 struct uci_delta *h = uci_to_delta(e);
507                 uci_delta_save(ctx, f, p->e.name, h);
508                 uci_free_delta(h);
509         }
510
511 done:
512         uci_close_stream(f);
513         free(filename);
514         if (ctx->err)
515                 UCI_THROW(ctx, ctx->err);
516
517         return 0;
518 }
519
520