Use offset into parser buffer to avoid potential heap overflow.
[project/uci.git] / delta.c
diff --git a/delta.c b/delta.c
index f697141..082633b 100644 (file)
--- a/delta.c
+++ b/delta.c
@@ -9,7 +9,7 @@
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
+ * GNU Lesser General Public License for more details.
  */
 
 /*
@@ -114,6 +114,9 @@ static inline int uci_parse_delta_tuple(struct uci_context *ctx, char **buf, str
        case '|':
                c = UCI_CMD_LIST_ADD;
                break;
+       case '~':
+               c = UCI_CMD_LIST_DEL;
+               break;
        }
 
        if (c != UCI_CMD_CHANGE)
@@ -138,6 +141,9 @@ static inline int uci_parse_delta_tuple(struct uci_context *ctx, char **buf, str
        case UCI_CMD_LIST_ADD:
                if (!ptr->option)
                        goto error;
+       case UCI_CMD_LIST_DEL:
+               if (!ptr->option)
+                       goto error;
        }
 
        return c;
@@ -176,6 +182,9 @@ static void uci_parse_delta_line(struct uci_context *ctx, struct uci_package *p,
        case UCI_CMD_LIST_ADD:
                UCI_INTERNAL(uci_add_list, ctx, &ptr);
                break;
+       case UCI_CMD_LIST_DEL:
+               UCI_INTERNAL(uci_del_list, ctx, &ptr);
+               break;
        case UCI_CMD_ADD:
        case UCI_CMD_CHANGE:
                UCI_INTERNAL(uci_set, ctx, &ptr);
@@ -231,7 +240,7 @@ static int uci_load_delta_file(struct uci_context *ctx, struct uci_package *p, c
        int changes = 0;
 
        UCI_TRAP_SAVE(ctx, done);
-       stream = uci_open_stream(ctx, filename, SEEK_SET, flush, false);
+       stream = uci_open_stream(ctx, filename, NULL, SEEK_SET, flush, false);
        if (p)
                changes = uci_parse_delta(ctx, stream, p);
        UCI_TRAP_RESTORE(ctx);
@@ -273,8 +282,7 @@ __private int uci_load_delta(struct uci_context *ctx, struct uci_package *p, boo
                        UCI_THROW(ctx, UCI_ERR_IO);
                }
        }
-       if (filename)
-               free(filename);
+       free(filename);
        uci_close_stream(f);
        ctx->err = 0;
        return changes;
@@ -297,7 +305,7 @@ static void uci_filter_delta(struct uci_context *ctx, const char *name, const ch
                UCI_THROW(ctx, UCI_ERR_MEM);
 
        UCI_TRAP_SAVE(ctx, done);
-       f = uci_open_stream(ctx, filename, SEEK_SET, true, false);
+       f = uci_open_stream(ctx, filename, NULL, SEEK_SET, true, false);
        pctx->file = f;
        while (!feof(f)) {
                struct uci_element *e;
@@ -308,8 +316,8 @@ static void uci_filter_delta(struct uci_context *ctx, const char *name, const ch
                if (!buf[0])
                        continue;
 
-               /* NB: need to allocate the element before the call to 
-                * uci_parse_delta_tuple, otherwise the original string 
+               /* NB: need to allocate the element before the call to
+                * uci_parse_delta_tuple, otherwise the original string
                 * gets modified before it is saved */
                e = uci_alloc_generic(ctx, UCI_TYPE_DELTA, pctx->buf, sizeof(struct uci_element));
                uci_list_add(&list, &e->list);
@@ -338,8 +346,7 @@ static void uci_filter_delta(struct uci_context *ctx, const char *name, const ch
        UCI_TRAP_RESTORE(ctx);
 
 done:
-       if (filename)
-               free(filename);
+       free(filename);
        uci_close_stream(pctx->file);
        uci_foreach_element_safe(&list, tmp, e) {
                uci_free_element(e);
@@ -357,7 +364,7 @@ int uci_revert(struct uci_context *ctx, struct uci_ptr *ptr)
        uci_expand_ptr(ctx, ptr, false);
        UCI_ASSERT(ctx, ptr->p->has_delta);
 
-       /* 
+       /*
         * - flush unwritten changes
         * - save the package name
         * - unload the package
@@ -367,7 +374,7 @@ int uci_revert(struct uci_context *ctx, struct uci_ptr *ptr)
        UCI_TRAP_SAVE(ctx, error);
        UCI_INTERNAL(uci_save, ctx, ptr->p);
 
-       /* NB: need to clone package, section and option names, 
+       /* NB: need to clone package, section and option names,
         * as they may get freed on uci_free_package() */
        package = uci_strdup(ctx, ptr->p->e.name);
        if (ptr->section)
@@ -383,12 +390,9 @@ int uci_revert(struct uci_context *ctx, struct uci_ptr *ptr)
        ctx->err = 0;
 
 error:
-       if (package)
-               free(package);
-       if (section)
-               free(section);
-       if (option)
-               free(option);
+       free(package);
+       free(section);
+       free(option);
        if (ctx->err)
                UCI_THROW(ctx, ctx->err);
        return 0;
@@ -404,7 +408,7 @@ int uci_save(struct uci_context *ctx, struct uci_package *p)
        UCI_HANDLE_ERR(ctx);
        UCI_ASSERT(ctx, p != NULL);
 
-       /* 
+       /*
         * if the config file was outside of the /etc/config path,
         * don't save the delta to a file, update the real file
         * directly.
@@ -416,28 +420,22 @@ int uci_save(struct uci_context *ctx, struct uci_package *p)
        if (uci_list_empty(&p->delta))
                return 0;
 
-       if (stat(ctx->savedir, &statbuf) < 0)
-               mkdir(ctx->savedir, UCI_DIRMODE);
-       else if ((statbuf.st_mode & S_IFMT) != S_IFDIR)
+       if (stat(ctx->savedir, &statbuf) < 0) {
+               if (stat(ctx->confdir, &statbuf) == 0) {
+                       mkdir(ctx->savedir, statbuf.st_mode);
+               } else {
+                       mkdir(ctx->savedir, UCI_DIRMODE);
+               }
+       } else if ((statbuf.st_mode & S_IFMT) != S_IFDIR) {
                UCI_THROW(ctx, UCI_ERR_IO);
+       }
 
        if ((asprintf(&filename, "%s/%s", ctx->savedir, p->e.name) < 0) || !filename)
                UCI_THROW(ctx, UCI_ERR_MEM);
 
-       uci_foreach_element(&ctx->hooks, tmp) {
-               struct uci_hook *hook = uci_to_hook(tmp);
-
-               if (!hook->ops->set)
-                       continue;
-
-               uci_foreach_element(&p->delta, e) {
-                       hook->ops->set(hook->ops, p, uci_to_delta(e));
-               }
-       }
-
        ctx->err = 0;
        UCI_TRAP_SAVE(ctx, done);
-       f = uci_open_stream(ctx, filename, SEEK_END, true, true);
+       f = uci_open_stream(ctx, filename, NULL, SEEK_END, true, true);
        UCI_TRAP_RESTORE(ctx);
 
        uci_foreach_element_safe(&p->delta, tmp, e) {
@@ -460,6 +458,9 @@ int uci_save(struct uci_context *ctx, struct uci_package *p)
                case UCI_CMD_LIST_ADD:
                        prefix = "|";
                        break;
+               case UCI_CMD_LIST_DEL:
+                       prefix = "~";
+                       break;
                default:
                        break;
                }
@@ -468,17 +469,27 @@ int uci_save(struct uci_context *ctx, struct uci_package *p)
                if (e->name)
                        fprintf(f, ".%s", e->name);
 
-               if (h->cmd == UCI_CMD_REMOVE)
+               if (h->cmd == UCI_CMD_REMOVE && !h->value)
                        fprintf(f, "\n");
-               else
-                       fprintf(f, "=%s\n", h->value);
+               else {
+                       int i;
+
+                       fprintf(f, "='");
+                       for (i = 0; h->value[i]; i++) {
+                               unsigned char c = h->value[i];
+                               if (c != '\'')
+                                       fputc(c, f);
+                               else
+                                       fprintf(f, "'\\''");
+                       }
+                       fprintf(f, "'\n");
+               }
                uci_free_delta(h);
        }
 
 done:
        uci_close_stream(f);
-       if (filename)
-               free(filename);
+       free(filename);
        if (ctx->err)
                UCI_THROW(ctx, ctx->err);