mirror of
https://github.com/samba-team/samba.git
synced 2025-12-16 00:23:52 +03:00
Rather than map the error returned by the registry to the correct error, return the correct error in the first place. Also deal with the fact that the right error code is now returned in a couple of places.
445 lines
11 KiB
C
445 lines
11 KiB
C
/*
|
|
Unix SMB/CIFS implementation.
|
|
Reading registry patch files
|
|
|
|
Copyright (C) Jelmer Vernooij 2004-2007
|
|
Copyright (C) Wilco Baan Hofman 2006
|
|
|
|
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 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
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.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "includes.h"
|
|
#include "lib/registry/patchfile.h"
|
|
#include "lib/registry/registry.h"
|
|
#include "system/filesys.h"
|
|
|
|
|
|
_PUBLIC_ WERROR reg_preg_diff_load(int fd,
|
|
const struct reg_diff_callbacks *callbacks,
|
|
void *callback_data);
|
|
|
|
_PUBLIC_ WERROR reg_dotreg_diff_load(int fd,
|
|
const struct reg_diff_callbacks *callbacks,
|
|
void *callback_data);
|
|
|
|
/*
|
|
* Generate difference between two keys
|
|
*/
|
|
WERROR reg_generate_diff_key(struct registry_key *oldkey,
|
|
struct registry_key *newkey,
|
|
const char *path,
|
|
const struct reg_diff_callbacks *callbacks,
|
|
void *callback_data)
|
|
{
|
|
int i;
|
|
struct registry_key *t1, *t2;
|
|
char *tmppath;
|
|
const char *keyname1;
|
|
WERROR error, error1, error2;
|
|
TALLOC_CTX *mem_ctx = talloc_init("writediff");
|
|
uint32_t old_num_subkeys, old_num_values,
|
|
new_num_subkeys, new_num_values;
|
|
|
|
if (oldkey != NULL) {
|
|
error = reg_key_get_info(mem_ctx, oldkey, NULL,
|
|
&old_num_subkeys, &old_num_values,
|
|
NULL, NULL, NULL, NULL);
|
|
if (!W_ERROR_IS_OK(error)) {
|
|
DEBUG(0, ("Error occured while getting key info: %s\n",
|
|
win_errstr(error)));
|
|
return error;
|
|
}
|
|
} else {
|
|
old_num_subkeys = 0;
|
|
old_num_values = 0;
|
|
}
|
|
|
|
/* Subkeys that were deleted */
|
|
for (i = 0; i < old_num_subkeys; i++) {
|
|
error1 = reg_key_get_subkey_by_index(mem_ctx, oldkey, i,
|
|
&keyname1,
|
|
NULL, NULL);
|
|
if (!W_ERROR_IS_OK(error1)) {
|
|
DEBUG(0, ("Error occured while getting subkey by index: %s\n",
|
|
win_errstr(error2)));
|
|
continue;
|
|
}
|
|
|
|
if (newkey != NULL) {
|
|
error2 = reg_open_key(mem_ctx, newkey, keyname1, &t2);
|
|
|
|
if (W_ERROR_IS_OK(error2))
|
|
continue;
|
|
} else {
|
|
error2 = WERR_DEST_NOT_FOUND;
|
|
t2 = NULL;
|
|
}
|
|
|
|
if (!W_ERROR_EQUAL(error2, WERR_DEST_NOT_FOUND)) {
|
|
DEBUG(0, ("Error occured while getting subkey by name: %s\n",
|
|
win_errstr(error2)));
|
|
talloc_free(mem_ctx);
|
|
return error2;
|
|
}
|
|
|
|
/* newkey didn't have such a subkey, add del diff */
|
|
tmppath = talloc_asprintf(mem_ctx, "%s\\%s", path, keyname1);
|
|
callbacks->del_key(callback_data, tmppath);
|
|
talloc_free(tmppath);
|
|
}
|
|
|
|
if (newkey != NULL) {
|
|
error = reg_key_get_info(mem_ctx, newkey, NULL,
|
|
&new_num_subkeys, &new_num_values,
|
|
NULL, NULL, NULL, NULL);
|
|
if (!W_ERROR_IS_OK(error)) {
|
|
DEBUG(0, ("Error occured while getting key info: %s\n",
|
|
win_errstr(error)));
|
|
return error;
|
|
}
|
|
} else {
|
|
new_num_subkeys = 0;
|
|
new_num_values = 0;
|
|
}
|
|
|
|
/* Subkeys that were added */
|
|
for(i = 0; i < new_num_subkeys; i++) {
|
|
error1 = reg_key_get_subkey_by_index(mem_ctx, newkey,
|
|
i, &keyname1,
|
|
NULL, NULL);
|
|
if (!W_ERROR_IS_OK(error1)) {
|
|
DEBUG(0, ("Error occured while getting subkey by index: %s\n",
|
|
win_errstr(error1)));
|
|
talloc_free(mem_ctx);
|
|
return error1;
|
|
}
|
|
|
|
if (oldkey != NULL) {
|
|
error2 = reg_open_key(mem_ctx, oldkey, keyname1, &t1);
|
|
|
|
if (W_ERROR_IS_OK(error2))
|
|
continue;
|
|
} else {
|
|
t1 = NULL;
|
|
error2 = WERR_NOT_FOUND;
|
|
}
|
|
|
|
if (!W_ERROR_EQUAL(error2, WERR_NOT_FOUND)) {
|
|
DEBUG(0, ("Error occured while getting subkey by name: %s\n",
|
|
win_errstr(error2)));
|
|
talloc_free(mem_ctx);
|
|
return error2;
|
|
}
|
|
|
|
/* oldkey didn't have such a subkey, add add diff */
|
|
tmppath = talloc_asprintf(mem_ctx, "%s\\%s", path, keyname1);
|
|
callbacks->add_key(callback_data, tmppath);
|
|
|
|
W_ERROR_NOT_OK_RETURN(
|
|
reg_open_key(mem_ctx, newkey, keyname1, &t2));
|
|
|
|
reg_generate_diff_key(t1, t2, tmppath,
|
|
callbacks, callback_data);
|
|
talloc_free(tmppath);
|
|
}
|
|
|
|
/* Values that were changed */
|
|
for(i = 0; i < new_num_values; i++) {
|
|
const char *name;
|
|
uint32_t type1, type2;
|
|
DATA_BLOB contents1, contents2;
|
|
|
|
error1 = reg_key_get_value_by_index(mem_ctx, newkey, i,
|
|
&name, &type1, &contents1);
|
|
if (!W_ERROR_IS_OK(error1)) {
|
|
DEBUG(0, ("Unable to get key by index: %s\n",
|
|
win_errstr(error1)));
|
|
talloc_free(mem_ctx);
|
|
return error1;
|
|
}
|
|
|
|
if (oldkey != NULL) {
|
|
error2 = reg_key_get_value_by_name(mem_ctx, oldkey,
|
|
name, &type2,
|
|
&contents2);
|
|
} else
|
|
error2 = WERR_BADFILE;
|
|
|
|
if(!W_ERROR_IS_OK(error2) &&
|
|
!W_ERROR_EQUAL(error2, WERR_BADFILE)) {
|
|
DEBUG(0, ("Error occured while getting value by name: %s\n",
|
|
win_errstr(error2)));
|
|
talloc_free(mem_ctx);
|
|
return error2;
|
|
}
|
|
|
|
if (W_ERROR_IS_OK(error2) &&
|
|
data_blob_cmp(&contents1, &contents2) == 0)
|
|
continue;
|
|
|
|
callbacks->set_value(callback_data, path, name,
|
|
type1, contents1);
|
|
}
|
|
|
|
/* Values that were deleted */
|
|
for (i = 0; i < old_num_values; i++) {
|
|
const char *name;
|
|
error1 = reg_key_get_value_by_index(mem_ctx, oldkey, i, &name,
|
|
NULL, NULL);
|
|
if (!W_ERROR_IS_OK(error1)) {
|
|
DEBUG(0, ("Error ocurred getting value by index: %s\n",
|
|
win_errstr(error1)));
|
|
talloc_free(mem_ctx);
|
|
return error1;
|
|
}
|
|
|
|
error2 = reg_key_get_value_by_name(mem_ctx, newkey, name, NULL,
|
|
NULL);
|
|
|
|
if (W_ERROR_IS_OK(error2))
|
|
continue;
|
|
|
|
if (!W_ERROR_EQUAL(error2, WERR_BADFILE)) {
|
|
DEBUG(0, ("Error occured while getting value by name: %s\n",
|
|
win_errstr(error2)));
|
|
return error2;
|
|
}
|
|
|
|
callbacks->del_value(callback_data, path, name);
|
|
}
|
|
|
|
talloc_free(mem_ctx);
|
|
return WERR_OK;
|
|
}
|
|
|
|
/**
|
|
* Generate diff between two registry contexts
|
|
*/
|
|
_PUBLIC_ WERROR reg_generate_diff(struct registry_context *ctx1,
|
|
struct registry_context *ctx2,
|
|
const struct reg_diff_callbacks *callbacks,
|
|
void *callback_data)
|
|
{
|
|
int i;
|
|
WERROR error;
|
|
|
|
for(i = HKEY_FIRST; i <= HKEY_LAST; i++) {
|
|
struct registry_key *r1 = NULL, *r2 = NULL;
|
|
error = reg_get_predefined_key(ctx1, i, &r1);
|
|
if (!W_ERROR_IS_OK(error) &&
|
|
!W_ERROR_EQUAL(error, WERR_NOT_FOUND)) {
|
|
DEBUG(0, ("Unable to open hive %s for backend 1\n",
|
|
reg_get_predef_name(i)));
|
|
}
|
|
|
|
error = reg_get_predefined_key(ctx2, i, &r2);
|
|
if (!W_ERROR_IS_OK(error) &&
|
|
!W_ERROR_EQUAL(error, WERR_NOT_FOUND)) {
|
|
DEBUG(0, ("Unable to open hive %s for backend 2\n",
|
|
reg_get_predef_name(i)));
|
|
}
|
|
|
|
if (r1 == NULL && r2 == NULL)
|
|
continue;
|
|
|
|
error = reg_generate_diff_key(r1, r2, reg_get_predef_name(i),
|
|
callbacks, callback_data);
|
|
if (!W_ERROR_IS_OK(error)) {
|
|
DEBUG(0, ("Unable to determine diff: %s\n",
|
|
win_errstr(error)));
|
|
return error;
|
|
}
|
|
}
|
|
if (callbacks->done != NULL) {
|
|
callbacks->done(callback_data);
|
|
}
|
|
return WERR_OK;
|
|
}
|
|
|
|
/**
|
|
* Load diff file
|
|
*/
|
|
_PUBLIC_ WERROR reg_diff_load(const char *filename,
|
|
const struct reg_diff_callbacks *callbacks,
|
|
void *callback_data)
|
|
{
|
|
int fd;
|
|
char hdr[4];
|
|
|
|
fd = open(filename, O_RDONLY, 0);
|
|
if (fd == -1) {
|
|
DEBUG(0, ("Error opening registry patch file `%s'\n",
|
|
filename));
|
|
return WERR_GENERAL_FAILURE;
|
|
}
|
|
|
|
if (read(fd, &hdr, 4) != 4) {
|
|
DEBUG(0, ("Error reading registry patch file `%s'\n",
|
|
filename));
|
|
return WERR_GENERAL_FAILURE;
|
|
}
|
|
|
|
/* Reset position in file */
|
|
lseek(fd, 0, SEEK_SET);
|
|
#if 0
|
|
if (strncmp(hdr, "CREG", 4) == 0) {
|
|
/* Must be a W9x CREG Config.pol file */
|
|
return reg_creg_diff_load(diff, fd);
|
|
} else if (strncmp(hdr, "regf", 4) == 0) {
|
|
/* Must be a REGF NTConfig.pol file */
|
|
return reg_regf_diff_load(diff, fd);
|
|
} else
|
|
#endif
|
|
if (strncmp(hdr, "PReg", 4) == 0) {
|
|
/* Must be a GPO Registry.pol file */
|
|
return reg_preg_diff_load(fd, callbacks, callback_data);
|
|
} else {
|
|
/* Must be a normal .REG file */
|
|
return reg_dotreg_diff_load(fd, callbacks, callback_data);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* The reg_diff_apply functions
|
|
*/
|
|
static WERROR reg_diff_apply_add_key(void *_ctx, const char *key_name)
|
|
{
|
|
struct registry_context *ctx = (struct registry_context *)_ctx;
|
|
struct registry_key *tmp;
|
|
WERROR error;
|
|
|
|
error = reg_key_add_abs(ctx, ctx, key_name, 0, NULL, &tmp);
|
|
|
|
if (!W_ERROR_EQUAL(error, WERR_ALREADY_EXISTS) &&
|
|
!W_ERROR_IS_OK(error)) {
|
|
DEBUG(0, ("Error adding new key '%s': %s\n",
|
|
key_name, win_errstr(error)));
|
|
return error;
|
|
}
|
|
return WERR_OK;
|
|
}
|
|
|
|
static WERROR reg_diff_apply_del_key(void *_ctx, const char *key_name)
|
|
{
|
|
struct registry_context *ctx = (struct registry_context *)_ctx;
|
|
WERROR error;
|
|
|
|
error = reg_key_del_abs(ctx, key_name);
|
|
|
|
if(!W_ERROR_IS_OK(error)) {
|
|
DEBUG(0, ("Unable to delete key '%s'\n", key_name));
|
|
return error;
|
|
}
|
|
|
|
return WERR_OK;
|
|
}
|
|
|
|
static WERROR reg_diff_apply_set_value(void *_ctx, const char *path,
|
|
const char *value_name,
|
|
uint32_t value_type, DATA_BLOB value)
|
|
{
|
|
struct registry_context *ctx = (struct registry_context *)_ctx;
|
|
struct registry_key *tmp;
|
|
WERROR error;
|
|
|
|
/* Open key */
|
|
error = reg_open_key_abs(ctx, ctx, path, &tmp);
|
|
|
|
if (W_ERROR_EQUAL(error, WERR_DEST_NOT_FOUND)) {
|
|
DEBUG(0, ("Error opening key '%s'\n", path));
|
|
return error;
|
|
}
|
|
|
|
/* Set value */
|
|
error = reg_val_set(tmp, value_name,
|
|
value_type, value);
|
|
if (!W_ERROR_IS_OK(error)) {
|
|
DEBUG(0, ("Error setting value '%s'\n", value_name));
|
|
return error;
|
|
}
|
|
|
|
return WERR_OK;
|
|
}
|
|
|
|
static WERROR reg_diff_apply_del_value(void *_ctx, const char *key_name,
|
|
const char *value_name)
|
|
{
|
|
struct registry_context *ctx = (struct registry_context *)_ctx;
|
|
struct registry_key *tmp;
|
|
WERROR error;
|
|
|
|
/* Open key */
|
|
error = reg_open_key_abs(ctx, ctx, key_name, &tmp);
|
|
|
|
if (!W_ERROR_IS_OK(error)) {
|
|
DEBUG(0, ("Error opening key '%s'\n", key_name));
|
|
return error;
|
|
}
|
|
|
|
error = reg_del_value(tmp, value_name);
|
|
if (!W_ERROR_IS_OK(error)) {
|
|
DEBUG(0, ("Error deleting value '%s'\n", value_name));
|
|
return error;
|
|
}
|
|
|
|
|
|
return WERR_OK;
|
|
}
|
|
|
|
static WERROR reg_diff_apply_del_all_values(void *_ctx, const char *key_name)
|
|
{
|
|
struct registry_context *ctx = (struct registry_context *)_ctx;
|
|
struct registry_key *key;
|
|
WERROR error;
|
|
int i;
|
|
uint32_t num_values;
|
|
|
|
error = reg_open_key_abs(ctx, ctx, key_name, &key);
|
|
|
|
if (!W_ERROR_IS_OK(error)) {
|
|
DEBUG(0, ("Error opening key '%s'\n", key_name));
|
|
return error;
|
|
}
|
|
|
|
W_ERROR_NOT_OK_RETURN(reg_key_get_info(ctx, key, NULL,
|
|
NULL, &num_values, NULL, NULL, NULL, NULL));
|
|
|
|
for (i = 0; i < num_values; i++) {
|
|
const char *name;
|
|
W_ERROR_NOT_OK_RETURN(reg_key_get_value_by_index(ctx, key, i,
|
|
&name,
|
|
NULL, NULL));
|
|
W_ERROR_NOT_OK_RETURN(reg_del_value(key, name));
|
|
}
|
|
|
|
return WERR_OK;
|
|
}
|
|
|
|
/**
|
|
* Apply diff to a registry context
|
|
*/
|
|
_PUBLIC_ WERROR reg_diff_apply(struct registry_context *ctx, const char *filename)
|
|
{
|
|
struct reg_diff_callbacks callbacks;
|
|
|
|
callbacks.add_key = reg_diff_apply_add_key;
|
|
callbacks.del_key = reg_diff_apply_del_key;
|
|
callbacks.set_value = reg_diff_apply_set_value;
|
|
callbacks.del_value = reg_diff_apply_del_value;
|
|
callbacks.del_all_values = reg_diff_apply_del_all_values;
|
|
callbacks.done = NULL;
|
|
|
|
return reg_diff_load(filename, &callbacks, ctx);
|
|
}
|