s4-registry: fixed byte order assumptions
[ira/wip.git] / source4 / lib / registry / patchfile_preg.c
index 90a4f2529a0c38c06f486d3b64f2aecaaba5308a..d7b4bc3730819c1c142dedc1ee086b01d5567e8b 100644 (file)
@@ -2,11 +2,11 @@
    Unix SMB/CIFS implementation.
    Reading Registry.pol PReg registry files
 
-   Copyright (C) Wilco Baan Hofman 2006
+   Copyright (C) Wilco Baan Hofman 2006-2008
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
 
    This program is distributed in the hope that it will be useful,
 
 #include "includes.h"
 #include "lib/registry/registry.h"
-#include "lib/registry/patchfile.h"
 #include "system/filesys.h"
-#include "pstring.h"
+#include "librpc/gen_ndr/winreg.h"
 
 struct preg_data {
        int fd;
+       TALLOC_CTX *ctx;
 };
 
 static WERROR preg_read_utf16(int fd, char *c)
@@ -39,17 +39,23 @@ static WERROR preg_read_utf16(int fd, char *c)
        push_codepoint(c, v);
        return WERR_OK;
 }
-
-/* FIXME These functions need to be implemented */
-static WERROR reg_preg_diff_add_key(void *_data, const char *key_name)
+static WERROR preg_write_utf16(int fd, const char *string)
 {
-       struct preg_data *data = (struct preg_data *)_data;
+       codepoint_t v;
+       uint16_t i;
+       size_t size;
+
+       for (i = 0; i < strlen(string); i+=size) {
+               v = next_codepoint(&string[i], &size);
+               if (write(fd, &v, 2) < 2) {
+                       return WERR_GENERAL_FAILURE;
+               }
+       }
        return WERR_OK;
 }
-
-static WERROR reg_preg_diff_del_key(void *_data, const char *key_name)
+/* PReg does not support adding keys. */
+static WERROR reg_preg_diff_add_key(void *_data, const char *key_name)
 {
-       struct preg_data *data = (struct preg_data *)_data;
        return WERR_OK;
 }
 
@@ -58,26 +64,72 @@ static WERROR reg_preg_diff_set_value(void *_data, const char *key_name,
                                      uint32_t value_type, DATA_BLOB value_data)
 {
        struct preg_data *data = (struct preg_data *)_data;
+       uint32_t buf;
+       
+       preg_write_utf16(data->fd, "[");
+       preg_write_utf16(data->fd, key_name);
+       preg_write_utf16(data->fd, ";");
+       preg_write_utf16(data->fd, value_name);
+       preg_write_utf16(data->fd, ";");
+       SIVAL(&buf, 0, value_type);
+       write(data->fd, &buf, sizeof(uint32_t));
+       preg_write_utf16(data->fd, ";");
+       SIVAL(&buf, 0, value_data.length);
+       write(data->fd, &buf, sizeof(uint32_t));
+       preg_write_utf16(data->fd, ";");
+       write(data->fd, value_data.data, value_data.length);
+       preg_write_utf16(data->fd, "]");
+       
        return WERR_OK;
 }
 
+static WERROR reg_preg_diff_del_key(void *_data, const char *key_name)
+{
+       struct preg_data *data = (struct preg_data *)_data;
+       char *parent_name;
+       DATA_BLOB blob;
+
+       parent_name = talloc_strndup(data->ctx, key_name, strrchr(key_name, '\\')-key_name);
+       blob.data = (uint8_t *)talloc_strndup(data->ctx, key_name+(strrchr(key_name, '\\')-key_name)+1,
+                       strlen(key_name)-(strrchr(key_name, '\\')-key_name));
+       blob.length = strlen((char *)blob.data)+1;
+       
+
+       /* FIXME: These values should be accumulated to be written at done(). */
+       return reg_preg_diff_set_value(data, parent_name, "**DeleteKeys", REG_SZ, blob);
+}
+
 static WERROR reg_preg_diff_del_value(void *_data, const char *key_name,
                                      const char *value_name)
 {
        struct preg_data *data = (struct preg_data *)_data;
-       return WERR_OK;
+       char *val;
+       DATA_BLOB blob;
+
+       val = talloc_asprintf(data->ctx, "**Del.%s", value_name);
+
+       blob.data = (uint8_t *)talloc(data->ctx, uint32_t);
+       SIVAL(blob.data, 0, 0);
+       blob.length = 4;
+       return reg_preg_diff_set_value(data, key_name, val, REG_DWORD, blob);
 }
 
 static WERROR reg_preg_diff_del_all_values(void *_data, const char *key_name)
 {
        struct preg_data *data = (struct preg_data *)_data;
-       return WERR_OK;
+       DATA_BLOB blob;
+
+       blob.data = (uint8_t *)talloc(data->ctx, uint32_t);
+       SIVAL(blob.data, 0, 0);
+       blob.length = 4;
+
+       return reg_preg_diff_set_value(data, key_name, "**DelVals.", REG_DWORD, blob);
 }
 
 static WERROR reg_preg_diff_done(void *_data)
 {
        struct preg_data *data = (struct preg_data *)_data;
-
+       
        close(data->fd);
        talloc_free(data);
        return WERR_OK;
@@ -87,6 +139,7 @@ static WERROR reg_preg_diff_done(void *_data)
  * Save registry diff
  */
 _PUBLIC_ WERROR reg_preg_diff_save(TALLOC_CTX *ctx, const char *filename,
+                                  struct smb_iconv_convenience *ic,
                                   struct reg_diff_callbacks **callbacks,
                                   void **callback_data)
 {
@@ -101,18 +154,21 @@ _PUBLIC_ WERROR reg_preg_diff_save(TALLOC_CTX *ctx, const char *filename,
        *callback_data = data;
 
        if (filename) {
-               data->fd = open(filename, O_CREAT, 0755);
-               if (data->fd == -1) {
+               data->fd = open(filename, O_CREAT|O_WRONLY, 0755);
+               if (data->fd < 0) {
                        DEBUG(0, ("Unable to open %s\n", filename));
                        return WERR_BADFILE;
                }
        } else {
                data->fd = STDOUT_FILENO;
        }
-       snprintf(preg_header.hdr, 4, "PReg");
+
+       strncpy(preg_header.hdr, "PReg", 4);
        SIVAL(&preg_header, 4, 1);
        write(data->fd, (uint8_t *)&preg_header,8);
 
+       data->ctx = ctx;
+
        *callbacks = talloc(ctx, struct reg_diff_callbacks);
 
        (*callbacks)->add_key = reg_preg_diff_add_key;
@@ -128,6 +184,7 @@ _PUBLIC_ WERROR reg_preg_diff_save(TALLOC_CTX *ctx, const char *filename,
  * Load diff file
  */
 _PUBLIC_ WERROR reg_preg_diff_load(int fd,
+                                  struct smb_iconv_convenience *iconv_convenience, 
                                   const struct reg_diff_callbacks *callbacks,
                                   void *callback_data)
 {
@@ -135,22 +192,31 @@ _PUBLIC_ WERROR reg_preg_diff_load(int fd,
                char hdr[4];
                uint32_t version;
        } preg_header;
-       pstring buf;
-       char *buf_ptr = buf;
+       char *buf;
+       size_t buf_size = 1024;
+       char *buf_ptr;
        TALLOC_CTX *mem_ctx = talloc_init("reg_preg_diff_load");
+       WERROR ret = WERR_OK;
+       DATA_BLOB data = {NULL, 0};
+       char *key = NULL;
+       char *value_name = NULL;
 
+       buf = talloc_array(mem_ctx, char, buf_size);
+       buf_ptr = buf;
 
        /* Read first 8 bytes (the header) */
        if (read(fd, &preg_header, 8) != 8) {
                DEBUG(0, ("Could not read PReg file: %s\n",
                                strerror(errno)));
-               close(fd);
-               return WERR_GENERAL_FAILURE;
+               ret = WERR_GENERAL_FAILURE;
+               goto cleanup;
        }
+       preg_header.version = IVAL(&preg_header.version, 0);
+
        if (strncmp(preg_header.hdr, "PReg", 4) != 0) {
                DEBUG(0, ("This file is not a valid preg registry file\n"));
-               close(fd);
-               return WERR_GENERAL_FAILURE;
+               ret = WERR_GENERAL_FAILURE;
+               goto cleanup;
        }
        if (preg_header.version > 1) {
                DEBUG(0, ("Warning: file format version is higher than expected.\n"));
@@ -158,77 +224,79 @@ _PUBLIC_ WERROR reg_preg_diff_load(int fd,
 
        /* Read the entries */
        while(1) {
-               char *key, *value_name;
                uint32_t value_type, length;
-               DATA_BLOB data;
 
                if (!W_ERROR_IS_OK(preg_read_utf16(fd, buf_ptr))) {
                        break;
                }
                if (*buf_ptr != '[') {
                        DEBUG(0, ("Error in PReg file.\n"));
-                       close(fd);
-                       return WERR_GENERAL_FAILURE;
+                       ret = WERR_GENERAL_FAILURE;
+                       goto cleanup;
                }
 
                /* Get the path */
                buf_ptr = buf;
                while (W_ERROR_IS_OK(preg_read_utf16(fd, buf_ptr)) &&
-                      *buf_ptr != ';' && buf_ptr-buf < sizeof(buf)) {
+                      *buf_ptr != ';' && buf_ptr-buf < buf_size) {
                        buf_ptr++;
                }
-               key = talloc_asprintf(mem_ctx, "\\%s", buf);
+               buf[buf_ptr-buf] = '\0';
+               key = talloc_strdup(mem_ctx, buf);
 
                /* Get the name */
                buf_ptr = buf;
                while (W_ERROR_IS_OK(preg_read_utf16(fd, buf_ptr)) &&
-                      *buf_ptr != ';' && buf_ptr-buf < sizeof(buf)) {
+                      *buf_ptr != ';' && buf_ptr-buf < buf_size) {
                        buf_ptr++;
                }
+               buf[buf_ptr-buf] = '\0';
                value_name = talloc_strdup(mem_ctx, buf);
 
                /* Get the type */
                if (read(fd, &value_type, 4) < 4) {
                        DEBUG(0, ("Error while reading PReg\n"));
-                       close(fd);
-                       return WERR_GENERAL_FAILURE;
+                       ret = WERR_GENERAL_FAILURE;
+                       goto cleanup;
                }
+               value_type = IVAL(&value_type, 0);
+
                /* Read past delimiter */
                buf_ptr = buf;
                if (!(W_ERROR_IS_OK(preg_read_utf16(fd, buf_ptr)) &&
-                   *buf_ptr == ';') && buf_ptr-buf < sizeof(buf)) {
+                   *buf_ptr == ';') && buf_ptr-buf < buf_size) {
                        DEBUG(0, ("Error in PReg file.\n"));
-                       close(fd);
-                       return WERR_GENERAL_FAILURE;
+                       ret = WERR_GENERAL_FAILURE;
+                       goto cleanup;
                }
                /* Get data length */
                if (read(fd, &length, 4) < 4) {
                        DEBUG(0, ("Error while reading PReg\n"));
-                       close(fd);
-                       return WERR_GENERAL_FAILURE;
+                       ret = WERR_GENERAL_FAILURE;
+                       goto cleanup;
                }
                /* Read past delimiter */
                buf_ptr = buf;
                if (!(W_ERROR_IS_OK(preg_read_utf16(fd, buf_ptr)) &&
-                   *buf_ptr == ';') && buf_ptr-buf < sizeof(buf)) {
+                   *buf_ptr == ';') && buf_ptr-buf < buf_size) {
                        DEBUG(0, ("Error in PReg file.\n"));
-                       close(fd);
-                       return WERR_GENERAL_FAILURE;
+                       ret = WERR_GENERAL_FAILURE;
+                       goto cleanup;
                }
                /* Get the data */
                buf_ptr = buf;
-               if (length < sizeof(buf) &&
+               if (length < buf_size &&
                    read(fd, buf_ptr, length) != length) {
                        DEBUG(0, ("Error while reading PReg\n"));
-                       close(fd);
-                       return WERR_GENERAL_FAILURE;
+                       ret = WERR_GENERAL_FAILURE;
+                       goto cleanup;
                }
                data = data_blob_talloc(mem_ctx, buf, length);
 
                /* Check if delimiter is in place (whine if it isn't) */
                buf_ptr = buf;
                if (!(W_ERROR_IS_OK(preg_read_utf16(fd, buf_ptr)) &&
-                   *buf_ptr == ']') && buf_ptr-buf < sizeof(buf)) {
+                   *buf_ptr == ']') && buf_ptr-buf < buf_size) {
                        DEBUG(0, ("Warning: Missing ']' in PReg file, expected ']', got '%c' 0x%x.\n",
                                *buf_ptr, *buf_ptr));
                }
@@ -277,10 +345,12 @@ _PUBLIC_ WERROR reg_preg_diff_load(int fd,
                        callbacks->set_value(callback_data, key, value_name,
                                             value_type, data);
                }
-               talloc_free(key);
-               talloc_free(value_name);
-               talloc_free(data.data);
        }
+cleanup:
        close(fd);
-       return WERR_OK;
+       talloc_free(data.data);
+       talloc_free(key);
+       talloc_free(value_name);
+       talloc_free(buf);
+       return ret;
 }