From c424c2b857fe08587eb81a5c5e3625545119d1c2 Mon Sep 17 00:00:00 2001 From: Jelmer Vernooij Date: Sun, 4 Apr 2004 16:24:08 +0000 Subject: r20: Add the registry library. Still needs a lot of work, see source/lib/registry/TODO for details. (This used to be commit 7cab3a00d7b4b1d95a3bfa6b28f318b4aaa5d493) --- source4/lib/registry/tools/regpatch.c | 808 ++++++++++++++++++++++++++++++++++ 1 file changed, 808 insertions(+) create mode 100644 source4/lib/registry/tools/regpatch.c (limited to 'source4/lib/registry/tools/regpatch.c') diff --git a/source4/lib/registry/tools/regpatch.c b/source4/lib/registry/tools/regpatch.c new file mode 100644 index 0000000000..f76da7ebf9 --- /dev/null +++ b/source4/lib/registry/tools/regpatch.c @@ -0,0 +1,808 @@ +/* + Unix SMB/CIFS implementation. + simple registry frontend + + Copyright (C) 2002, Richard Sharpe, rsharpe@richardsharpe.com + Copyright (C) 2004, Jelmer Vernooij, jelmer@samba.org + + 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 + (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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +/* + * Routines to parse a REGEDIT4 file + * + * The file consists of: + * + * REGEDIT4 + * \[[-]key-path\]\n + * * + * + * Format: + * [cmd:]name=type:value + * + * cmd = a|d|c|add|delete|change|as|ds|cs + * + * There can be more than one key-path and value-spec. + * + * Since we want to support more than one type of file format, we + * construct a command-file structure that keeps info about the command file + */ + +#define FMT_UNREC -1 +#define FMT_REGEDIT4 0 +#define FMT_EDITREG1_1 1 + +#define FMT_STRING_REGEDIT4 "REGEDIT4" +#define FMT_STRING_EDITREG1_0 "EDITREG1.0" + +#define CMD_NONE 0 +#define CMD_ADD_KEY 1 +#define CMD_DEL_KEY 2 + +#define CMD_KEY 1 +#define CMD_VAL 2 + +#include + +typedef struct val_spec_list { + struct val_spec_list *next; + char *name; + int type; + char *val; /* Kept as a char string, really? */ +} VAL_SPEC_LIST; + +typedef struct command_s { + int cmd; + char *key; + int val_count; + VAL_SPEC_LIST *val_spec_list, *val_spec_last; +} CMD; + +typedef struct cmd_line { + int len, line_len; + char *line; +} CMD_LINE; + +static void free_val_spec_list(VAL_SPEC_LIST *vl) +{ + if (!vl) return; + if (vl->name) free(vl->name); + if (vl->val) free(vl->val); + free(vl); + +} + +/* + * Some routines to handle lines of info in the command files + */ +static void skip_to_eol(int fd) +{ + int rc; + char ch = 0; + + while ((rc = read(fd, &ch, 1)) == 1) { + if (ch == 0x0A) return; + } + if (rc < 0) { + DEBUG(0, ("Could not read file descriptor: %d, %s\n", + fd, strerror(errno))); + exit(1); + } +} + +static void free_cmd(CMD *cmd) +{ + if (!cmd) return; + + while (cmd->val_spec_list) { + VAL_SPEC_LIST *tmp; + + tmp = cmd->val_spec_list; + cmd->val_spec_list = tmp->next; + free(tmp); + } + + free(cmd); + +} + +static void free_cmd_line(CMD_LINE *cmd_line) +{ + if (cmd_line) { + if (cmd_line->line) free(cmd_line->line); + free(cmd_line); + } +} + +static void print_line(struct cmd_line *cl) +{ + char *pl; + + if (!cl) return; + + pl = smb_xmalloc(cl->line_len + 1); + + strncpy(pl, cl->line, cl->line_len); + pl[cl->line_len] = 0; + + fprintf(stdout, "%s\n", pl); + free(pl); +} + +#define INIT_ALLOC 10 + +/* + * Read a line from the input file. + * NULL returned when EOF and no chars read + * Otherwise we return a cmd_line * + * Exit if other errors + */ +static struct cmd_line *get_cmd_line(int fd) +{ + struct cmd_line *cl = (CMD_LINE *)smb_xmalloc(sizeof(CMD_LINE)); + int i = 0, rc; + unsigned char ch; + + cl->len = INIT_ALLOC; + + /* + * Allocate some space for the line. We extend later if needed. + */ + + cl->line = (char *)smb_xmalloc(INIT_ALLOC); + + /* + * Now read in the chars to EOL. Don't store the EOL in the + * line. What about CR? + */ + + while ((rc = read(fd, &ch, 1)) == 1 && ch != '\n') { + if (ch == '\r') continue; /* skip CR */ + if (i == cl->len) { + /* + * Allocate some more memory + */ + if ((cl->line = realloc(cl->line, cl->len + INIT_ALLOC)) == NULL) { + DEBUG(0, ("Unable to realloc space for line: %s\n", + strerror(errno))); + exit(1); + } + cl->len += INIT_ALLOC; + } + cl->line[i] = ch; + i++; + } + + /* read 0 and we were at loc'n 0, return NULL */ + if (rc == 0 && i == 0) { + free_cmd_line(cl); + return NULL; + } + + cl->line_len = i; + + return cl; + +} + +/* + * parse_value: parse out a value. We pull it apart as: + * + * ::= =: + * + * ::= char-string-without-spaces | '"' char-string '"' + * + * If it parsed OK, return the as a string, and the + * value type and value-string in parameters. + * + * The value name can be empty. There can only be one empty name in + * a list of values. A value of - removes the value entirely. + */ + +static char *parse_name(char *nstr) +{ + int len = 0, start = 0; + if (!nstr) return NULL; + + len = strlen(nstr); + + while (len && nstr[len - 1] == ' ') len--; + + nstr[len] = 0; /* Trim any spaces ... if there were none, doesn't matter */ + + /* + * Beginning and end should be '"' or neither should be so + */ + if ((nstr[0] == '"' && nstr[len - 1] != '"') || + (nstr[0] != '"' && nstr[len - 1] == '"')) + return NULL; + + if (nstr[0] == '"') { + start = 1; + len -= 2; + } + + return strndup(&nstr[start], len); +} + +static int parse_value_type(char *tstr) +{ + int len = strlen(tstr); + + while (len && tstr[len - 1] == ' ') len--; + tstr[len] = 0; + + if (strcmp(tstr, "REG_DWORD") == 0) + return REG_DWORD; + else if (strcmp(tstr, "dword") == 0) + return REG_DWORD; + else if (strcmp(tstr, "REG_EXPAND_SZ") == 0) + return REG_EXPAND_SZ; + else if (strcmp(tstr, "REG_BIN") == 0) + return REG_BINARY; + else if (strcmp(tstr, "REG_SZ") == 0) + return REG_SZ; + else if (strcmp(tstr, "REG_MULTI_SZ") == 0) + return REG_MULTI_SZ; + else if (strcmp(tstr, "-") == 0) + return REG_DELETE; + + return 0; +} + +static char *parse_val_str(char *vstr) +{ + + return strndup(vstr, strlen(vstr)); + +} + +static char *parse_value(struct cmd_line *cl, int *vtype, char **val) +{ + char *p1 = NULL, *p2 = NULL, *nstr = NULL, *tstr = NULL, *vstr = NULL; + + if (!cl || !vtype || !val) return NULL; + if (!cl->line_len) return NULL; + + p1 = strndup(cl->line, cl->line_len); + /* FIXME: Better return codes etc ... */ + if (!p1) return NULL; + p2 = strchr(p1, '='); + if (!p2) return NULL; + + *p2 = 0; p2++; /* Split into two strings at p2 */ + + /* Now, parse the name ... */ + + nstr = parse_name(p1); + if (!nstr) goto error; + + /* Now, split the remainder and parse on type and val ... */ + + tstr = p2; + while (*tstr == ' ') tstr++; /* Skip leading white space */ + p2 = strchr(p2, ':'); + + if (p2) { + *p2 = 0; p2++; /* split on the : */ + } + + *vtype = parse_value_type(tstr); + + if (!vtype) goto error; + + if (!p2 || !*p2) return nstr; + + /* Now, parse the value string. It should return a newly malloc'd string */ + + while (*p2 == ' ') p2++; /* Skip leading space */ + vstr = parse_val_str(p2); + + if (!vstr) goto error; + + *val = vstr; + + return nstr; + + error: + if (p1) free(p1); + if (nstr) free(nstr); + if (vstr) free(vstr); + return NULL; +} + +/* + * Parse out a key. Look for a correctly formatted key [...] + * and whether it is a delete or add? A delete is signalled + * by a - in front of the key. + * Assumes that there are no leading and trailing spaces + */ + +static char *parse_key(struct cmd_line *cl, int *cmd) +{ + int start = 1; + char *tmp; + + if (cl->line[0] != '[' || + cl->line[cl->line_len - 1] != ']') return NULL; + if (cl->line_len == 2) return NULL; + *cmd = CMD_ADD_KEY; + if (cl->line[1] == '-') { + if (cl->line_len == 3) return NULL; + start = 2; + *cmd = CMD_DEL_KEY; + } + tmp = smb_xmalloc(cl->line_len - 1 - start + 1); + strncpy(tmp, &cl->line[start], cl->line_len - 1 - start); + tmp[cl->line_len - 1 - start] = 0; + return tmp; +} + +/* + * Parse a line to determine if we have a key or a value + * We only check for key or val ... + */ + +static int parse_line(struct cmd_line *cl) +{ + + if (!cl || cl->len == 0) return 0; + + if (cl->line[0] == '[') /* No further checking for now */ + return CMD_KEY; + else + return CMD_VAL; +} + +/* + * We seek to offset 0, read in the required number of bytes, + * and compare to the correct value. + * We then seek back to the original location + */ +static int regedit4_file_type(int fd) +{ + int cur_ofs = 0; + char desc[9]; + + cur_ofs = lseek(fd, 0, SEEK_CUR); /* Get current offset */ + if (cur_ofs < 0) { + DEBUG(0, ("Unable to get current offset: %s\n", strerror(errno))); + exit(1); /* FIXME */ + } + + if (cur_ofs) { + lseek(fd, 0, SEEK_SET); + } + + if (read(fd, desc, 8) < 8) { + DEBUG(0, ("Unable to read command file format\n")); + exit(2); /* FIXME */ + } + + desc[8] = 0; + + if (strcmp(desc, FMT_STRING_REGEDIT4) == 0) { + if (cur_ofs) { + lseek(fd, cur_ofs, SEEK_SET); + } + else { + skip_to_eol(fd); + } + return FMT_REGEDIT4; + } + + return FMT_UNREC; +} + +/* + * Run though the data in the line and strip anything after a comment + * char. + */ +static void strip_comment(struct cmd_line *cl) +{ + int i; + + if (!cl) return; + + for (i = 0; i < cl->line_len; i++) { + if (cl->line[i] == ';') { + cl->line_len = i; + return; + } + } +} + +/* + * trim leading space + */ + +static void trim_leading_spaces(struct cmd_line *cl) +{ + int i; + + if (!cl) return; + + for (i = 0; i < cl->line_len; i++) { + if (cl->line[i] != ' '){ + if (i) memcpy(cl->line, &cl->line[i], cl->line_len - i); + return; + } + } +} + +/* + * trim trailing spaces + */ +static void trim_trailing_spaces(struct cmd_line *cl) +{ + int i; + + if (!cl) return; + + for (i = cl->line_len; i == 0; i--) { + if (cl->line[i-1] != ' ' && + cl->line[i-1] != '\t') { + cl->line_len = i; + } + } +} + +/* + * Get a command ... This consists of possibly multiple lines: + * [key] + * values* + * possibly Empty line + * + * value ::= =':' + * is some path, possibly enclosed in quotes ... + * We alctually look for the next key to terminate a previous key + * if == '-', then it is a delete type. + */ +static CMD *regedit4_get_cmd(int fd) +{ + struct command_s *cmd = NULL; + struct cmd_line *cl = NULL; + struct val_spec_list *vl = NULL; + + cmd = (struct command_s *)smb_xmalloc(sizeof(struct command_s)); + + cmd->cmd = CMD_NONE; + cmd->key = NULL; + cmd->val_count = 0; + cmd->val_spec_list = cmd->val_spec_last = NULL; + while ((cl = get_cmd_line(fd))) { + + /* + * If it is an empty command line, and we already have a key + * then exit from here ... FIXME: Clean up the parser + */ + + if (cl->line_len == 0 && cmd->key) { + free_cmd_line(cl); + break; + } + + strip_comment(cl); /* remove anything beyond a comment char */ + trim_trailing_spaces(cl); + trim_leading_spaces(cl); + + if (cl->line_len == 0) { /* An empty line */ + free_cmd_line(cl); + } + else { /* Else, non-empty ... */ + /* + * Parse out the bits ... + */ + switch (parse_line(cl)) { + case CMD_KEY: + if ((cmd->key = parse_key(cl, &cmd->cmd)) == NULL) { + DEBUG(0, ("Error parsing key from line: ")); + print_line(cl); + DEBUG(0, ("\n")); + } + break; + + case CMD_VAL: + /* + * We need to add the value stuff to the list + * There could be a \ on the end which we need to + * handle at some time + */ + vl = (struct val_spec_list *)smb_xmalloc(sizeof(struct val_spec_list)); + vl->next = NULL; + vl->val = NULL; + vl->name = parse_value(cl, &vl->type, &vl->val); + if (!vl->name) goto error; + if (cmd->val_spec_list == NULL) { + cmd->val_spec_list = cmd->val_spec_last = vl; + } + else { + cmd->val_spec_last->next = vl; + cmd->val_spec_last = vl; + } + cmd->val_count++; + break; + + default: + DEBUG(0, ("Unrecognized line in command file: \n")); + print_line(cl); + break; + } + } + + } + if (!cmd->cmd) goto error; /* End of file ... */ + + return cmd; + + error: + if (vl) free(vl); + if (cmd) free_cmd(cmd); + return NULL; +} + +static int regedit4_exec_cmd(CMD *cmd) +{ + + return 0; +} + +static int editreg_1_0_file_type(int fd) +{ + int cur_ofs = 0; + char desc[11]; + + cur_ofs = lseek(fd, 0, SEEK_CUR); /* Get current offset */ + if (cur_ofs < 0) { + DEBUG(0, ("Unable to get current offset: %s\n", strerror(errno))); + exit(1); /* FIXME */ + } + + if (cur_ofs) { + lseek(fd, 0, SEEK_SET); + } + + if (read(fd, desc, 10) < 10) { + DEBUG(0, ("Unable to read command file format\n")); + exit(2); /* FIXME */ + } + + desc[10] = 0; + + if (strcmp(desc, FMT_STRING_EDITREG1_0) == 0) { + lseek(fd, cur_ofs, SEEK_SET); + return FMT_REGEDIT4; + } + + return FMT_UNREC; +} + +static CMD *editreg_1_0_get_cmd(int fd) +{ + return NULL; +} + +static int editreg_1_0_exec_cmd(CMD *cmd) +{ + + return -1; +} + +typedef struct command_ops_s { + int type; + int (*file_type)(int fd); + CMD *(*get_cmd)(int fd); + int (*exec_cmd)(CMD *cmd); +} CMD_OPS; + +CMD_OPS default_cmd_ops[] = { + {0, regedit4_file_type, regedit4_get_cmd, regedit4_exec_cmd}, + {1, editreg_1_0_file_type, editreg_1_0_get_cmd, editreg_1_0_exec_cmd}, + {-1, NULL, NULL, NULL} +}; + +typedef struct command_file_s { + char *name; + int type, fd; + CMD_OPS cmd_ops; +} CMD_FILE; + +/* + * Create a new command file structure + */ + +static CMD_FILE *cmd_file_create(char *file) +{ + CMD_FILE *tmp; + struct stat sbuf; + int i = 0; + + /* + * Let's check if the file exists ... + * No use creating the cmd_file structure if the file does not exist + */ + + if (stat(file, &sbuf) < 0) { /* Not able to access file */ + + return NULL; + } + + tmp = (CMD_FILE *)smb_xmalloc(sizeof(CMD_FILE)); + + /* + * Let's fill in some of the fields; + */ + + tmp->name = strdup(file); + + if ((tmp->fd = open(file, O_RDONLY, 666)) < 0) { + free(tmp); + return NULL; + } + + /* + * Now, try to find the format by indexing through the table + */ + while (default_cmd_ops[i].type != -1) { + if ((tmp->type = default_cmd_ops[i].file_type(tmp->fd)) >= 0) { + tmp->cmd_ops = default_cmd_ops[i]; + return tmp; + } + i++; + } + + /* + * If we got here, return NULL, as we could not figure out the type + * of command file. + * + * What about errors? + */ + + free(tmp); + return NULL; +} + +/* + * Extract commands from the command file, and execute them. + * We pass a table of command callbacks for that + */ + +//FIXME + +/* + * Main code from here on ... + */ + +/* + * key print function here ... + */ + +/* + * Sec Desc print functions + */ + +char *str_type(unsigned char type); + +int nt_apply_reg_command_file(REG_HANDLE *regf, const char *cmd_file_name) +{ + CMD *cmd; + int modified = 0; + CMD_FILE *cmd_file = NULL; + cmd_file = cmd_file_create(cmd_file_name); + + while ((cmd = cmd_file->cmd_ops.get_cmd(cmd_file->fd)) != NULL) { + + /* + * Now, apply the requests to the tree ... + */ + switch (cmd->cmd) { + case CMD_ADD_KEY: { + REG_KEY *tmp = NULL; + tmp = reg_open_key(reg_get_root(regf), cmd->key); + /* If we found it, apply the other bits, else create such a key */ + if (!tmp) { + if(reg_key_add_name(reg_get_root(regf), cmd->key)) { + tmp = reg_open_key(reg_get_root(regf), cmd->key); + } + modified = 1; + } + + while (cmd->val_count) { + VAL_SPEC_LIST *val = cmd->val_spec_list; + REG_VAL *reg_val = NULL; + + if (val->type == REG_DELETE) { + reg_val = reg_key_get_value_by_name( tmp, val->name); + reg_val_del(reg_val); + modified = 1; + } + else { + /* FIXME + reg_val = nt_add_reg_value(tmp, val->name, val->type, + val->val); */ + modified = 1; + } + + cmd->val_spec_list = val->next; + free_val_spec_list(val); + cmd->val_count--; + } + + break; + } + + case CMD_DEL_KEY: + /* + * Any value does not matter ... + * Find the key if it exists, and delete it ... + */ + + reg_key_del_recursive(reg_open_key(reg_get_root(regf), cmd->key)); + modified = 1; + break; + } + } + free_cmd(cmd); + + return modified; +} + +int main (int argc, char **argv) +{ + uint32 setparms, checkparms; + int opt; + poptContext pc; + REG_KEY *root; + const char *location; + const char *patch; + char *backend = "dir"; + REG_HANDLE *h; + int fullpath = 0, no_values = 0; + struct poptOption long_options[] = { + POPT_AUTOHELP + {"backend", 'b', POPT_ARG_STRING, &backend, 'b', "backend to use", NULL}, + POPT_TABLEEND + }; + + pc = poptGetContext(argv[0], argc, (const char **) argv, long_options,0); + + while((opt = poptGetNextOpt(pc)) != -1) { + } + + setup_logging(argv[0], True); + + location = poptGetArg(pc); + if(!location) { + poptPrintUsage(pc, stderr, 0); + return 1; + } + + h = reg_open(backend, location, True); + if(!h) { + fprintf(stderr, "Unable to open '%s' with backend '%s'\n", location, backend); + return 1; + } + + poptFreeContext(pc); + + patch = poptGetArg(pc); + if(!patch) patch = "/dev/stdin"; + + nt_apply_reg_command_file(h, patch); + + return 0; +} -- cgit