From 58d50a614f1b4a3fc6b60ad5f777d987263fe54f Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Wed, 31 Mar 2004 06:45:39 +0000 Subject: make a more recent snapshot of ldb available to interested people. Note that I decided to make it LGPL. ldb is not finished yet, but enough of it is there for people to get an idea of what it does, and quite a few simple tests work (This used to be commit dc6f41f9e777d37f883303ddef0d96840d80f78e) --- source4/lib/ldb/ldb_tdb/.cvsignore | 7 + source4/lib/ldb/ldb_tdb/ldb_index.c | 641 +++++++++++++++++++++++++++++++++++ source4/lib/ldb/ldb_tdb/ldb_ldif.c | 366 ++++++++++++++++++++ source4/lib/ldb/ldb_tdb/ldb_match.c | 176 ++++++++++ source4/lib/ldb/ldb_tdb/ldb_pack.c | 174 ++++++++++ source4/lib/ldb/ldb_tdb/ldb_parse.c | 448 ++++++++++++++++++++++++ source4/lib/ldb/ldb_tdb/ldb_parse.h | 40 +++ source4/lib/ldb/ldb_tdb/ldb_search.c | 482 ++++++++++++++++++++++++++ source4/lib/ldb/ldb_tdb/ldb_tdb.c | 303 +++++++++++++++++ source4/lib/ldb/ldb_tdb/ldb_tdb.h | 11 + source4/lib/ldb/ldb_tdb/ldbadd.c | 55 +++ source4/lib/ldb/ldb_tdb/ldbdel.c | 50 +++ source4/lib/ldb/ldb_tdb/ldbsearch.c | 73 ++++ 13 files changed, 2826 insertions(+) create mode 100644 source4/lib/ldb/ldb_tdb/.cvsignore create mode 100644 source4/lib/ldb/ldb_tdb/ldb_index.c create mode 100644 source4/lib/ldb/ldb_tdb/ldb_ldif.c create mode 100644 source4/lib/ldb/ldb_tdb/ldb_match.c create mode 100644 source4/lib/ldb/ldb_tdb/ldb_pack.c create mode 100644 source4/lib/ldb/ldb_tdb/ldb_parse.c create mode 100644 source4/lib/ldb/ldb_tdb/ldb_parse.h create mode 100644 source4/lib/ldb/ldb_tdb/ldb_search.c create mode 100644 source4/lib/ldb/ldb_tdb/ldb_tdb.c create mode 100644 source4/lib/ldb/ldb_tdb/ldb_tdb.h create mode 100644 source4/lib/ldb/ldb_tdb/ldbadd.c create mode 100644 source4/lib/ldb/ldb_tdb/ldbdel.c create mode 100644 source4/lib/ldb/ldb_tdb/ldbsearch.c (limited to 'source4/lib/ldb/ldb_tdb') diff --git a/source4/lib/ldb/ldb_tdb/.cvsignore b/source4/lib/ldb/ldb_tdb/.cvsignore new file mode 100644 index 0000000000..8f968d06f4 --- /dev/null +++ b/source4/lib/ldb/ldb_tdb/.cvsignore @@ -0,0 +1,7 @@ +ldbadd +ldbsearch +ldbdel +test.ldb +TAGS +.*~ +*.o diff --git a/source4/lib/ldb/ldb_tdb/ldb_index.c b/source4/lib/ldb/ldb_tdb/ldb_index.c new file mode 100644 index 0000000000..dda80a6b2a --- /dev/null +++ b/source4/lib/ldb/ldb_tdb/ldb_index.c @@ -0,0 +1,641 @@ +/* + ldb database library + + Copyright (C) Andrew Tridgell 2004 + + ** NOTE! The following LGPL license applies to the ldb + ** library. This does NOT imply that all of Samba is released + ** under the LGPL + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* + * Name: ldb + * + * Component: ldb tdb backend - indexing + * + * Description: indexing routines for ldb tdb backend + * + * Author: Andrew Tridgell + */ + +#include "includes.h" + +struct dn_list { + unsigned int count; + char **dn; +}; + +/* + free a struct dn_list +*/ +static void dn_list_free(struct dn_list *list) +{ + int i; + for (i=0;icount;i++) { + free(list->dn[i]); + } + if (list->dn) free(list->dn); +} + +/* + return the dn key to be used for an index + caller frees +*/ +static char *ldb_dn_key(const char *attr, const struct ldb_val *value) +{ + char *ret = NULL; + + if (ldb_should_b64_encode(value)) { + char *vstr = ldb_base64_encode(value->data, value->length); + if (!vstr) return NULL; + asprintf(&ret, "@INDEX:%s::%s", attr, vstr); + free(vstr); + return ret; + } + + asprintf(&ret, "@INDEX:%s:%s", attr, (char *)value->data); + return ret; +} + +/* + see if a attribute value is in the list of indexed attributes +*/ +static int ldb_msg_find_idx(const struct ldb_message *msg, const char *attr) +{ + int i; + for (i=0;inum_elements;i++) { + if (strcmp(msg->elements[i].name, "@IDXATTR") == 0 && + strcmp((char *)msg->elements[i].value.data, attr) == 0) { + return i; + } + } + return -1; +} + +/* + return a list of dn's that might match a simple indexed search or + */ +static int ltdb_index_dn_simple(struct ldb_context *ldb, + struct ldb_parse_tree *tree, + const struct ldb_message *index_list, + struct dn_list *list) +{ + char *dn = NULL; + int ret, i; + struct ldb_message msg; + + list->count = 0; + list->dn = NULL; + + /* + if the value is a wildcard then we can't do a match via indexing + */ + if (ltdb_has_wildcard(&tree->u.simple.value)) { + return -1; + } + + /* if the attribute isn't in the list of indexed attributes then + this node needs a full search */ + if (ldb_msg_find_idx(index_list, tree->u.simple.attr) == -1) { + return -1; + } + + /* the attribute is indexed. Pull the list of DNs that match the + search criterion */ + dn = ldb_dn_key(tree->u.simple.attr, &tree->u.simple.value); + if (!dn) return -1; + + ret = ltdb_search_dn1(ldb, dn, &msg); + free(dn); + if (ret == 0 || ret == -1) { + return ret; + } + + list->dn = malloc_array_p(char *, msg.num_elements); + if (!list->dn) { + ltdb_search_dn1_free(ldb, &msg); + } + + for (i=0;idn[list->count] = + strdup((char *)msg.elements[i].value.data); + if (!list->dn[list->count]) { + dn_list_free(list); + ltdb_search_dn1_free(ldb, &msg); + return -1; + } + list->count++; + } + + ltdb_search_dn1_free(ldb, &msg); + + qsort(list->dn, list->count, sizeof(char *), (comparison_fn_t) strcmp); + + return 1; +} + +/* + list intersection + list = list & list2 + relies on the lists being sorted +*/ +static int list_intersect(struct dn_list *list, const struct dn_list *list2) +{ + struct dn_list list3; + int i; + + if (list->count == 0 || list2->count == 0) { + /* 0 & X == 0 */ + dn_list_free(list); + return 0; + } + + list3.dn = malloc_array_p(char *, list->count); + if (!list3.dn) { + dn_list_free(list); + return -1; + } + list3.count = 0; + + for (i=0;icount;i++) { + if (list_find(list->dn[i], list2->dn, list2->count, + sizeof(char *), (comparison_fn_t)strcmp) != -1) { + list3.dn[list3.count] = list->dn[i]; + list3.count++; + } else { + free(list->dn[i]); + } + } + + free(list->dn); + list->dn = list3.dn; + list->count = list3.count; + + return 0; +} + + +/* + list union + list = list | list2 + relies on the lists being sorted +*/ +static int list_union(struct dn_list *list, const struct dn_list *list2) +{ + int i; + char **d; + unsigned int count = list->count; + + if (list->count == 0 && list2->count == 0) { + /* 0 | 0 == 0 */ + dn_list_free(list); + return 0; + } + + d = realloc_p(list->dn, char *, list->count + list2->count); + if (!d) { + dn_list_free(list); + return -1; + } + list->dn = d; + + for (i=0;icount;i++) { + if (list_find(list2->dn[i], list->dn, count, + sizeof(char *), (comparison_fn_t)strcmp) == -1) { + list->dn[list->count] = strdup(list2->dn[i]); + if (!list->dn[list->count]) { + dn_list_free(list); + return -1; + } + list->count++; + } + } + + if (list->count != count) { + qsort(list->dn, list->count, sizeof(char *), (comparison_fn_t)strcmp); + } + + return 0; +} + +static int ltdb_index_dn(struct ldb_context *ldb, + struct ldb_parse_tree *tree, + const struct ldb_message *index_list, + struct dn_list *list); + + +/* + OR two index results + */ +static int ltdb_index_dn_or(struct ldb_context *ldb, + struct ldb_parse_tree *tree, + const struct ldb_message *index_list, + struct dn_list *list) +{ + int ret, i; + + ret = -1; + list->dn = NULL; + list->count = 0; + + for (i=0;iu.list.num_elements;i++) { + struct dn_list list2; + int v; + v = ltdb_index_dn(ldb, tree->u.list.elements[i], index_list, &list2); + + if (v == 0) { + /* 0 || X == X */ + if (ret == -1) { + ret = 0; + } + continue; + } + + if (v == -1) { + /* 1 || X == 1 */ + dn_list_free(list); + return -1; + } + + if (ret == -1) { + ret = 1; + *list = list2; + } else { + if (list_union(list, &list2) == -1) { + dn_list_free(&list2); + return -1; + } + dn_list_free(&list2); + } + } + + if (list->count == 0) { + dn_list_free(list); + return 0; + } + + return ret; +} + + +/* + NOT an index results + */ +static int ltdb_index_dn_not(struct ldb_context *ldb, + struct ldb_parse_tree *tree, + const struct ldb_message *index_list, + struct dn_list *list) +{ + /* the only way to do an indexed not would be if we could + negate the not via another not or if we knew the total + number of database elements so we could know that the + existing expression covered the whole database. + + instead, we just give up, and rely on a full index scan + (unless an outer & manages to reduce the list) + */ + return -1; +} + +/* + AND two index results + */ +static int ltdb_index_dn_and(struct ldb_context *ldb, + struct ldb_parse_tree *tree, + const struct ldb_message *index_list, + struct dn_list *list) +{ + int ret, i; + + ret = -1; + list->dn = NULL; + list->count = 0; + + for (i=0;iu.list.num_elements;i++) { + struct dn_list list2; + int v; + v = ltdb_index_dn(ldb, tree->u.list.elements[i], index_list, &list2); + + if (v == 0) { + /* 0 && X == 0 */ + dn_list_free(list); + return 0; + } + + if (v == -1) { + continue; + } + + if (ret == -1) { + ret = 1; + *list = list2; + } else { + if (list_intersect(list, &list2) == -1) { + dn_list_free(&list2); + return -1; + } + dn_list_free(&list2); + } + + if (list->count == 0) { + if (list->dn) free(list->dn); + return 0; + } + } + + return ret; +} + +/* + return a list of dn's that might match a indexed search or + -1 if an error. return 0 for no matches, or 1 for matches + */ +static int ltdb_index_dn(struct ldb_context *ldb, + struct ldb_parse_tree *tree, + const struct ldb_message *index_list, + struct dn_list *list) +{ + int ret; + + switch (tree->operation) { + case LDB_OP_SIMPLE: + ret = ltdb_index_dn_simple(ldb, tree, index_list, list); + break; + + case LDB_OP_AND: + ret = ltdb_index_dn_and(ldb, tree, index_list, list); + break; + + case LDB_OP_OR: + ret = ltdb_index_dn_or(ldb, tree, index_list, list); + break; + + case LDB_OP_NOT: + ret = ltdb_index_dn_not(ldb, tree, index_list, list); + break; + } + + return ret; +} + +/* + filter a candidate dn_list from an indexed search into a set of results + extracting just the given attributes +*/ +static int ldb_index_filter(struct ldb_context *ldb, struct ldb_parse_tree *tree, + const char *base, + enum ldb_scope scope, + const struct dn_list *dn_list, + const char *attrs[], struct ldb_message ***res) +{ + int i; + unsigned int count = 0; + + for (i=0;icount;i++) { + struct ldb_message msg; + int ret; + ret = ltdb_search_dn1(ldb, dn_list->dn[i], &msg); + if (ret == 0) { + /* the record has disappeared? yes, this can happen */ + continue; + } + + if (ret == -1) { + /* an internal error */ + return -1; + } + + if (ldb_message_match(ldb, &msg, tree, base, scope) == 1) { + ret = ltdb_add_attr_results(ldb, &msg, attrs, &count, res); + } + ltdb_search_dn1_free(ldb, &msg); + if (ret != 0) { + return -1; + } + } + + return count; +} + +/* + search the database with a LDAP-like expression using indexes + returns -1 if an indexed search is not possible, in which + case the caller should call ltdb_search_full() +*/ +int ltdb_search_indexed(struct ldb_context *ldb, + const char *base, + enum ldb_scope scope, + struct ldb_parse_tree *tree, + const char *attrs[], struct ldb_message ***res) +{ + struct ldb_message index_list; + struct dn_list dn_list; + int ret; + + /* find the list of indexed fields */ + ret = ltdb_search_dn1(ldb, "@INDEXLIST", &index_list); + if (ret != 1) { + /* no index list? must do full search */ + return -1; + } + + ret = ltdb_index_dn(ldb, tree, &index_list, &dn_list); + ltdb_search_dn1_free(ldb, &index_list); + + if (ret == 1) { + /* we've got a candidate list - now filter by the full tree + and extract the needed attributes */ + ret = ldb_index_filter(ldb, tree, base, scope, &dn_list, + attrs, res); + dn_list_free(&dn_list); + } + + return ret; +} + +/* + add an index entry for one message element +*/ +static int ltdb_index_add1(struct ldb_context *ldb, const char *dn, + struct ldb_message_element *el) +{ + struct ldb_message msg; + char *dn_key; + int ret; + struct ldb_message_element *el2; + + dn_key = ldb_dn_key(el->name, &el->value); + if (!dn_key) { + return -1; + } + + ret = ltdb_search_dn1(ldb, dn_key, &msg); + if (ret == -1) { + free(dn_key); + return -1; + } + + if (ret == 0) { + msg.dn = dn_key; + msg.num_elements = 0; + msg.elements = NULL; + msg.private = NULL; + } + + /* add another entry */ + el2 = realloc_p(msg.elements, struct ldb_message_element, msg.num_elements+1); + if (!el2) { + if (ret == 1) { + ltdb_search_dn1_free(ldb, &msg); + } + free(dn_key); + return -1; + } + + msg.elements = el2; + msg.elements[msg.num_elements].name = "@IDX"; + msg.elements[msg.num_elements].value.length = strlen(dn); + msg.elements[msg.num_elements].value.data = dn; + msg.num_elements++; + + ret = ltdb_store(ldb, &msg, TDB_REPLACE); + + if (msg.num_elements == 1) { + free(msg.elements); + } else { + ltdb_search_dn1_free(ldb, &msg); + } + + return ret; +} + +/* + add the index entries for a new record + return -1 on failure +*/ +int ltdb_index_add(struct ldb_context *ldb, const struct ldb_message *msg) +{ + int ret, i; + struct ldb_message index_list; + + /* find the list of indexed fields */ + ret = ltdb_search_dn1(ldb, "@INDEXLIST", &index_list); + if (ret != 1) { + /* no indexed fields or an error */ + return ret; + } + + for (i=0;inum_elements;i++) { + ret = ldb_msg_find_idx(&index_list, msg->elements[i].name); + if (ret == -1) { + continue; + } + ret = ltdb_index_add1(ldb, msg->dn, &msg->elements[i]); + if (ret == -1) { + ltdb_search_dn1_free(ldb, &index_list); + return -1; + } + } + + return 0; +} + + +/* + delete an index entry for one message element +*/ +static int ltdb_index_del1(struct ldb_context *ldb, const char *dn, + struct ldb_message_element *el) +{ + struct ldb_message msg; + char *dn_key; + int ret, i; + + dn_key = ldb_dn_key(el->name, &el->value); + if (!dn_key) { + return -1; + } + + ret = ltdb_search_dn1(ldb, dn_key, &msg); + if (ret == -1) { + free(dn_key); + return -1; + } + + if (ret == 0) { + /* it wasn't indexed. Did we have an earlier error? If we did then + its gone now */ + ltdb_search_dn1_free(ldb, &msg); + return 0; + } + + i = ldb_msg_find_idx(&msg, dn); + if (i == -1) { + /* it ain't there. hmmm */ + ltdb_search_dn1_free(ldb, &msg); + return 0; + } + + if (i != msg.num_elements - 1) { + memmove(&msg.elements[i], &msg.elements[i+1], sizeof(msg.elements[i])); + } + msg.num_elements--; + + if (msg.num_elements == 0) { + ret = ltdb_delete_noindex(ldb, dn_key); + } else { + ret = ltdb_store(ldb, &msg, TDB_REPLACE); + } + + ltdb_search_dn1_free(ldb, &msg); + + return ret; +} + +/* + delete the index entries for a record + return -1 on failure +*/ +int ltdb_index_del(struct ldb_context *ldb, const struct ldb_message *msg) +{ + int ret, i; + struct ldb_message index_list; + + /* find the list of indexed fields */ + ret = ltdb_search_dn1(ldb, "@INDEXLIST", &index_list); + if (ret != 1) { + /* no indexed fields or an error */ + return ret; + } + + for (i=0;inum_elements;i++) { + ret = ldb_msg_find_idx(&index_list, msg->elements[i].name); + if (ret == -1) { + continue; + } + ret = ltdb_index_del1(ldb, msg->dn, &msg->elements[i]); + if (ret == -1) { + ltdb_search_dn1_free(ldb, &index_list); + return -1; + } + } + + return 0; +} diff --git a/source4/lib/ldb/ldb_tdb/ldb_ldif.c b/source4/lib/ldb/ldb_tdb/ldb_ldif.c new file mode 100644 index 0000000000..170685cc03 --- /dev/null +++ b/source4/lib/ldb/ldb_tdb/ldb_ldif.c @@ -0,0 +1,366 @@ + /* + Unix SMB/CIFS implementation. + + ldif utilities for ldb + + Copyright (C) Andrew Tridgell 2004 + + 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" + + +/* + this base64 decoder was taken from jitterbug (written by tridge). + we might need to replace it with a new version +*/ +static int base64_decode(char *s) +{ + const char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + int bit_offset, byte_offset, idx, i, n; + unsigned char *d = (unsigned char *)s; + char *p; + + n=i=0; + + while (*s && (p=strchr(b64,*s))) { + idx = (int)(p - b64); + byte_offset = (i*6)/8; + bit_offset = (i*6)%8; + d[byte_offset] &= ~((1<<(8-bit_offset))-1); + if (bit_offset < 3) { + d[byte_offset] |= (idx << (2-bit_offset)); + n = byte_offset+1; + } else { + d[byte_offset] |= (idx >> (bit_offset-2)); + d[byte_offset+1] = 0; + d[byte_offset+1] |= (idx << (8-(bit_offset-2))) & 0xFF; + n = byte_offset+2; + } + s++; i++; + } + + if (*s && !p) { + /* the only termination allowed */ + if (*s != '=') { + return -1; + } + } + + /* null terminate */ + d[n] = 0; + return n; +} + + +/* + encode as base64 + caller frees +*/ +char *ldb_base64_encode(const char *buf, int len) +{ + const char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + int bit_offset, byte_offset, idx, i; + unsigned char *d = (unsigned char *)buf; + int bytes = (len*8 + 5)/6; + char *out; + + out = malloc(bytes+2); + if (!out) return NULL; + + for (i=0;i> (2-bit_offset)) & 0x3F; + } else { + idx = (d[byte_offset] << (bit_offset-2)) & 0x3F; + if (byte_offset+1 < len) { + idx |= (d[byte_offset+1] >> (8-(bit_offset-2))); + } + } + out[i] = b64[idx]; + } + + out[i++] = '='; + out[i] = 0; + + return out; +} + +/* + see if a buffer should be base64 encoded +*/ +int ldb_should_b64_encode(const struct ldb_val *val) +{ + int i; + unsigned char *p = val->data; + + if (val->length == 0 || p[0] == ' ' || p[0] == ':') { + return 1; + } + + for (i=0; ilength; i++) { + if (!isprint(p[i]) || p[i] == '\n') { + return 1; + } + } + return 0; +} + + +/* + encode as base64 to a file +*/ +static int base64_encode_f(FILE *f, const char *buf, int len, int start_pos) +{ + int i; + char *b = ldb_base64_encode(buf, len); + + if (!b) { + return -1; + } + + for (i=0;b[i];i++) { + fputc(b[i], f); + if (b[i+1] && (i + start_pos) % 77 == 0) { + fputc('\n', f); + fputc(' ', f); + } + } + free(b); + return 0; +} + +/* + write a line folded string onto a file +*/ +static void fold_string(FILE *f, const char *buf, size_t length, int start_pos) +{ + int i; + + for (i=0;i 1 && chunk[chunk_size-1] == '\n') { + chunk_size--; + continue; + } + + /* chunks are terminated by a double line-feed */ + if (c == '\n' && chunk_size > 0 && chunk[chunk_size-1] == '\n') { + chunk[chunk_size-1] = 0; + return chunk; + } + + if (c == '#' && (chunk_size == 0 || chunk[chunk_size-1] == '\n')) { + in_comment = 1; + continue; + } + + chunk[chunk_size++] = c; + } + + return chunk; +} + + +/* simple ldif attribute parser */ +static int next_attr(char **s, char **attr, struct ldb_val *value) +{ + char *p; + int base64_encoded = 0; + + p = strchr(*s, ':'); + if (!p) { + return -1; + } + + *p++ = 0; + + if (*p == ':') { + base64_encoded = 1; + p++; + } + + *attr = *s; + + while (isspace(*p)) { + p++; + } + + value->data = p; + + p = strchr(p, '\n'); + + if (!p) { + value->length = strlen((char *)value->data); + *s = ((char *)value->data) + value->length; + } else { + value->length = p - (char *)value->data; + *s = p+1; + *p = 0; + } + + if (base64_encoded) { + int len = base64_decode(value->data); + if (len == -1) { + /* it wasn't valid base64 data */ + return -1; + } + value->length = len; + } + + return 0; +} + + +/* + free a message from a ldif_read +*/ +void ldif_read_free(struct ldb_message *msg) +{ + if (msg->elements) free(msg->elements); + if (msg->private) free(msg->private); + free(msg); +} + +/* + read from a LDIF file, creating a ldb_message +*/ +struct ldb_message *ldif_read(FILE *f) +{ + struct ldb_message *msg; + char *attr=NULL, *chunk=NULL, *s; + struct ldb_val value; + + value.data = NULL; + + msg = malloc_p(struct ldb_message); + if (!msg) return NULL; + + msg->dn = NULL; + msg->elements = NULL; + msg->num_elements = 0; + msg->private = NULL; + + chunk = next_chunk(f); + if (!chunk) { + goto failed; + } + + msg->private = chunk; + s = chunk; + + if (next_attr(&s, &attr, &value) != 0) { + goto failed; + } + + /* first line must be a dn */ + if (strcmp(attr, "dn") != 0) { + fprintf(stderr, "First line must be a dn not '%s'\n", attr); + goto failed; + } + + msg->dn = value.data; + + while (next_attr(&s, &attr, &value) == 0) { + msg->elements = realloc_p(msg->elements, + struct ldb_message_element, + msg->num_elements+1); + if (!msg->elements) { + goto failed; + } + msg->elements[msg->num_elements].flags = 0; + msg->elements[msg->num_elements].name = attr; + msg->elements[msg->num_elements].value = value; + msg->num_elements++; + } + + return msg; + +failed: + if (msg) ldif_read_free(msg); + return NULL; +} + + +/* + write to a ldif file +*/ +void ldif_write(FILE *f, const struct ldb_message *msg) +{ + int i; + fprintf(f, "dn: %s\n", msg->dn); + for (i=0;inum_elements;i++) { + if (ldb_should_b64_encode(&msg->elements[i].value)) { + fprintf(f, "%s:: ", msg->elements[i].name); + base64_encode_f(f, + msg->elements[i].value.data, + msg->elements[i].value.length, + strlen(msg->elements[i].name)+3); + fprintf(f, "\n"); + } else { + fprintf(f, "%s: ", msg->elements[i].name); + fold_string(f, msg->elements[i].value.data, + msg->elements[i].value.length, + strlen(msg->elements[i].name)+2); + fprintf(f, "\n"); + } + } + fprintf(f,"\n"); +} diff --git a/source4/lib/ldb/ldb_tdb/ldb_match.c b/source4/lib/ldb/ldb_tdb/ldb_match.c new file mode 100644 index 0000000000..89d204f56a --- /dev/null +++ b/source4/lib/ldb/ldb_tdb/ldb_match.c @@ -0,0 +1,176 @@ +/* + ldb database library + + Copyright (C) Andrew Tridgell 2004 + + ** NOTE! The following LGPL license applies to the ldb + ** library. This does NOT imply that all of Samba is released + ** under the LGPL + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* + * Name: ldb + * + * Component: ldb expression matching + * + * Description: ldb expression matching for tdb backend + * + * Author: Andrew Tridgell + */ + +#include "includes.h" + + +/* + see if two ldb_val structures contain the same data + return 1 for a match, 0 for a mis-match +*/ +static int ldb_val_equal(struct ldb_val *v1, struct ldb_val *v2) +{ + if (v1->length != v2->length) return 0; + + if (v1->length == 0) return 1; + + if (memcmp(v1->data, v2->data, v1->length) == 0) { + return 1; + } + + return 0; +} + +/* + check if the scope matches in a search result +*/ +static int scope_match(const char *dn, const char *base, enum ldb_scope scope) +{ + size_t dn_len, base_len; + + if (base == NULL) { + return 1; + } + + base_len = strlen(base); + dn_len = strlen(dn); + + if (strcmp(dn, base) == 0) { + return 1; + } + + if (base_len+1 >= dn_len) { + return 0; + } + + switch (scope) { + case LDB_SCOPE_BASE: + break; + + case LDB_SCOPE_ONELEVEL: + if (strcmp(dn + (dn_len - base_len), base) == 0 && + dn[dn_len - base_len - 1] == ',' && + strchr(dn, ',') == &dn[dn_len - base_len - 1]) { + return 1; + } + break; + + case LDB_SCOPE_SUBTREE: + default: + if (strcmp(dn + (dn_len - base_len), base) == 0 && + dn[dn_len - base_len - 1] == ',') { + return 1; + } + break; + } + + return 0; +} + + +/* + match a leaf node +*/ +static int match_leaf(struct ldb_context *ldb, + struct ldb_message *msg, + struct ldb_parse_tree *tree, + const char *base, + enum ldb_scope scope) +{ + int i; + + if (!scope_match(msg->dn, base, scope)) { + return 0; + } + + if (strcmp(tree->u.simple.attr, "dn") == 0) { + if (strcmp(tree->u.simple.value.data, "*") == 0) { + return 1; + } + return strcmp(msg->dn, tree->u.simple.value.data) == 0; + } + + for (i=0;inum_elements;i++) { + if (strcmp(msg->elements[i].name, tree->u.simple.attr) == 0 && + (strcmp(tree->u.simple.value.data, "*") == 0 || + ldb_val_equal(&msg->elements[i].value, &tree->u.simple.value))) { + return 1; + } + } + + return 0; +} + +/* + return 0 if the given parse tree matches the given message. Assumes + the message is in sorted order + + return 1 if it matches, and 0 if it doesn't match + + this is a recursive function, and does short-circuit evaluation + */ +int ldb_message_match(struct ldb_context *ldb, + struct ldb_message *msg, + struct ldb_parse_tree *tree, + const char *base, + enum ldb_scope scope) +{ + int v, i; + + switch (tree->operation) { + case LDB_OP_SIMPLE: + break; + + case LDB_OP_NOT: + return ! ldb_message_match(ldb, msg, tree->u.not.child, base, scope); + + case LDB_OP_AND: + for (i=0;iu.list.num_elements;i++) { + v = ldb_message_match(ldb, msg, tree->u.list.elements[i], + base, scope); + if (!v) return 0; + } + return 1; + + case LDB_OP_OR: + for (i=0;iu.list.num_elements;i++) { + v = ldb_message_match(ldb, msg, tree->u.list.elements[i], + base, scope); + if (v) return 1; + } + return 0; + } + + return match_leaf(ldb, msg, tree, base, scope); +} diff --git a/source4/lib/ldb/ldb_tdb/ldb_pack.c b/source4/lib/ldb/ldb_tdb/ldb_pack.c new file mode 100644 index 0000000000..b0c825e8e2 --- /dev/null +++ b/source4/lib/ldb/ldb_tdb/ldb_pack.c @@ -0,0 +1,174 @@ +/* + ldb database library + + Copyright (C) Andrew Tridgell 2004 + + ** NOTE! The following LGPL license applies to the ldb + ** library. This does NOT imply that all of Samba is released + ** under the LGPL + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* + * Name: ldb + * + * Component: ldb pack/unpack + * + * Description: pack/unpack routines for ldb messages as key/value blobs + * + * Author: Andrew Tridgell + */ + +#include "includes.h" +#include "ldb_tdb/ldb_tdb.h" + +/* change this if the data format ever changes */ +#define LTDB_PACKING_FORMAT 0x26011966 + +/* + pack a ldb message into a linear buffer in a TDB_DATA + + caller frees the data buffer after use +*/ +int ltdb_pack_data(struct ldb_context *ctx, + const struct ldb_message *message, + struct TDB_DATA *data) +{ + int i; + size_t size; + char *p; + + /* work out how big it needs to be */ + size = 8; + + for (i=0;inum_elements;i++) { + size += 1 + strlen(message->elements[i].name); + size += 4 + message->elements[i].value.length + 1; + } + + /* allocate it */ + data->dptr = malloc(size); + if (!data->dptr) { + errno = ENOMEM; + return -1; + } + data->dsize = size; + + p = data->dptr; + SIVAL(p, 0, LTDB_PACKING_FORMAT); + SIVAL(p, 4, message->num_elements); + p += 8; + + for (i=0;inum_elements;i++) { + size_t len = strlen(message->elements[i].name); + memcpy(p, message->elements[i].name, len+1); + p += len + 1; + SIVAL(p, 0, message->elements[i].value.length); + memcpy(p+4, message->elements[i].value.data, + message->elements[i].value.length); + p[4+message->elements[i].value.length] = 0; + p += 4 + message->elements[i].value.length + 1; + } + + return 0; +} + + +/* + unpack a ldb message from a linear buffer in TDB_DATA + + note that this does not fill in the class and key elements + + caller frees. Memory for the elements[] array is malloced, + but the memory for the elements is re-used from the TDB_DATA + data. This means the caller only has to free the elements array +*/ +int ltdb_unpack_data(struct ldb_context *ctx, + const struct TDB_DATA *data, + struct ldb_message *message) +{ + char *p; + unsigned int remaining; + int i; + + message->elements = NULL; + + p = data->dptr; + if (data->dsize < 4) { + errno = EIO; + goto failed; + } + + if (IVAL(p, 0) != LTDB_PACKING_FORMAT) { + /* this is where we will cope with upgrading the + format if/when the format is ever changed */ + errno = EIO; + goto failed; + } + + message->num_elements = IVAL(p, 4); + p += 8; + + if (message->num_elements == 0) { + message->elements = NULL; + return 0; + } + + /* basic sanity check */ + remaining = data->dsize - 8; + + if (message->num_elements > remaining / 6) { + errno = EIO; + goto failed; + } + + message->elements = malloc_array_p(struct ldb_message_element, + message->num_elements); + + if (!message->elements) { + errno = ENOMEM; + goto failed; + } + + for (i=0;inum_elements;i++) { + size_t len; + if (remaining < 6) { + errno = EIO; + goto failed; + } + len = strnlen(p, remaining-6); + message->elements[i].name = p; + remaining -= len + 1; + p += len + 1; + len = IVAL(p, 0); + if (len > remaining-5) { + errno = EIO; + goto failed; + } + message->elements[i].value.length = len; + message->elements[i].value.data = p+4; + remaining -= len+4+1; + p += len+4+1; + } + + return 0; + +failed: + if (message->elements) { + free(message->elements); + } + return -1; +} diff --git a/source4/lib/ldb/ldb_tdb/ldb_parse.c b/source4/lib/ldb/ldb_tdb/ldb_parse.c new file mode 100644 index 0000000000..44cfc5b4ff --- /dev/null +++ b/source4/lib/ldb/ldb_tdb/ldb_parse.c @@ -0,0 +1,448 @@ + /* + Unix SMB/CIFS implementation. + + parse a LDAP-like expression + + Copyright (C) Andrew Tridgell 2004 + + 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. +*/ + +/* + TODO: + - add RFC2254 binary string handling + - possibly add ~=, <= and >= handling + - expand the test suite + - add better parse error handling + +*/ + +#include "includes.h" + + +/* +a filter is defined by: + ::= '(' ')' + ::= | | | + ::= '&' + ::= '|' + ::= '!' + ::= | + ::= + ::= '=' | '~=' | '<=' | '>=' +*/ + +/* + return next token element. Caller frees +*/ +static char *ldb_parse_lex(const char **s) +{ + const char *p = *s; + char *ret; + + while (isspace(*p)) { + p++; + } + *s = p; + + if (*p == 0) { + return NULL; + } + + if (strchr("()&|=!", *p)) { + (*s) = p+1; + ret = strndup(p, 1); + if (!ret) { + errno = ENOMEM; + } + return ret; + } + + while (*p && (isalnum(*p) || !strchr("()&|=!", *p))) { + p++; + } + + if (p == *s) { + return NULL; + } + + ret = strndup(*s, p - *s); + if (!ret) { + errno = ENOMEM; + } + + *s = p; + + return ret; +} + +/* + find a matching close brace in a string +*/ +static const char *match_brace(const char *s) +{ + unsigned int count = 0; + while (*s && (count != 0 || *s != ')')) { + if (*s == '(') { + count++; + } + if (*s == ')') { + count--; + } + s++; + } + if (! *s) { + return NULL; + } + return s; +} + + +static struct ldb_parse_tree *ldb_parse_filter(const char **s); + +/* + ::= +*/ +static struct ldb_parse_tree *ldb_parse_simple(const char *s) +{ + char *eq, *val, *l; + struct ldb_parse_tree *ret; + + l = ldb_parse_lex(&s); + if (!l) { + fprintf(stderr, "Unexpected end of expression\n"); + return NULL; + } + + if (strchr("()&|=", *l)) { + fprintf(stderr, "Unexpected token '%s'\n", l); + free(l); + return NULL; + } + + eq = ldb_parse_lex(&s); + if (!eq || strcmp(eq, "=") != 0) { + fprintf(stderr, "Expected '='\n"); + free(l); + if (eq) free(eq); + return NULL; + } + free(eq); + + val = ldb_parse_lex(&s); + if (val && strchr("()&|=", *val)) { + fprintf(stderr, "Unexpected token '%s'\n", val); + free(l); + if (val) free(val); + return NULL; + } + + ret = malloc_p(struct ldb_parse_tree); + if (!ret) { + errno = ENOMEM; + return NULL; + } + + ret->operation = LDB_OP_SIMPLE; + ret->u.simple.attr = l; + ret->u.simple.value.data = val; + ret->u.simple.value.length = val?strlen(val):0; + + return ret; +} + + +/* + parse a filterlist + ::= '&' + ::= '|' + ::= | +*/ +static struct ldb_parse_tree *ldb_parse_filterlist(enum ldb_parse_op op, const char *s) +{ + struct ldb_parse_tree *ret, *next; + + ret = malloc_p(struct ldb_parse_tree); + if (!ret) { + errno = ENOMEM; + return NULL; + } + + ret->operation = op; + ret->u.list.num_elements = 1; + ret->u.list.elements = malloc_p(struct ldb_parse_tree *); + if (!ret->u.list.elements) { + errno = ENOMEM; + free(ret); + return NULL; + } + + ret->u.list.elements[0] = ldb_parse_filter(&s); + if (!ret->u.list.elements[0]) { + free(ret->u.list.elements); + free(ret); + return NULL; + } + + while (isspace(*s)) s++; + + while (*s && (next = ldb_parse_filter(&s))) { + struct ldb_parse_tree **e; + e = realloc_p(ret->u.list.elements, + struct ldb_parse_tree *, + ret->u.list.num_elements+1); + if (!e) { + errno = ENOMEM; + ldb_parse_tree_free(next); + ldb_parse_tree_free(ret); + return NULL; + } + ret->u.list.elements = e; + ret->u.list.elements[ret->u.list.num_elements] = next; + ret->u.list.num_elements++; + while (isspace(*s)) s++; + } + + return ret; +} + + +/* + ::= '!' +*/ +static struct ldb_parse_tree *ldb_parse_not(const char *s) +{ + struct ldb_parse_tree *ret; + + ret = malloc_p(struct ldb_parse_tree); + if (!ret) { + errno = ENOMEM; + return NULL; + } + + ret->operation = LDB_OP_NOT; + ret->u.not.child = ldb_parse_filter(&s); + if (!ret->u.not.child) { + free(ret); + return NULL; + } + + return ret; +} + +/* + parse a filtercomp + ::= | | | +*/ +static struct ldb_parse_tree *ldb_parse_filtercomp(const char *s) +{ + while (isspace(*s)) s++; + + switch (*s) { + case '&': + return ldb_parse_filterlist(LDB_OP_AND, s+1); + + case '|': + return ldb_parse_filterlist(LDB_OP_OR, s+1); + + case '!': + return ldb_parse_not(s+1); + + case '(': + case ')': + fprintf(stderr, "Unexpected token '%c'\n", *s); + return NULL; + } + + return ldb_parse_simple(s); +} + + +/* + ::= '(' ')' +*/ +static struct ldb_parse_tree *ldb_parse_filter(const char **s) +{ + char *l, *s2; + const char *p, *p2; + struct ldb_parse_tree *ret; + + l = ldb_parse_lex(s); + if (!l) { + fprintf(stderr, "Unexpected end of expression\n"); + return NULL; + } + + if (strcmp(l, "(") != 0) { + free(l); + fprintf(stderr, "Expected '('\n"); + return NULL; + } + free(l); + + p = match_brace(*s); + if (!p) { + fprintf(stderr, "Parse error - mismatched braces\n"); + return NULL; + } + p2 = p + 1; + + s2 = strndup(*s, p - *s); + if (!s2) { + errno = ENOMEM; + return NULL; + } + + ret = ldb_parse_filtercomp(s2); + free(s2); + + *s = p2; + + return ret; +} + + +/* + main parser entry point. Takes a search string and returns a parse tree + + expression ::= | +*/ +struct ldb_parse_tree *ldb_parse_tree(const char *s) +{ + while (isspace(*s)) s++; + + if (*s == '(') { + return ldb_parse_filter(&s); + } + + return ldb_parse_simple(s); +} + +/* + free a parse tree returned from ldb_parse_tree() +*/ +void ldb_parse_tree_free(struct ldb_parse_tree *tree) +{ + int i; + + switch (tree->operation) { + case LDB_OP_SIMPLE: + free(tree->u.simple.attr); + if (tree->u.simple.value.data) free(tree->u.simple.value.data); + break; + + case LDB_OP_AND: + case LDB_OP_OR: + for (i=0;iu.list.num_elements;i++) { + ldb_parse_tree_free(tree->u.list.elements[i]); + } + if (tree->u.list.elements) free(tree->u.list.elements); + break; + + case LDB_OP_NOT: + ldb_parse_tree_free(tree->u.not.child); + break; + } + + free(tree); +} + +#if TEST_PROGRAM +/* + return a string representation of a parse tree + used for debugging +*/ +static char *tree_string(struct ldb_parse_tree *tree) +{ + char *s = NULL; + char *s1, *s2; + int i; + + switch (tree->operation) { + case LDB_OP_SIMPLE: + asprintf(&s, "( %s = \"%s\" )", tree->u.simple.attr, + (char *)tree->u.simple.value.data); + break; + + case LDB_OP_AND: + case LDB_OP_OR: + asprintf(&s, "( %c", tree->operation==LDB_OP_AND?'&':'|'); + if (!s) return NULL; + + for (i=0;iu.list.num_elements;i++) { + s1 = tree_string(tree->u.list.elements[i]); + if (!s1) { + free(s); + return NULL; + } + asprintf(&s2, "%s %s", s, s1); + free(s); + free(s1); + s = s2; + } + if (!s) { + return NULL; + } + asprintf(&s2, "%s )", s); + free(s); + s = s2; + break; + + case LDB_OP_NOT: + s1 = tree_string(tree->u.not.child); + asprintf(&s, "( ! %s )", s1); + free(s1); + break; + } + return s; +} + + +/* + print a tree + */ +static void print_tree(struct ldb_parse_tree *tree) +{ + char *s = tree_string(tree); + printf("%s\n", s); + free(s); +} + + + int main(void) +{ + char line[1000]; + int ret = 0; + + while (fgets(line, sizeof(line)-1, stdin)) { + struct ldb_parse_tree *tree; + + if (line[strlen(line)-1] == '\n') { + line[strlen(line)-1] = 0; + } + tree = ldb_parse_tree(line); + if (!tree) { + fprintf(stderr, "Failed to parse\n"); + ret = 1; + continue; + } + print_tree(tree); + ldb_parse_tree_free(tree); + } + + return ret; +} +#endif /* TEST_PROGRAM */ + diff --git a/source4/lib/ldb/ldb_tdb/ldb_parse.h b/source4/lib/ldb/ldb_tdb/ldb_parse.h new file mode 100644 index 0000000000..995e0e04ff --- /dev/null +++ b/source4/lib/ldb/ldb_tdb/ldb_parse.h @@ -0,0 +1,40 @@ + /* + Unix SMB/CIFS implementation. + + parse a LDAP-like expression - header + + Copyright (C) Andrew Tridgell 2004 + + 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. +*/ + +enum ldb_parse_op {LDB_OP_SIMPLE, LDB_OP_AND, LDB_OP_OR, LDB_OP_NOT}; + +struct ldb_parse_tree { + enum ldb_parse_op operation; + union { + struct { + char *attr; + struct ldb_val value; + } simple; + struct { + unsigned int num_elements; + struct ldb_parse_tree **elements; + } list; + struct { + struct ldb_parse_tree *child; + } not; + } u; +}; diff --git a/source4/lib/ldb/ldb_tdb/ldb_search.c b/source4/lib/ldb/ldb_tdb/ldb_search.c new file mode 100644 index 0000000000..952053c920 --- /dev/null +++ b/source4/lib/ldb/ldb_tdb/ldb_search.c @@ -0,0 +1,482 @@ +/* + ldb database library + + Copyright (C) Andrew Tridgell 2004 + + ** NOTE! The following LGPL license applies to the ldb + ** library. This does NOT imply that all of Samba is released + ** under the LGPL + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* + * Name: ldb + * + * Component: ldb search functions + * + * Description: functions to search ldb+tdb databases + * + * Author: Andrew Tridgell + */ + +#include "includes.h" +#include "ldb_tdb/ldb_tdb.h" + +/* + free a message that has all parts separately allocated +*/ +static void msg_free_all_parts(struct ldb_message *msg) +{ + int i; + if (msg->dn) free(msg->dn); + for (i=0;inum_elements;i++) { + if (msg->elements[i].name) free(msg->elements[i].name); + if (msg->elements[i].value.data) free(msg->elements[i].value.data); + } + free(msg->elements); + free(msg); +} + + +/* + TODO: this should take advantage of the sorted nature of the message + return index of the attribute, or -1 if not found +*/ +int ldb_msg_find_attr(const struct ldb_message *msg, const char *attr) +{ + int i; + for (i=0;inum_elements;i++) { + if (strcmp(msg->elements[i].name, attr) == 0) { + return i; + } + } + return -1; +} + +/* + duplicate a ldb_val structure +*/ +static struct ldb_val ldb_val_dup(const struct ldb_val *v) +{ + struct ldb_val v2; + v2.length = v->length; + if (v->length == 0) { + v2.data = NULL; + return v2; + } + + /* the +1 is to cope with buggy C library routines like strndup + that look one byte beyond */ + v2.data = malloc(v->length+1); + if (!v2.data) { + v2.length = 0; + return v2; + } + + memcpy(v2.data, v->data, v->length); + ((char *)v2.data)[v->length] = 0; + return v2; +} + + + +/* + add one element to a message +*/ +static int msg_add_element(struct ldb_message *ret, const struct ldb_message_element *el) +{ + struct ldb_message_element *e2; + + e2 = realloc_p(ret->elements, struct ldb_message_element, ret->num_elements+1); + if (!e2) { + return -1; + } + ret->elements = e2; + + e2[ret->num_elements].name = strdup(el->name); + if (!e2[ret->num_elements].name) { + return -1; + } + e2[ret->num_elements].value = ldb_val_dup(&el->value); + if (e2[ret->num_elements].value.length != el->value.length) { + return -1; + } + + ret->num_elements++; + + return 0; +} + +/* + add all elements from one message into another + */ +static int msg_add_all_elements(struct ldb_message *ret, + const struct ldb_message *msg) +{ + int i; + for (i=0;inum_elements;i++) { + if (msg_add_element(ret, &msg->elements[i]) != 0) { + return -1; + } + } + + return 0; +} + + +/* + pull the specified list of attributes from a message + */ +static struct ldb_message *ltdb_pull_attrs(struct ldb_context *ldb, + const struct ldb_message *msg, + const char **attrs) +{ + struct ldb_message *ret; + int i; + + ret = malloc_p(struct ldb_message); + if (!ret) { + return NULL; + } + + ret->dn = strdup(msg->dn); + if (!ret->dn) { + free(ret); + return NULL; + } + + ret->num_elements = 0; + ret->elements = NULL; + ret->private = NULL; + + if (!attrs) { + if (msg_add_all_elements(ret, msg) != 0) { + msg_free_all_parts(ret); + return NULL; + } + return ret; + } + + for (i=0;attrs[i];i++) { + int j; + + if (strcmp(attrs[i], "*") == 0) { + if (msg_add_all_elements(ret, msg) != 0) { + msg_free_all_parts(ret); + return NULL; + } + continue; + } + j = ldb_msg_find_attr(msg, attrs[i]); + if (j == -1) { + continue; + } + do { + if (msg_add_element(ret, &msg->elements[j]) != 0) { + msg_free_all_parts(ret); + return NULL; + } + } while (++j < msg->num_elements && + strcmp(attrs[i], msg->elements[j].name) == 0); + } + + return ret; +} + + + +/* + see if a ldb_val is a wildcard +*/ +int ltdb_has_wildcard(const struct ldb_val *val) +{ + if (val->length == 1 && ((char *)val->data)[0] == '*') { + return 1; + } + return 0; +} + + +/* + free the results of a ltdb_search_dn1 search +*/ +void ltdb_search_dn1_free(struct ldb_context *ldb, struct ldb_message *msg) +{ + free(msg->dn); + free(msg->private); + if (msg->elements) free(msg->elements); +} + + +/* + search the database for a single simple dn, returning all attributes + in a single message + + return 1 on success, 0 on record-not-found and -1 on error +*/ +int ltdb_search_dn1(struct ldb_context *ldb, const char *dn, struct ldb_message *msg) +{ + struct ltdb_private *ltdb = ldb->private; + int ret; + TDB_DATA tdb_key, tdb_data; + + /* form the key */ + tdb_key = ltdb_key(dn); + if (!tdb_key.dptr) { + return -1; + } + + tdb_data = tdb_fetch(ltdb->tdb, tdb_key); + free(tdb_key.dptr); + if (!tdb_data.dptr) { + return 0; + } + + msg->dn = strdup(dn); + if (!msg->dn) { + free(tdb_data.dptr); + return -1; + } + msg->private = tdb_data.dptr; + msg->num_elements = 0; + msg->elements = NULL; + + ret = ltdb_unpack_data(ldb, &tdb_data, msg); + if (ret == -1) { + free(tdb_data.dptr); + return -1; + } + + return 1; +} + + +/* + search the database for a single simple dn +*/ +int ltdb_search_dn(struct ldb_context *ldb, char *dn, + const char *attrs[], struct ldb_message ***res) +{ + int ret; + struct ldb_message msg, *msg2; + + ret = ltdb_search_dn1(ldb, dn, &msg); + if (ret != 1) { + return ret; + } + + msg2 = ltdb_pull_attrs(ldb, &msg, attrs); + + ltdb_search_dn1_free(ldb, &msg); + + if (!msg2) { + return -1; + } + + *res = malloc_array_p(struct ldb_message *, 2); + if (! *res) { + msg_free_all_parts(msg2); + return -1; + } + + (*res)[0] = msg2; + (*res)[1] = NULL; + + return 1; +} + + +/* + add a set of attributes from a record to a set of results + return 0 on success, -1 on failure +*/ +int ltdb_add_attr_results(struct ldb_context *ldb, struct ldb_message *msg, + const char *attrs[], + unsigned int *count, + struct ldb_message ***res) +{ + struct ldb_message *msg2; + struct ldb_message **res2; + + /* pull the attributes that the user wants */ + msg2 = ltdb_pull_attrs(ldb, msg, attrs); + if (!msg2) { + return -1; + } + + /* add to the results list */ + res2 = realloc_p(*res, struct ldb_message *, (*count)+2); + if (!res2) { + msg_free_all_parts(msg2); + return -1; + } + + (*res) = res2; + + (*res)[*count] = msg2; + (*res)[(*count)+1] = NULL; + (*count)++; + + return 0; +} + + +/* + internal search state during a full db search +*/ +struct ltdb_search_info { + struct ldb_context *ldb; + struct ldb_parse_tree *tree; + const char *base; + enum ldb_scope scope; + const char **attrs; + struct ldb_message **msgs; + int failures; + int count; +}; + + +/* + search function for a non-indexed search + */ +static int search_func(struct tdb_context *tdb, TDB_DATA key, TDB_DATA data, void *state) +{ + struct ltdb_search_info *sinfo = state; + struct ldb_message msg; + int ret; + + if (key.dsize < 4 || + strncmp(key.dptr, "DN=", 3) != 0) { + return 0; + } + + msg.dn = key.dptr + 3; + + /* unpack the record */ + ret = ltdb_unpack_data(sinfo->ldb, &data, &msg); + if (ret == -1) { + sinfo->failures++; + return 0; + } + + /* see if it matches the given expression */ + if (!ldb_message_match(sinfo->ldb, &msg, sinfo->tree, + sinfo->base, sinfo->scope)) { + if (msg.elements) free(msg.elements); + return 0; + } + + ret = ltdb_add_attr_results(sinfo->ldb, &msg, sinfo->attrs, &sinfo->count, &sinfo->msgs); + + if (ret == -1) { + sinfo->failures++; + } + + if (msg.elements) free(msg.elements); + + return ret; +} + + +/* + free a set of search results +*/ +int ltdb_search_free(struct ldb_context *ldb, struct ldb_message **msgs) +{ + int i; + + if (!msgs) return 0; + + for (i=0;msgs[i];i++) { + msg_free_all_parts(msgs[i]); + } + + free(msgs); + + return 0; +} + +/* + search the database with a LDAP-like expression. + this is the "full search" non-indexed varient +*/ +static int ltdb_search_full(struct ldb_context *ldb, + const char *base, + enum ldb_scope scope, + struct ldb_parse_tree *tree, + const char *attrs[], struct ldb_message ***res) +{ + struct ltdb_private *ltdb = ldb->private; + int ret; + struct ltdb_search_info sinfo; + + sinfo.tree = tree; + sinfo.ldb = ldb; + sinfo.scope = scope; + sinfo.base = base; + sinfo.attrs = attrs; + sinfo.msgs = NULL; + sinfo.count = 0; + sinfo.failures = 0; + + ret = tdb_traverse(ltdb->tdb, search_func, &sinfo); + + if (ret == -1) { + ltdb_search_free(ldb, sinfo.msgs); + return -1; + } + + *res = sinfo.msgs; + return sinfo.count; +} + + +/* + search the database with a LDAP-like expression. + choses a search method +*/ +int ltdb_search(struct ldb_context *ldb, const char *base, + enum ldb_scope scope, const char *expression, + const char *attrs[], struct ldb_message ***res) +{ + struct ldb_parse_tree *tree; + int ret; + + *res = NULL; + + /* form a parse tree for the expression */ + tree = ldb_parse_tree(expression); + if (!tree) { + return -1; + } + + if (tree->operation == LDB_OP_SIMPLE && + strcmp(tree->u.simple.attr, "dn") == 0 && + !ltdb_has_wildcard(&tree->u.simple.value)) { + /* yay! its a nice simple one */ + ret = ltdb_search_dn(ldb, tree->u.simple.value.data, attrs, res); + } else { + ret = ltdb_search_indexed(ldb, base, scope, tree, attrs, res); + if (ret == -1) { + ret = ltdb_search_full(ldb, base, scope, tree, attrs, res); + } + } + + ldb_parse_tree_free(tree); + + return ret; +} + diff --git a/source4/lib/ldb/ldb_tdb/ldb_tdb.c b/source4/lib/ldb/ldb_tdb/ldb_tdb.c new file mode 100644 index 0000000000..17931352f7 --- /dev/null +++ b/source4/lib/ldb/ldb_tdb/ldb_tdb.c @@ -0,0 +1,303 @@ +/* + ldb database library + + Copyright (C) Andrew Tridgell 2004 + + ** NOTE! The following LGPL license applies to the ldb + ** library. This does NOT imply that all of Samba is released + ** under the LGPL + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* + * Name: ldb + * + * Component: ldb tdb backend + * + * Description: core functions for tdb backend + * + * Author: Andrew Tridgell + */ + +#include "includes.h" +#include "ldb_tdb/ldb_tdb.h" + +/* + form a TDB_DATA for a record key + caller frees +*/ +struct TDB_DATA ltdb_key(const char *dn) +{ + TDB_DATA key; + char *key_str = NULL; + + asprintf(&key_str, "DN=%s", dn); + if (!key_str) { + errno = ENOMEM; + key.dptr = NULL; + key.dsize = 0; + return key; + } + + key.dptr = key_str; + key.dsize = strlen(key_str)+1; + + return key; +} + + +/* + store a record into the db +*/ +int ltdb_store(struct ldb_context *ldb, const struct ldb_message *msg, int flgs) +{ + struct ltdb_private *ltdb = ldb->private; + TDB_DATA tdb_key, tdb_data; + int ret; + + tdb_key = ltdb_key(msg->dn); + if (!tdb_key.dptr) { + return -1; + } + + ret = ltdb_pack_data(ldb, msg, &tdb_data); + if (ret == -1) { + free(tdb_key.dptr); + return -1; + } + + ret = tdb_store(ltdb->tdb, tdb_key, tdb_data, flgs); + if (ret == -1) { + goto done; + } + + ret = ltdb_index_add(ldb, msg); + if (ret == -1) { + tdb_delete(ltdb->tdb, tdb_key); + } + +done: + free(tdb_key.dptr); + free(tdb_data.dptr); + + return ret; +} + + +/* + add a record to the database +*/ +static int ltdb_add(struct ldb_context *ldb, const struct ldb_message *msg) +{ + return ltdb_store(ldb, msg, TDB_INSERT); +} + + +/* + delete a record from the database, not updating indexes (used for deleting + index records) +*/ +int ltdb_delete_noindex(struct ldb_context *ldb, const char *dn) +{ + struct ltdb_private *ltdb = ldb->private; + TDB_DATA tdb_key; + int ret; + + tdb_key = ltdb_key(dn); + if (!tdb_key.dptr) { + return -1; + } + + ret = tdb_delete(ltdb->tdb, tdb_key); + free(tdb_key.dptr); + + return ret; +} + +/* + delete a record from the database +*/ +static int ltdb_delete(struct ldb_context *ldb, const char *dn) +{ + int ret; + struct ldb_message msg; + + /* in case any attribute of the message was indexed, we need + to fetch the old record */ + ret = ltdb_search_dn1(ldb, dn, &msg); + if (ret != 1) { + /* not finding the old record is an error */ + return -1; + } + + ret = ltdb_delete_noindex(ldb, dn); + if (ret == -1) { + ltdb_search_dn1_free(ldb, &msg); + return -1; + } + + /* remove any indexed attributes */ + ret = ltdb_index_del(ldb, &msg); + + ltdb_search_dn1_free(ldb, &msg); + + return ret; +} + + +/* + modify a record +*/ +static int ltdb_modify(struct ldb_context *ldb, const struct ldb_message *msg) +{ + struct ltdb_private *ltdb = ldb->private; + TDB_DATA tdb_key, tdb_data; + struct ldb_message msg2; + int ret; + + tdb_key = ltdb_key(msg->dn); + if (!tdb_key.dptr) { + return -1; + } + + tdb_data = tdb_fetch(ltdb->tdb, tdb_key); + if (!tdb_data.dptr) { + free(tdb_key.dptr); + return -1; + } + + ret = ltdb_unpack_data(ldb, &tdb_data, &msg2); + if (ret == -1) { + free(tdb_key.dptr); + free(tdb_data.dptr); + return -1; + } + +#if 0 + for (i=0;inum_elements;i++) { + switch (msg->elements[i].flags & LDB_FLAG_MOD_MASK) { + case LDB_FLAG_MOD_ADD: + ret = find_element(&msg2, msg->elements[i].name); + if (ret != -1) { + errno = EEXIST; + goto failed; + } + + } + } + +failed: +#endif + + free(tdb_key.dptr); + free(tdb_data.dptr); + if (msg2.elements) free(msg2.elements); + + return -1; +} + +/* + close database +*/ +static int ltdb_close(struct ldb_context *ldb) +{ + struct ltdb_private *ltdb = ldb->private; + int ret; + ret = tdb_close(ltdb->tdb); + free(ltdb); + free(ldb); + return ret; +} + + +/* + return extended error information +*/ +static const char *ltdb_errstring(struct ldb_context *ldb) +{ + struct ltdb_private *ltdb = ldb->private; + return tdb_errorstr(ltdb->tdb); +} + + +static const struct ldb_backend_ops ltdb_ops = { + ltdb_close, + ltdb_search, + ltdb_search_free, + ltdb_add, + ltdb_modify, + ltdb_delete, + ltdb_errstring +}; + + +/* + connect to the database +*/ +struct ldb_context *ltdb_connect(const char *url, + unsigned int flags, + const char *options[]) +{ + const char *path; + int tdb_flags, open_flags; + struct ltdb_private *ltdb; + TDB_CONTEXT *tdb; + struct ldb_context *ldb; + + /* parse the url */ + if (strncmp(url, "tdb://", 6) != 0) { + errno = EINVAL; + return NULL; + } + + path = url+6; + + tdb_flags = TDB_DEFAULT; + + if (flags & LDB_FLG_RDONLY) { + open_flags = O_RDONLY; + } else { + open_flags = O_CREAT | O_RDWR; + } + + tdb = tdb_open(path, 0, tdb_flags, open_flags, 0666); + if (!tdb) { + return NULL; + } + + ltdb = malloc_p(struct ltdb_private); + if (!ltdb) { + tdb_close(tdb); + errno = ENOMEM; + return NULL; + } + + ltdb->tdb = tdb; + + + ldb = malloc_p(struct ldb_context); + if (!ldb) { + tdb_close(tdb); + free(ltdb); + errno = ENOMEM; + return NULL; + } + + ldb->private = ltdb; + ldb->ops = <db_ops; + + return ldb; +} diff --git a/source4/lib/ldb/ldb_tdb/ldb_tdb.h b/source4/lib/ldb/ldb_tdb/ldb_tdb.h new file mode 100644 index 0000000000..edf525f895 --- /dev/null +++ b/source4/lib/ldb/ldb_tdb/ldb_tdb.h @@ -0,0 +1,11 @@ +/* this private structure is used by the ltdb backend in the + ldb_context */ +struct ltdb_private { + TDB_CONTEXT *tdb; + unsigned int connect_flags; +}; + + +#define IVAL(p, ofs) (((unsigned *)((char *)(p) + (ofs)))[0]) +#define SIVAL(p, ofs, v) do { IVAL(p, ofs) = (v); } while (0) + diff --git a/source4/lib/ldb/ldb_tdb/ldbadd.c b/source4/lib/ldb/ldb_tdb/ldbadd.c new file mode 100644 index 0000000000..3959a17525 --- /dev/null +++ b/source4/lib/ldb/ldb_tdb/ldbadd.c @@ -0,0 +1,55 @@ + /* + Unix SMB/CIFS implementation. + + a utility to add elements to a ldb + + Copyright (C) Andrew Tridgell 2004 + + 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" + + int main(void) +{ + static struct ldb_context *ldb; + struct ldb_message *msg; + int ret; + int count=0, failures=0; + + ldb = ltdb_connect("tdb://test.ldb", 0, NULL); + + if (!ldb) { + perror("ldb_connect"); + exit(1); + } + + while ((msg = ldif_read(stdin))) { + ret = ldb->ops->add(ldb, msg); + if (ret != 0) { + fprintf(stderr, "Failed to add record '%s'\n", msg->dn); + failures++; + } else { + count++; + } + ldif_read_free(msg); + } + + ldb->ops->close(ldb); + + printf("Added %d records with %d failures\n", count, failures); + + return 0; +} diff --git a/source4/lib/ldb/ldb_tdb/ldbdel.c b/source4/lib/ldb/ldb_tdb/ldbdel.c new file mode 100644 index 0000000000..8f8a03913f --- /dev/null +++ b/source4/lib/ldb/ldb_tdb/ldbdel.c @@ -0,0 +1,50 @@ + /* + Unix SMB/CIFS implementation. + + a utility to delete elements in a ldb + + Copyright (C) Andrew Tridgell 2004 + + 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" + + int main(int argc, const char *argv[]) +{ + static struct ldb_context *ldb; + int ret, i; + + if (argc < 2) { + printf("Usage: ldbdel \n"); + exit(1); + } + + ldb = ltdb_connect("tdb://test.ldb", 0, NULL); + if (!ldb) { + perror("ldb_connect"); + exit(1); + } + + for (i=1;iops->delete(ldb, argv[i]); + if (ret != 0) { + printf("delete of '%s' failed\n", argv[i]); + } + } + + ldb->ops->close(ldb); + return 0; +} diff --git a/source4/lib/ldb/ldb_tdb/ldbsearch.c b/source4/lib/ldb/ldb_tdb/ldbsearch.c new file mode 100644 index 0000000000..a6d63e78d5 --- /dev/null +++ b/source4/lib/ldb/ldb_tdb/ldbsearch.c @@ -0,0 +1,73 @@ + /* + Unix SMB/CIFS implementation. + + simple ldb search tool + + Copyright (C) Andrew Tridgell 2004 + + 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" + + int main(int argc, const char *argv[]) +{ + static struct ldb_context *ldb; + struct ldb_message **msgs; + int ret, i; + const char *expression; + const char **attrs = NULL; + + if (argc < 2) { + printf("Usage: ldbsearch [attrs...]\n"); + exit(1); + } + + if (argc > 2) { + attrs = argv+2; + } + + expression = argv[1]; + + ldb = ltdb_connect("tdb://test.ldb", 0, NULL); + + if (!ldb) { + perror("ldb_connect"); + exit(1); + } + + ret = ldb->ops->search(ldb, expression, attrs, &msgs); + + if (ret == -1) { + printf("search failed\n"); + exit(1); + } + + printf("# returned %d records\n", ret); + + for (i=0;iops->search_free(ldb, msgs); + if (ret == -1) { + fprintf(stderr, "search_free failed\n"); + exit(1); + } + + ldb->ops->close(ldb); + return 0; +} -- cgit