• source navigation  • diff markup  • identifier search  • freetext search  • 

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

This page was automatically generated by LXR 0.3.1.  •  OpenWrt