summaryrefslogtreecommitdiff
path: root/source4/lib/ldb/ldb_tdb/ldb_tdb.c
diff options
context:
space:
mode:
authorAndrew Tridgell <tridge@samba.org>2004-04-03 12:29:21 +0000
committerAndrew Tridgell <tridge@samba.org>2004-04-03 12:29:21 +0000
commitee44733f94864fb0a1ae15d48e3335c0705a82ae (patch)
tree8f9691975aab7a6ce3736e64283e66a69db3c194 /source4/lib/ldb/ldb_tdb/ldb_tdb.c
parentf1c3fa060efcecde404d0bfb55c359ef7fe36ed8 (diff)
downloadsamba-ee44733f94864fb0a1ae15d48e3335c0705a82ae.tar.gz
samba-ee44733f94864fb0a1ae15d48e3335c0705a82ae.tar.bz2
samba-ee44733f94864fb0a1ae15d48e3335c0705a82ae.zip
added the rest of the ldb_modify() code, which required a fairly large
change in the ldb API. The API is now much closer to LDAP. (This used to be commit e9e85c464411c561c5073d262a2e3533fec175ca)
Diffstat (limited to 'source4/lib/ldb/ldb_tdb/ldb_tdb.c')
-rw-r--r--source4/lib/ldb/ldb_tdb/ldb_tdb.c263
1 files changed, 251 insertions, 12 deletions
diff --git a/source4/lib/ldb/ldb_tdb/ldb_tdb.c b/source4/lib/ldb/ldb_tdb/ldb_tdb.c
index 17931352f7..95dce498f1 100644
--- a/source4/lib/ldb/ldb_tdb/ldb_tdb.c
+++ b/source4/lib/ldb/ldb_tdb/ldb_tdb.c
@@ -58,6 +58,44 @@ struct TDB_DATA ltdb_key(const char *dn)
return key;
}
+/*
+ lock the database for write - currently a single lock is used
+*/
+static int ltdb_lock(struct ldb_context *ldb)
+{
+ struct ltdb_private *ltdb = ldb->private;
+ TDB_DATA key;
+ int ret;
+
+ key = ltdb_key("LDBLOCK");
+ if (!key.dptr) {
+ return -1;
+ }
+
+ ret = tdb_chainlock(ltdb->tdb, key);
+
+ free(key.dptr);
+
+ return ret;
+}
+
+/*
+ unlock the database after a ltdb_lock()
+*/
+static void ltdb_unlock(struct ldb_context *ldb)
+{
+ struct ltdb_private *ltdb = ldb->private;
+ TDB_DATA key;
+
+ key = ltdb_key("LDBLOCK");
+ if (!key.dptr) {
+ return;
+ }
+
+ tdb_chainunlock(ltdb->tdb, key);
+
+ free(key.dptr);
+}
/*
store a record into the db
@@ -102,7 +140,17 @@ done:
*/
static int ltdb_add(struct ldb_context *ldb, const struct ldb_message *msg)
{
- return ltdb_store(ldb, msg, TDB_INSERT);
+ int ret;
+
+ if (ltdb_lock(ldb) != 0) {
+ return -1;
+ }
+
+ ret = ltdb_store(ldb, msg, TDB_INSERT);
+
+ ltdb_unlock(ldb);
+
+ return ret;
}
@@ -135,18 +183,22 @@ static int ltdb_delete(struct ldb_context *ldb, const char *dn)
int ret;
struct ldb_message msg;
+ if (ltdb_lock(ldb) != 0) {
+ 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);
if (ret != 1) {
/* not finding the old record is an error */
- return -1;
+ goto failed;
}
ret = ltdb_delete_noindex(ldb, dn);
if (ret == -1) {
ltdb_search_dn1_free(ldb, &msg);
- return -1;
+ goto failed;
}
/* remove any indexed attributes */
@@ -154,57 +206,244 @@ static int ltdb_delete(struct ldb_context *ldb, const char *dn)
ltdb_search_dn1_free(ldb, &msg);
+ ltdb_unlock(ldb);
return ret;
+
+failed:
+ ltdb_unlock(ldb);
+ return -1;
+}
+
+
+/*
+ find an element by attribute name. At the moment this does a linear search, it should
+ be re-coded to use a binary search once all places that modify records guarantee
+ sorted order
+
+ return the index of the first matching element if found, otherwise -1
+*/
+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) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+
+/*
+ add an element to an existing record. Assumes a elements array that we
+ can call re-alloc on, and assumed that we can re-use the data pointers from the
+ passed in additional values. Use with care!
+
+ returns 0 on success, -1 on failure (and sets errno)
+*/
+static int msg_add_element(struct ldb_message *msg, struct ldb_message_element *el)
+{
+ struct ldb_message_element *e2;
+ int i;
+
+ e2 = realloc_p(msg->elements, struct ldb_message_element,
+ msg->num_elements+1);
+ if (!e2) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ msg->elements = e2;
+
+ e2 = &msg->elements[msg->num_elements];
+
+ e2->name = el->name;
+ e2->flags = el->flags;
+ e2->values = NULL;
+ if (el->num_values != 0) {
+ e2->values = malloc_array_p(struct ldb_val, el->num_values);
+ if (!e2->values) {
+ free(e2->name);
+ errno = ENOMEM;
+ return -1;
+ }
+ }
+ for (i=0;i<el->num_values;i++) {
+ e2->values[i] = el->values[i];
+ }
+ e2->num_values = el->num_values;
+
+ msg->num_elements++;
+
+ return 0;
+}
+
+/*
+ delete all elements having a specified attribute name
+*/
+static int msg_delete_attribute(struct ldb_message *msg, const char *name)
+{
+ int i, count=0;
+ struct ldb_message_element *el2;
+
+ el2 = malloc_array_p(struct ldb_message_element, msg->num_elements);
+ if (!el2) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ for (i=0;i<msg->num_elements;i++) {
+ if (strcmp(msg->elements[i].name, name) != 0) {
+ el2[count++] = msg->elements[i];
+ } else {
+ if (msg->elements[i].values) free(msg->elements[i].values);
+ }
+ }
+
+ msg->num_elements = count;
+ if (msg->elements) free(msg->elements);
+ msg->elements = el2;
+
+ return 0;
}
+/*
+ delete all elements matching an attribute name/value
+
+ return 0 on success, -1 on failure
+*/
+static int msg_delete_element(struct ldb_message *msg,
+ const char *name,
+ const struct ldb_val *val)
+{
+ int i;
+ struct ldb_message_element *el;
+
+ i = find_element(msg, name);
+ if (i == -1) {
+ return -1;
+ }
+
+ el = &msg->elements[i];
+
+ for (i=0;i<el->num_values;i++) {
+ if (ldb_val_equal(&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));
+ }
+ el->num_values--;
+ return 0;
+ }
+ }
+
+ return -1;
+}
/*
modify a record
+
+ 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)
{
struct ltdb_private *ltdb = ldb->private;
TDB_DATA tdb_key, tdb_data;
struct ldb_message msg2;
- int ret;
+ int ret, i, j;
+
+ if (ltdb_lock(ldb) != 0) {
+ return -1;
+ }
tdb_key = ltdb_key(msg->dn);
if (!tdb_key.dptr) {
- return -1;
+ goto unlock_fail;
}
tdb_data = tdb_fetch(ltdb->tdb, tdb_key);
if (!tdb_data.dptr) {
free(tdb_key.dptr);
- return -1;
+ goto unlock_fail;
}
ret = ltdb_unpack_data(ldb, &tdb_data, &msg2);
if (ret == -1) {
free(tdb_key.dptr);
free(tdb_data.dptr);
- return -1;
+ goto unlock_fail;
}
-#if 0
+ msg2.dn = msg->dn;
+
for (i=0;i<msg->num_elements;i++) {
switch (msg->elements[i].flags & LDB_FLAG_MOD_MASK) {
+
case LDB_FLAG_MOD_ADD:
+ /* add this element to the message. fail if it
+ already exists */
ret = find_element(&msg2, msg->elements[i].name);
if (ret != -1) {
errno = EEXIST;
goto failed;
}
-
+ if (msg_add_element(&msg2, &msg->elements[i]) != 0) {
+ goto failed;
+ }
+ break;
+
+ case LDB_FLAG_MOD_REPLACE:
+ /* replace all elements of this attribute name with the elements
+ listed */
+ if (msg_delete_attribute(&msg2, msg->elements[i].name) != 0) {
+ goto failed;
+ }
+ /* add the replacement element */
+ if (msg_add_element(&msg2, &msg->elements[i]) != 0) {
+ goto failed;
+ }
+ break;
+
+ case LDB_FLAG_MOD_DELETE:
+ /* we could be being asked to delete all
+ values or just some values */
+ if (msg->elements[i].num_values == 0) {
+ if (msg_delete_attribute(&msg2,
+ msg->elements[i].name) != 0) {
+ goto failed;
+ }
+ 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) {
+ goto failed;
+ }
+ }
+ break;
}
}
-failed:
-#endif
+ /* we've made all the mods - save the modified record back into the database */
+ ret = ltdb_store(ldb, &msg2, TDB_MODIFY);
+
+ 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);
- if (msg2.elements) free(msg2.elements);
+ ltdb_unpack_data_free(&msg2);
+
+unlock_fail:
+ ltdb_unlock(ldb);
return -1;
}