summaryrefslogtreecommitdiff
path: root/source4/lib/ldb/ldb_tdb
diff options
context:
space:
mode:
authorAndrew Tridgell <tridge@samba.org>2004-05-01 09:45:56 +0000
committerGerald (Jerry) Carter <jerry@samba.org>2007-10-10 12:51:38 -0500
commit0dad5a34273bf5cadcfd4a36d69bdffbf69eb073 (patch)
tree78ddbe4e053287a0e7a0300e945627ce4b24bc29 /source4/lib/ldb/ldb_tdb
parenta2b6d47390a07f0d2a737d17144f825a9733d5bf (diff)
downloadsamba-0dad5a34273bf5cadcfd4a36d69bdffbf69eb073.tar.gz
samba-0dad5a34273bf5cadcfd4a36d69bdffbf69eb073.tar.bz2
samba-0dad5a34273bf5cadcfd4a36d69bdffbf69eb073.zip
r435: a major upgrade for ldb
- added the ability to mark record attributes as being CASE_INSENSITIVE, WILDCARD or INTEGER. - added the ability to support objectclass subclasses, and to search by a parent class - added internal support for case insensitive versus case sensitive indexing (not UTF8 compliant yet) - cleaned up a number of const warnings - added a number of helper functions for fetching integers, strings and doubles - added a in-memory cache for important database properties, supported by a database sequence number - changed some variable names to avoid conflicts with C++ (This used to be commit f2bf06f25c2e6c744817711c7bedbd1d3b52f994)
Diffstat (limited to 'source4/lib/ldb/ldb_tdb')
-rw-r--r--source4/lib/ldb/ldb_tdb/ldb_cache.c229
-rw-r--r--source4/lib/ldb/ldb_tdb/ldb_index.c75
-rw-r--r--source4/lib/ldb/ldb_tdb/ldb_match.c165
-rw-r--r--source4/lib/ldb/ldb_tdb/ldb_pack.c56
-rw-r--r--source4/lib/ldb/ldb_tdb/ldb_search.c56
-rw-r--r--source4/lib/ldb/ldb_tdb/ldb_tdb.c186
-rw-r--r--source4/lib/ldb/ldb_tdb/ldb_tdb.h36
7 files changed, 682 insertions, 121 deletions
diff --git a/source4/lib/ldb/ldb_tdb/ldb_cache.c b/source4/lib/ldb/ldb_tdb/ldb_cache.c
new file mode 100644
index 0000000000..5d61fd35b3
--- /dev/null
+++ b/source4/lib/ldb/ldb_tdb/ldb_cache.c
@@ -0,0 +1,229 @@
+/*
+ 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 cache functions
+ *
+ * Description: cache special records in a ldb/tdb
+ *
+ * Author: Andrew Tridgell
+ */
+
+#include "includes.h"
+#include "ldb/ldb_tdb/ldb_tdb.h"
+
+/*
+ initialise the baseinfo record
+*/
+static int ltdb_baseinfo_init(struct ldb_context *ldb)
+{
+ struct ltdb_private *ltdb = ldb->private_data;
+ struct ldb_message msg;
+ struct ldb_message_element el;
+ struct ldb_val val;
+
+ ltdb->sequence_number = 0;
+
+ msg.num_elements = 1;
+ msg.elements = &el;
+ msg.dn = LTDB_BASEINFO;
+ el.name = LTDB_SEQUENCE_NUMBER;
+ el.values = &val;
+ el.num_values = 1;
+ el.flags = 0;
+ val.data = "0";
+ val.length = 1;
+
+ return ltdb_store(ldb, &msg, TDB_INSERT);
+}
+
+/*
+ free any cache records
+ */
+void ltdb_cache_free(struct ldb_context *ldb)
+{
+ struct ltdb_private *ltdb = ldb->private_data;
+
+ ltdb->sequence_number = 0;
+ ltdb_search_dn1_free(ldb, &ltdb->cache.baseinfo);
+ ltdb_search_dn1_free(ldb, &ltdb->cache.indexlist);
+ ltdb_search_dn1_free(ldb, &ltdb->cache.subclasses);
+ ltdb_search_dn1_free(ldb, &ltdb->cache.attributes);
+
+ if (ltdb->cache.last_attribute.name) free(ltdb->cache.last_attribute.name);
+ memset(&ltdb->cache, 0, sizeof(ltdb->cache));
+}
+
+/*
+ load the cache records
+*/
+int ltdb_cache_load(struct ldb_context *ldb)
+{
+ struct ltdb_private *ltdb = ldb->private_data;
+ double seq;
+
+ ltdb_search_dn1_free(ldb, &ltdb->cache.baseinfo);
+
+ if (ltdb_search_dn1(ldb, LTDB_BASEINFO, &ltdb->cache.baseinfo) == -1) {
+ return -1;
+ }
+
+ /* possibly initialise the baseinfo */
+ if (!ltdb->cache.baseinfo.dn) {
+ if (ltdb_baseinfo_init(ldb) != 0) {
+ return -1;
+ }
+ if (ltdb_search_dn1(ldb, LTDB_BASEINFO, &ltdb->cache.baseinfo) != 1) {
+ return -1;
+ }
+ }
+
+ /* if the current internal sequence number is the same as the one
+ in the database then assume the rest of the cache is OK */
+ seq = ldb_msg_find_double(&ltdb->cache.baseinfo, LTDB_SEQUENCE_NUMBER, 0);
+ if (seq == ltdb->sequence_number) {
+ return 0;
+ }
+ ltdb->sequence_number = seq;
+
+ if (ltdb->cache.last_attribute.name) free(ltdb->cache.last_attribute.name);
+ memset(&ltdb->cache.last_attribute, 0, sizeof(ltdb->cache.last_attribute));
+
+ ltdb_search_dn1_free(ldb, &ltdb->cache.indexlist);
+ ltdb_search_dn1_free(ldb, &ltdb->cache.subclasses);
+ ltdb_search_dn1_free(ldb, &ltdb->cache.attributes);
+
+ if (ltdb_search_dn1(ldb, LTDB_INDEXLIST, &ltdb->cache.indexlist) == -1) {
+ return -1;
+ }
+ if (ltdb_search_dn1(ldb, LTDB_SUBCLASSES, &ltdb->cache.subclasses) == -1) {
+ return -1;
+ }
+ if (ltdb_search_dn1(ldb, LTDB_ATTRIBUTES, &ltdb->cache.attributes) == -1) {
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/*
+ increase the sequence number to indicate a database change
+*/
+int ltdb_increase_sequence_number(struct ldb_context *ldb)
+{
+ struct ltdb_private *ltdb = ldb->private_data;
+ struct ldb_message msg;
+ struct ldb_message_element el;
+ struct ldb_val val;
+ char *s = NULL;
+ int ret;
+
+ asprintf(&s, "%.0f", ltdb->sequence_number+1);
+ if (!s) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ msg.num_elements = 1;
+ msg.elements = &el;
+ msg.dn = LTDB_BASEINFO;
+ el.name = LTDB_SEQUENCE_NUMBER;
+ el.values = &val;
+ el.num_values = 1;
+ el.flags = LDB_FLAG_MOD_REPLACE;
+ val.data = s;
+ val.length = strlen(s);
+
+ ret = ltdb_modify_internal(ldb, &msg);
+
+ free(s);
+
+ if (ret == 0) {
+ ltdb->sequence_number += 1;
+ }
+
+ return ret;
+}
+
+
+/*
+ return the attribute flags from the @ATTRIBUTES record
+ for the given attribute
+*/
+int ltdb_attribute_flags(struct ldb_context *ldb, const char *attr_name)
+{
+ struct ltdb_private *ltdb = ldb->private_data;
+ const char *attrs;
+ const struct {
+ const char *name;
+ int value;
+ } names[] = {
+ { "CASE_INSENSITIVE", LTDB_FLAG_CASE_INSENSITIVE },
+ { "INTEGER", LTDB_FLAG_INTEGER },
+ { "WILDCARD", LTDB_FLAG_WILDCARD },
+ { NULL, 0}
+ };
+ size_t len;
+ int i, ret=0;
+
+ if (ltdb->cache.last_attribute.name &&
+ ldb_attr_cmp(ltdb->cache.last_attribute.name, attr_name) == 0) {
+ return ltdb->cache.last_attribute.flags;
+ }
+
+ /* objectclass is a special default case */
+ if (ldb_attr_cmp(attr_name, LTDB_OBJECTCLASS) == 0) {
+ ret = LTDB_FLAG_OBJECTCLASS | LTDB_FLAG_CASE_INSENSITIVE;
+ }
+
+ attrs = ldb_msg_find_string(&ltdb->cache.attributes, attr_name, NULL);
+
+ if (!attrs) {
+ return ret;
+ }
+
+ /* we avoid using strtok and friends due to their nasty
+ interface. This is a little trickier, but much nicer
+ from a C interface point of view */
+ while ((len = strcspn(attrs, " ,")) > 0) {
+ for (i=0;names[i].name;i++) {
+ if (strncmp(names[i].name, attrs, len) == 0 &&
+ names[i].name[len] == 0) {
+ ret |= names[i].value;
+ }
+ }
+ attrs += len;
+ attrs += strspn(attrs, " ,");
+ }
+
+ if (ltdb->cache.last_attribute.name) free(ltdb->cache.last_attribute.name);
+
+ ltdb->cache.last_attribute.name = strdup(attr_name);
+ ltdb->cache.last_attribute.flags = ret;
+
+ return ret;
+}
diff --git a/source4/lib/ldb/ldb_tdb/ldb_index.c b/source4/lib/ldb/ldb_tdb/ldb_index.c
index a23fc2d525..c4243e9b40 100644
--- a/source4/lib/ldb/ldb_tdb/ldb_index.c
+++ b/source4/lib/ldb/ldb_tdb/ldb_index.c
@@ -63,12 +63,12 @@ static char *ldb_dn_key(const char *attr, const struct ldb_val *value)
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);
+ asprintf(&ret, "%s:%s::%s", LTDB_INDEX, attr, vstr);
free(vstr);
return ret;
}
- asprintf(&ret, "@INDEX:%s:%s", attr, (char *)value->data);
+ asprintf(&ret, "%s:%s:%s", LTDB_INDEX, attr, (char *)value->data);
return ret;
}
@@ -80,11 +80,11 @@ static int ldb_msg_find_idx(const struct ldb_message *msg, const char *attr,
{
int i, j;
for (i=0;i<msg->num_elements;i++) {
- if (strcmp(msg->elements[i].name, "@IDXATTR") == 0) {
+ if (ldb_attr_cmp(msg->elements[i].name, LTDB_IDXATTR) == 0) {
const struct ldb_message_element *el =
&msg->elements[i];
for (j=0;j<el->num_values;j++) {
- if (strcmp((char *)el->values[j].data, attr) == 0) {
+ if (ldb_attr_cmp((char *)el->values[j].data, attr) == 0) {
if (v_idx) {
*v_idx = j;
}
@@ -114,7 +114,7 @@ static int ltdb_index_dn_simple(struct ldb_context *ldb,
/*
if the value is a wildcard then we can't do a match via indexing
*/
- if (ltdb_has_wildcard(&tree->u.simple.value)) {
+ if (ltdb_has_wildcard(ldb, tree->u.simple.attr, &tree->u.simple.value)) {
return -1;
}
@@ -138,7 +138,7 @@ static int ltdb_index_dn_simple(struct ldb_context *ldb,
for (i=0;i<msg.num_elements;i++) {
struct ldb_message_element *el;
- if (strcmp(msg.elements[i].name, "@IDX") != 0) {
+ if (strcmp(msg.elements[i].name, LTDB_IDX) != 0) {
continue;
}
@@ -420,7 +420,7 @@ 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)
+ char * const attrs[], struct ldb_message ***res)
{
int i;
unsigned int count = 0;
@@ -460,21 +460,18 @@ 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)
+ char * const attrs[], struct ldb_message ***res)
{
- struct ldb_message index_list;
+ struct ltdb_private *ltdb = ldb->private_data;
struct dn_list dn_list;
int ret;
- /* find the list of indexed fields */
- ret = ltdb_search_dn1(ldb, "@INDEXLIST", &index_list);
- if (ret != 1) {
+ if (ltdb->cache.indexlist.num_elements == 0) {
/* 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);
+ ret = ltdb_index_dn(ldb, tree, &ltdb->cache.indexlist, &dn_list);
if (ret == 1) {
/* we've got a candidate list - now filter by the full tree
@@ -493,7 +490,7 @@ int ltdb_search_indexed(struct ldb_context *ldb,
static int ltdb_index_add1_new(struct ldb_context *ldb,
struct ldb_message *msg,
struct ldb_message_element *el,
- const char *dn)
+ char *dn)
{
struct ldb_message_element *el2;
@@ -504,7 +501,7 @@ static int ltdb_index_add1_new(struct ldb_context *ldb,
}
msg->elements = el2;
- msg->elements[msg->num_elements].name = "@IDX";
+ msg->elements[msg->num_elements].name = LTDB_IDX;
msg->elements[msg->num_elements].num_values = 0;
msg->elements[msg->num_elements].values = malloc_p(struct ldb_val);
if (!msg->elements[msg->num_elements].values) {
@@ -527,7 +524,7 @@ static int ltdb_index_add1_add(struct ldb_context *ldb,
struct ldb_message *msg,
struct ldb_message_element *el,
int idx,
- const char *dn)
+ char *dn)
{
struct ldb_val *v2;
@@ -549,7 +546,7 @@ static int ltdb_index_add1_add(struct ldb_context *ldb,
/*
add an index entry for one message element
*/
-static int ltdb_index_add1(struct ldb_context *ldb, const char *dn,
+static int ltdb_index_add1(struct ldb_context *ldb, char *dn,
struct ldb_message_element *el, int v_idx)
{
struct ldb_message msg;
@@ -582,7 +579,7 @@ static int ltdb_index_add1(struct ldb_context *ldb, const char *dn,
free(dn_key);
for (i=0;i<msg.num_elements;i++) {
- if (strcmp("@IDX", msg.elements[i].name) == 0) {
+ if (strcmp(LTDB_IDX, msg.elements[i].name) == 0) {
break;
}
}
@@ -608,32 +605,27 @@ static int ltdb_index_add1(struct ldb_context *ldb, const char *dn,
*/
int ltdb_index_add(struct ldb_context *ldb, const struct ldb_message *msg)
{
+ struct ltdb_private *ltdb = ldb->private_data;
int ret, i, j;
- 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;
+ if (ltdb->cache.indexlist.num_elements == 0) {
+ /* no indexed fields */
+ return 0;
}
for (i=0;i<msg->num_elements;i++) {
- ret = ldb_msg_find_idx(&index_list, msg->elements[i].name, NULL);
+ ret = ldb_msg_find_idx(&ltdb->cache.indexlist, msg->elements[i].name, NULL);
if (ret == -1) {
continue;
}
for (j=0;j<msg->elements[i].num_values;j++) {
ret = ltdb_index_add1(ldb, msg->dn, &msg->elements[i], j);
if (ret == -1) {
- ltdb_search_dn1_free(ldb, &index_list);
return -1;
}
}
}
- ltdb_search_dn1_free(ldb, &index_list);
-
return 0;
}
@@ -698,25 +690,23 @@ static int ltdb_index_del1(struct ldb_context *ldb, const char *dn,
*/
int ltdb_index_del(struct ldb_context *ldb, const struct ldb_message *msg)
{
+ struct ltdb_private *ltdb = ldb->private_data;
int ret, i, j;
- 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;
+ if (ltdb->cache.indexlist.num_elements == 0) {
+ /* no indexed fields */
+ return 0;
}
for (i=0;i<msg->num_elements;i++) {
- ret = ldb_msg_find_idx(&index_list, msg->elements[i].name, NULL);
+ ret = ldb_msg_find_idx(&ltdb->cache.indexlist, msg->elements[i].name, NULL);
if (ret == -1) {
continue;
}
for (j=0;j<msg->elements[i].num_values;j++) {
ret = ltdb_index_del1(ldb, msg->dn, &msg->elements[i], j);
if (ret == -1) {
- ltdb_search_dn1_free(ldb, &index_list);
return -1;
}
}
@@ -731,7 +721,8 @@ int ltdb_index_del(struct ldb_context *ldb, const struct ldb_message *msg)
*/
static int delete_index(struct tdb_context *tdb, TDB_DATA key, TDB_DATA data, void *state)
{
- if (strncmp(key.dptr, "DN=@INDEX:", 10) == 0) {
+ const char *dn = "DN=" LTDB_INDEX ":";
+ if (strncmp(key.dptr, dn, strlen(dn)) == 0) {
return tdb_delete(tdb, key);
}
return 0;
@@ -756,7 +747,9 @@ static int re_index(struct tdb_context *tdb, TDB_DATA key, TDB_DATA data, void *
return -1;
}
- msg.dn = key.dptr+3;
+ if (!msg.dn) {
+ msg.dn = key.dptr+3;
+ }
ret = ltdb_index_add(ldb, &msg);
@@ -773,6 +766,12 @@ int ltdb_reindex(struct ldb_context *ldb)
struct ltdb_private *ltdb = ldb->private_data;
int ret;
+ ltdb_cache_free(ldb);
+
+ if (ltdb_cache_load(ldb) != 0) {
+ return -1;
+ }
+
/* first traverse the database deleting any @INDEX records */
ret = tdb_traverse(ltdb->tdb, delete_index, NULL);
if (ret == -1) {
diff --git a/source4/lib/ldb/ldb_tdb/ldb_match.c b/source4/lib/ldb/ldb_tdb/ldb_match.c
index 6f29726ee7..26e0eebfe7 100644
--- a/source4/lib/ldb/ldb_tdb/ldb_match.c
+++ b/source4/lib/ldb/ldb_tdb/ldb_match.c
@@ -33,14 +33,162 @@
*/
#include "includes.h"
+#include "ldb/ldb_tdb/ldb_tdb.h"
/*
+ see if two ldb_val structures contain the same data as integers
+ return 1 for a match, 0 for a mis-match
+*/
+static int ldb_val_equal_integer(const struct ldb_val *v1, const struct ldb_val *v2)
+{
+ int i1, i2;
+
+ i1 = strtol(v1->data, NULL, 0);
+ i2 = strtol(v2->data, NULL, 0);
+
+ return i1 == i2;
+}
+
+/*
+ see if two ldb_val structures contain the same data as case insensitive strings
+ return 1 for a match, 0 for a mis-match
+*/
+static int ldb_val_equal_case_insensitive(const struct ldb_val *v1,
+ const struct ldb_val *v2)
+{
+ if (v1->length != v2->length) {
+ return 0;
+ }
+ if (strncasecmp(v1->data, v2->data, v1->length) == 0) {
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ see if two ldb_val structures contain the same data with wildcards
+ and case insensitive
+ return 1 for a match, 0 for a mis-match
+*/
+static int ldb_val_equal_wildcard_ci(const struct ldb_val *v1,
+ const struct ldb_val *v2)
+{
+ char *s1, *s2;
+ int ret;
+
+ if (!v1->data || !v2->data) {
+ return v1->data == v2->data;
+ }
+
+ s1 = ldb_casefold(v1->data);
+ if (!s1) {
+ return -1;
+ }
+
+ s2 = ldb_casefold(v2->data);
+ if (!s2) {
+ return -1;
+ }
+
+ ret = fnmatch(s2, s1, 0);
+
+ free(s1);
+ free(s2);
+
+ if (ret == 0) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ see if two ldb_val structures contain the same data with wildcards
+ return 1 for a match, 0 for a mis-match
+*/
+static int ldb_val_equal_wildcard(const struct ldb_val *v1,
+ const struct ldb_val *v2,
+ int flags)
+{
+ if (flags & LTDB_FLAG_CASE_INSENSITIVE) {
+ return ldb_val_equal_wildcard_ci(v1, v2);
+ }
+ if (!v1->data || !v2->data) {
+ return v1->data == v2->data;
+ }
+ if (fnmatch(v2->data, v1->data, 0) == 0) {
+ return 1;
+ }
+ return 0;
+}
+
+
+/*
+ see if two objectclasses are considered equal. This handles
+ the subclass attributes
+
+ v1 contains the in-database value, v2 contains the value
+ that the user gave
+
+ return 1 for a match, 0 for a mis-match
+*/
+static int ldb_val_equal_objectclass(struct ldb_context *ldb,
+ const struct ldb_val *v1, const struct ldb_val *v2)
+{
+ struct ltdb_private *ltdb = ldb->private_data;
+ int i;
+
+ if (ldb_val_equal_case_insensitive(v1, v2) == 1) {
+ return 1;
+ }
+
+ for (i=0;i<ltdb->cache.subclasses.num_elements;i++) {
+ struct ldb_message_element *el = &ltdb->cache.subclasses.elements[i];
+ if (ldb_attr_cmp(el->name, v2->data) == 0) {
+ int j;
+ for (j=0;j<el->num_values;j++) {
+ if (ldb_val_equal_objectclass(ldb, v1, &el->values[j])) {
+ return 1;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+/*
see if two ldb_val structures contain the same data
+
+ v1 contains the in-database value, v2 contains the value
+ that the user gave
+
return 1 for a match, 0 for a mis-match
*/
-int ldb_val_equal(const struct ldb_val *v1, const struct ldb_val *v2)
+int ldb_val_equal(struct ldb_context *ldb,
+ const char *attr_name,
+ const struct ldb_val *v1, const struct ldb_val *v2)
{
+ int flags = ltdb_attribute_flags(ldb, attr_name);
+
+ if (flags & LTDB_FLAG_OBJECTCLASS) {
+ return ldb_val_equal_objectclass(ldb, v1, v2);
+ }
+
+ if (flags & LTDB_FLAG_INTEGER) {
+ return ldb_val_equal_integer(v1, v2);
+ }
+
+ if (flags & LTDB_FLAG_WILDCARD) {
+ return ldb_val_equal_wildcard(v1, v2, flags);
+ }
+
+ if (flags & LTDB_FLAG_CASE_INSENSITIVE) {
+ return ldb_val_equal_case_insensitive(v1, v2);
+ }
+
if (v1->length != v2->length) return 0;
if (v1->length == 0) return 1;
@@ -66,7 +214,7 @@ static int scope_match(const char *dn, const char *base, enum ldb_scope scope)
base_len = strlen(base);
dn_len = strlen(dn);
- if (strcmp(dn, base) == 0) {
+ if (ldb_dn_cmp(dn, base) == 0) {
return 1;
}
@@ -79,7 +227,7 @@ static int scope_match(const char *dn, const char *base, enum ldb_scope scope)
break;
case LDB_SCOPE_ONELEVEL:
- if (strcmp(dn + (dn_len - base_len), base) == 0 &&
+ if (ldb_dn_cmp(dn + (dn_len - base_len), base) == 0 &&
dn[dn_len - base_len - 1] == ',' &&
strchr(dn, ',') == &dn[dn_len - base_len - 1]) {
return 1;
@@ -88,7 +236,7 @@ static int scope_match(const char *dn, const char *base, enum ldb_scope scope)
case LDB_SCOPE_SUBTREE:
default:
- if (strcmp(dn + (dn_len - base_len), base) == 0 &&
+ if (ldb_dn_cmp(dn + (dn_len - base_len), base) == 0 &&
dn[dn_len - base_len - 1] == ',') {
return 1;
}
@@ -114,20 +262,21 @@ static int match_leaf(struct ldb_context *ldb,
return 0;
}
- if (strcmp(tree->u.simple.attr, "dn") == 0) {
+ if (ldb_attr_cmp(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;
+ return ldb_dn_cmp(msg->dn, tree->u.simple.value.data) == 0;
}
for (i=0;i<msg->num_elements;i++) {
- if (strcmp(msg->elements[i].name, tree->u.simple.attr) == 0) {
+ if (ldb_attr_cmp(msg->elements[i].name, tree->u.simple.attr) == 0) {
if (strcmp(tree->u.simple.value.data, "*") == 0) {
return 1;
}
for (j=0;j<msg->elements[i].num_values;j++) {
- if (ldb_val_equal(&msg->elements[i].values[j],
+ if (ldb_val_equal(ldb, msg->elements[i].name,
+ &msg->elements[i].values[j],
&tree->u.simple.value)) {
return 1;
}
diff --git a/source4/lib/ldb/ldb_tdb/ldb_pack.c b/source4/lib/ldb/ldb_tdb/ldb_pack.c
index 1196e561a2..3ded595259 100644
--- a/source4/lib/ldb/ldb_tdb/ldb_pack.c
+++ b/source4/lib/ldb/ldb_tdb/ldb_pack.c
@@ -36,7 +36,10 @@
#include "ldb/ldb_tdb/ldb_tdb.h"
/* change this if the data format ever changes */
-#define LTDB_PACKING_FORMAT 0x26011966
+#define LTDB_PACKING_FORMAT 0x26011967
+
+/* old packing formats */
+#define LTDB_PACKING_FORMAT_NODN 0x26011966
/*
pack a ldb message into a linear buffer in a TDB_DATA
@@ -53,10 +56,13 @@ int ltdb_pack_data(struct ldb_context *ctx,
int i, j;
size_t size;
char *p;
+ size_t len;
/* work out how big it needs to be */
size = 8;
+ size += 1 + strlen(message->dn);
+
for (i=0;i<message->num_elements;i++) {
if (message->elements[i].num_values == 0) {
continue;
@@ -79,9 +85,14 @@ int ltdb_pack_data(struct ldb_context *ctx,
SIVAL(p, 0, LTDB_PACKING_FORMAT);
SIVAL(p, 4, message->num_elements);
p += 8;
+
+ /* the dn needs to be packed so we can be case preserving
+ while hashing on a case folded dn */
+ len = strlen(message->dn);
+ memcpy(p, message->dn, len+1);
+ p += len + 1;
for (i=0;i<message->num_elements;i++) {
- size_t len;
if (message->elements[i].num_values == 0) {
continue;
}
@@ -133,33 +144,49 @@ int ltdb_unpack_data(struct ldb_context *ctx,
char *p;
unsigned int remaining;
int i, j;
+ unsigned format;
+ size_t len;
message->elements = NULL;
p = data->dptr;
- if (data->dsize < 4) {
+ if (data->dsize < 8) {
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 */
+ format = IVAL(p, 0);
+ message->num_elements = IVAL(p, 4);
+ p += 8;
+
+ remaining = data->dsize - 8;
+
+ switch (format) {
+ case LTDB_PACKING_FORMAT_NODN:
+ message->dn = NULL;
+ break;
+
+ case LTDB_PACKING_FORMAT:
+ len = strnlen(p, remaining);
+ if (len == remaining) {
+ errno = EIO;
+ goto failed;
+ }
+ message->dn = p;
+ remaining -= len + 1;
+ p += len + 1;
+ break;
+
+ default:
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;
@@ -174,12 +201,15 @@ int ltdb_unpack_data(struct ldb_context *ctx,
}
for (i=0;i<message->num_elements;i++) {
- size_t len;
if (remaining < 10) {
errno = EIO;
goto failed;
}
len = strnlen(p, remaining-6);
+ if (len == remaining-6) {
+ errno = EIO;
+ goto failed;
+ }
message->elements[i].flags = 0;
message->elements[i].name = p;
remaining -= len + 1;
diff --git a/source4/lib/ldb/ldb_tdb/ldb_search.c b/source4/lib/ldb/ldb_tdb/ldb_search.c
index fe84091303..7059030212 100644
--- a/source4/lib/ldb/ldb_tdb/ldb_search.c
+++ b/source4/lib/ldb/ldb_tdb/ldb_search.c
@@ -148,7 +148,7 @@ static int msg_add_all_elements(struct ldb_message *ret,
*/
static struct ldb_message *ltdb_pull_attrs(struct ldb_context *ldb,
const struct ldb_message *msg,
- const char **attrs)
+ char * const *attrs)
{
struct ldb_message *ret;
int i;
@@ -205,11 +205,20 @@ static struct ldb_message *ltdb_pull_attrs(struct ldb_context *ldb,
/*
see if a ldb_val is a wildcard
*/
-int ltdb_has_wildcard(const struct ldb_val *val)
+int ltdb_has_wildcard(struct ldb_context *ldb, const char *attr_name,
+ const struct ldb_val *val)
{
- if (val->length == 1 && ((char *)val->data)[0] == '*') {
+ int flags;
+
+ if (strpbrk(val->data, "*?") == NULL) {
+ return 0;
+ }
+
+ flags = ltdb_attribute_flags(ldb, attr_name);
+ if (flags & LTDB_FLAG_WILDCARD) {
return 1;
}
+
return 0;
}
@@ -220,12 +229,12 @@ int ltdb_has_wildcard(const struct ldb_val *val)
void ltdb_search_dn1_free(struct ldb_context *ldb, struct ldb_message *msg)
{
int i;
- if (msg->dn) free(msg->dn);
if (msg->private_data) free(msg->private_data);
for (i=0;i<msg->num_elements;i++) {
if (msg->elements[i].values) free(msg->elements[i].values);
}
if (msg->elements) free(msg->elements);
+ memset(msg, 0, sizeof(*msg));
}
@@ -242,7 +251,7 @@ int ltdb_search_dn1(struct ldb_context *ldb, const char *dn, struct ldb_message
TDB_DATA tdb_key, tdb_data;
/* form the key */
- tdb_key = ltdb_key(dn);
+ tdb_key = ltdb_key(ldb, dn);
if (!tdb_key.dptr) {
return -1;
}
@@ -253,11 +262,6 @@ int ltdb_search_dn1(struct ldb_context *ldb, const char *dn, struct ldb_message
return 0;
}
- msg->dn = strdup(dn);
- if (!msg->dn) {
- free(tdb_data.dptr);
- return -1;
- }
msg->private_data = tdb_data.dptr;
msg->num_elements = 0;
msg->elements = NULL;
@@ -268,6 +272,14 @@ int ltdb_search_dn1(struct ldb_context *ldb, const char *dn, struct ldb_message
return -1;
}
+ if (!msg->dn) {
+ msg->dn = strdup(dn);
+ }
+ if (!msg->dn) {
+ free(tdb_data.dptr);
+ return -1;
+ }
+
return 1;
}
@@ -276,7 +288,7 @@ int ltdb_search_dn1(struct ldb_context *ldb, const char *dn, struct ldb_message
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)
+ char * const attrs[], struct ldb_message ***res)
{
int ret;
struct ldb_message msg, *msg2;
@@ -312,7 +324,7 @@ int ltdb_search_dn(struct ldb_context *ldb, char *dn,
return 0 on success, -1 on failure
*/
int ltdb_add_attr_results(struct ldb_context *ldb, struct ldb_message *msg,
- const char *attrs[],
+ char * const attrs[],
unsigned int *count,
struct ldb_message ***res)
{
@@ -350,7 +362,7 @@ struct ltdb_search_info {
struct ldb_parse_tree *tree;
const char *base;
enum ldb_scope scope;
- const char **attrs;
+ char * const *attrs;
struct ldb_message **msgs;
int failures;
int count;
@@ -371,8 +383,6 @@ static int search_func(struct tdb_context *tdb, TDB_DATA key, TDB_DATA data, voi
return 0;
}
- msg.dn = key.dptr + 3;
-
/* unpack the record */
ret = ltdb_unpack_data(sinfo->ldb, &data, &msg);
if (ret == -1) {
@@ -380,6 +390,10 @@ static int search_func(struct tdb_context *tdb, TDB_DATA key, TDB_DATA data, voi
return 0;
}
+ if (!msg.dn) {
+ msg.dn = key.dptr + 3;
+ }
+
/* see if it matches the given expression */
if (!ldb_message_match(sinfo->ldb, &msg, sinfo->tree,
sinfo->base, sinfo->scope)) {
@@ -425,7 +439,7 @@ 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)
+ char * const attrs[], struct ldb_message ***res)
{
struct ltdb_private *ltdb = ldb->private_data;
int ret;
@@ -458,11 +472,15 @@ static int ltdb_search_full(struct ldb_context *ldb,
*/
int ltdb_search(struct ldb_context *ldb, const char *base,
enum ldb_scope scope, const char *expression,
- const char *attrs[], struct ldb_message ***res)
+ char * const attrs[], struct ldb_message ***res)
{
struct ldb_parse_tree *tree;
int ret;
+ if (ltdb_cache_load(ldb) != 0) {
+ return -1;
+ }
+
*res = NULL;
/* form a parse tree for the expression */
@@ -472,8 +490,8 @@ int ltdb_search(struct ldb_context *ldb, const char *base,
}
if (tree->operation == LDB_OP_SIMPLE &&
- strcmp(tree->u.simple.attr, "dn") == 0 &&
- !ltdb_has_wildcard(&tree->u.simple.value)) {
+ ldb_attr_cmp(tree->u.simple.attr, "dn") == 0 &&
+ !ltdb_has_wildcard(ldb, tree->u.simple.attr, &tree->u.simple.value)) {
/* yay! its a nice simple one */
ret = ltdb_search_dn(ldb, tree->u.simple.value.data, attrs, res);
} else {
diff --git a/source4/lib/ldb/ldb_tdb/ldb_tdb.c b/source4/lib/ldb/ldb_tdb/ldb_tdb.c
index ff0c0a53b7..eb2decfe31 100644
--- a/source4/lib/ldb/ldb_tdb/ldb_tdb.c
+++ b/source4/lib/ldb/ldb_tdb/ldb_tdb.c
@@ -38,24 +38,70 @@
/*
form a TDB_DATA for a record key
caller frees
+
+ note that the key for a record can depend on whether the
+ dn refers to a case sensitive index record or not
*/
-struct TDB_DATA ltdb_key(const char *dn)
+struct TDB_DATA ltdb_key(struct ldb_context *ldb, const char *dn)
{
TDB_DATA key;
char *key_str = NULL;
+ char *dn_folded = NULL;
+ const char *prefix = LTDB_INDEX ":";
+ const char *s;
+ int flags;
+
+ /*
+ most DNs are case insensitive. The exception is index DNs for
+ case sensitive attributes
+ */
+ if (strncmp(dn, prefix, strlen(prefix)) == 0 &&
+ (s = strchr(dn+strlen(prefix), ':'))) {
+ char *attr_name, *attr_name_folded;
+ attr_name = strndup(dn+strlen(prefix), (s-(dn+strlen(prefix))));
+ if (!attr_name) {
+ goto failed;
+ }
+ flags = ltdb_attribute_flags(ldb, attr_name);
+
+ if (flags & LTDB_FLAG_CASE_INSENSITIVE) {
+ dn_folded = ldb_casefold(dn);
+ } else {
+ attr_name_folded = ldb_casefold(attr_name);
+ if (!attr_name_folded) {
+ goto failed;
+ }
+ asprintf(&dn_folded, "%s:%s:%s",
+ prefix, attr_name_folded,
+ s+1);
+ free(attr_name_folded);
+ }
+ free(attr_name);
+ } else {
+ dn_folded = ldb_casefold(dn);
+ }
+
+ if (!dn_folded) {
+ goto failed;
+ }
+
+ asprintf(&key_str, "DN=%s", dn_folded);
+ free(dn_folded);
- asprintf(&key_str, "DN=%s", dn);
if (!key_str) {
- errno = ENOMEM;
- key.dptr = NULL;
- key.dsize = 0;
- return key;
+ goto failed;
}
key.dptr = key_str;
key.dsize = strlen(key_str)+1;
return key;
+
+failed:
+ errno = ENOMEM;
+ key.dptr = NULL;
+ key.dsize = 0;
+ return key;
}
/*
@@ -67,7 +113,7 @@ static int ltdb_lock(struct ldb_context *ldb)
TDB_DATA key;
int ret;
- key = ltdb_key("LDBLOCK");
+ key = ltdb_key(ldb, "LDBLOCK");
if (!key.dptr) {
return -1;
}
@@ -87,7 +133,7 @@ static void ltdb_unlock(struct ldb_context *ldb)
struct ltdb_private *ltdb = ldb->private_data;
TDB_DATA key;
- key = ltdb_key("LDBLOCK");
+ key = ltdb_key(ldb, "LDBLOCK");
if (!key.dptr) {
return;
}
@@ -97,6 +143,28 @@ static void ltdb_unlock(struct ldb_context *ldb)
free(key.dptr);
}
+
+/*
+ we've made a modification to a dn - possibly reindex and
+ update sequence number
+*/
+static int ltdb_modified(struct ldb_context *ldb, const char *dn)
+{
+ int ret = 0;
+
+ if (strcmp(dn, LTDB_INDEXLIST) == 0 ||
+ strcmp(dn, LTDB_ATTRIBUTES) == 0) {
+ ret = ltdb_reindex(ldb);
+ }
+
+ if (ret == 0 &&
+ strcmp(dn, LTDB_BASEINFO) != 0) {
+ ret = ltdb_increase_sequence_number(ldb);
+ }
+
+ return ret;
+}
+
/*
store a record into the db
*/
@@ -106,7 +174,7 @@ int ltdb_store(struct ldb_context *ldb, const struct ldb_message *msg, int flgs)
TDB_DATA tdb_key, tdb_data;
int ret;
- tdb_key = ltdb_key(msg->dn);
+ tdb_key = ltdb_key(ldb, msg->dn);
if (!tdb_key.dptr) {
return -1;
}
@@ -145,15 +213,19 @@ static int ltdb_add(struct ldb_context *ldb, const struct ldb_message *msg)
if (ltdb_lock(ldb) != 0) {
return -1;
}
+
+ if (ltdb_cache_load(ldb) != 0) {
+ ltdb_unlock(ldb);
+ return -1;
+ }
ret = ltdb_store(ldb, msg, TDB_INSERT);
- if (strcmp(msg->dn, "@INDEXLIST") == 0) {
- ltdb_reindex(ldb);
+ if (ret == 0) {
+ ltdb_modified(ldb, msg->dn);
}
ltdb_unlock(ldb);
-
return ret;
}
@@ -168,7 +240,7 @@ int ltdb_delete_noindex(struct ldb_context *ldb, const char *dn)
TDB_DATA tdb_key;
int ret;
- tdb_key = ltdb_key(dn);
+ tdb_key = ltdb_key(ldb, dn);
if (!tdb_key.dptr) {
return -1;
}
@@ -191,6 +263,11 @@ static int ltdb_delete(struct ldb_context *ldb, const char *dn)
return -1;
}
+ if (ltdb_cache_load(ldb) != 0) {
+ ltdb_unlock(ldb);
+ return -1;
+ }
+
/* in case any attribute of the message was indexed, we need
to fetch the old record */
ret = ltdb_search_dn1(ldb, dn, &msg);
@@ -210,8 +287,8 @@ static int ltdb_delete(struct ldb_context *ldb, const char *dn)
ltdb_search_dn1_free(ldb, &msg);
- if (strcmp(dn, "@INDEXLIST") == 0) {
- ltdb_reindex(ldb);
+ if (ret == 0) {
+ ltdb_modified(ldb, dn);
}
ltdb_unlock(ldb);
@@ -234,7 +311,7 @@ static int find_element(const struct ldb_message *msg, const char *name)
{
int i;
for (i=0;i<msg->num_elements;i++) {
- if (strcmp(msg->elements[i].name, name) == 0) {
+ if (ldb_attr_cmp(msg->elements[i].name, name) == 0) {
return i;
}
}
@@ -301,7 +378,7 @@ static int msg_delete_attribute(struct ldb_message *msg, const char *name)
}
for (i=0;i<msg->num_elements;i++) {
- if (strcmp(msg->elements[i].name, name) != 0) {
+ if (ldb_attr_cmp(msg->elements[i].name, name) != 0) {
el2[count++] = msg->elements[i];
} else {
if (msg->elements[i].values) free(msg->elements[i].values);
@@ -320,7 +397,8 @@ static int msg_delete_attribute(struct ldb_message *msg, const char *name)
return 0 on success, -1 on failure
*/
-static int msg_delete_element(struct ldb_message *msg,
+static int msg_delete_element(struct ldb_context *ldb,
+ struct ldb_message *msg,
const char *name,
const struct ldb_val *val)
{
@@ -335,7 +413,7 @@ static int msg_delete_element(struct ldb_message *msg,
el = &msg->elements[i];
for (i=0;i<el->num_values;i++) {
- if (ldb_val_equal(&el->values[i], val)) {
+ if (ldb_val_equal(ldb, msg->elements[i].name, &el->values[i], val)) {
if (i<el->num_values-1) {
memmove(&el->values[i], &el->values[i+1],
sizeof(el->values[i])*el->num_values-(i+1));
@@ -348,43 +426,42 @@ static int msg_delete_element(struct ldb_message *msg,
return -1;
}
+
/*
- modify a record
+ modify a record - internal interface
yuck - this is O(n^2). Luckily n is usually small so we probably
get away with it, but if we ever have really large attribute lists
then we'll need to look at this again
*/
-static int ltdb_modify(struct ldb_context *ldb, const struct ldb_message *msg)
+int ltdb_modify_internal(struct ldb_context *ldb, const struct ldb_message *msg)
{
struct ltdb_private *ltdb = ldb->private_data;
TDB_DATA tdb_key, tdb_data;
struct ldb_message msg2;
int ret, i, j;
- if (ltdb_lock(ldb) != 0) {
- return -1;
- }
-
- tdb_key = ltdb_key(msg->dn);
+ tdb_key = ltdb_key(ldb, msg->dn);
if (!tdb_key.dptr) {
- goto unlock_fail;
+ return -1;
}
tdb_data = tdb_fetch(ltdb->tdb, tdb_key);
if (!tdb_data.dptr) {
free(tdb_key.dptr);
- goto unlock_fail;
+ return -1;
}
ret = ltdb_unpack_data(ldb, &tdb_data, &msg2);
if (ret == -1) {
free(tdb_key.dptr);
free(tdb_data.dptr);
- goto unlock_fail;
+ return -1;
}
- msg2.dn = msg->dn;
+ if (!msg2.dn) {
+ msg2.dn = msg->dn;
+ }
for (i=0;i<msg->num_elements;i++) {
switch (msg->elements[i].flags & LDB_FLAG_MOD_MASK) {
@@ -425,9 +502,10 @@ static int ltdb_modify(struct ldb_context *ldb, const struct ldb_message *msg)
break;
}
for (j=0;j<msg->elements[i].num_values;j++) {
- if (msg_delete_element(&msg2,
- msg->elements[i].name,
- &msg->elements[i].values[j]) != 0) {
+ if (msg_delete_element(ldb,
+ &msg2,
+ msg->elements[i].name,
+ &msg->elements[i].values[j]) != 0) {
goto failed;
}
}
@@ -438,26 +516,43 @@ static int ltdb_modify(struct ldb_context *ldb, const struct ldb_message *msg)
/* we've made all the mods - save the modified record back into the database */
ret = ltdb_store(ldb, &msg2, TDB_MODIFY);
- if (strcmp(msg2.dn, "@INDEXLIST") == 0) {
- ltdb_reindex(ldb);
- }
-
free(tdb_key.dptr);
free(tdb_data.dptr);
ltdb_unpack_data_free(&msg2);
- ltdb_unlock(ldb);
-
return ret;
failed:
free(tdb_key.dptr);
free(tdb_data.dptr);
ltdb_unpack_data_free(&msg2);
+ return -1;
+}
+
+/*
+ modify a record
+*/
+static int ltdb_modify(struct ldb_context *ldb, const struct ldb_message *msg)
+{
+ int ret;
+
+ if (ltdb_lock(ldb) != 0) {
+ return -1;
+ }
+
+ if (ltdb_cache_load(ldb) != 0) {
+ ltdb_unlock(ldb);
+ return -1;
+ }
+
+ ret = ltdb_modify_internal(ldb, msg);
+
+ if (ret == 0) {
+ ltdb_modified(ldb, msg->dn);
+ }
-unlock_fail:
ltdb_unlock(ldb);
-
- return -1;
+
+ return ret;
}
/*
@@ -467,6 +562,9 @@ static int ltdb_close(struct ldb_context *ldb)
{
struct ltdb_private *ltdb = ldb->private_data;
int ret;
+
+ ltdb_cache_free(ldb);
+
ret = tdb_close(ltdb->tdb);
free(ltdb);
free(ldb);
@@ -538,7 +636,9 @@ struct ldb_context *ltdb_connect(const char *url,
}
ltdb->tdb = tdb;
-
+ ltdb->sequence_number = 0;
+
+ memset(&ltdb->cache, 0, sizeof(ltdb->cache));
ldb = malloc_p(struct ldb_context);
if (!ldb) {
diff --git a/source4/lib/ldb/ldb_tdb/ldb_tdb.h b/source4/lib/ldb/ldb_tdb/ldb_tdb.h
index efb0af1c9c..e87027db63 100644
--- a/source4/lib/ldb/ldb_tdb/ldb_tdb.h
+++ b/source4/lib/ldb/ldb_tdb/ldb_tdb.h
@@ -3,8 +3,44 @@
struct ltdb_private {
TDB_CONTEXT *tdb;
unsigned int connect_flags;
+
+ /* a double is used for portability and ease of string
+ handling. It has plenty of digits of precision */
+ double sequence_number;
+
+ struct {
+ struct ldb_message baseinfo;
+ struct ldb_message indexlist;
+ struct ldb_message attributes;
+ struct ldb_message subclasses;
+
+ struct {
+ char *name;
+ int flags;
+ } last_attribute;
+ } cache;
};
+/* special record types */
+#define LTDB_INDEX "@INDEX"
+#define LTDB_INDEXLIST "@INDEXLIST"
+#define LTDB_IDX "@IDX"
+#define LTDB_IDXATTR "@IDXATTR"
+#define LTDB_BASEINFO "@BASEINFO"
+#define LTDB_ATTRIBUTES "@ATTRIBUTES"
+#define LTDB_SUBCLASSES "@SUBCLASSES"
+
+/* special attribute types */
+#define LTDB_SEQUENCE_NUMBER "sequenceNumber"
+#define LTDB_OBJECTCLASS "objectClass"
+
+/* well known attribute flags */
+#define LTDB_FLAG_CASE_INSENSITIVE (1<<0)
+#define LTDB_FLAG_INTEGER (1<<1)
+#define LTDB_FLAG_WILDCARD (1<<2)
+#define LTDB_FLAG_OBJECTCLASS (1<<3)
+
+
#ifndef IVAL
#define IVAL(p, ofs) (((unsigned *)((char *)(p) + (ofs)))[0])
#endif