summaryrefslogtreecommitdiff
path: root/lib/ldb/ldb_tdb
diff options
context:
space:
mode:
authorAndrew Bartlett <abartlet@samba.org>2012-07-31 10:17:20 +1000
committerAndrew Bartlett <abartlet@samba.org>2012-07-31 14:15:29 +0200
commit8208d7b2c207c39c3d45f96f85728c233dce126d (patch)
tree541aa4eedef8c6a964d3227ba0f6c2101cb01b8f /lib/ldb/ldb_tdb
parent221cd524e31fce5efa2de179074cfe97bf2c909c (diff)
downloadsamba-8208d7b2c207c39c3d45f96f85728c233dce126d.tar.gz
samba-8208d7b2c207c39c3d45f96f85728c233dce126d.tar.bz2
samba-8208d7b2c207c39c3d45f96f85728c233dce126d.zip
lib/ldb: Ensure rename target does not exist before deleting old record
This is all in a transaction, but when we are handling rename errors in the repl_meta_data module, we key off the error, and do not close the transaction. We found that the old record was gone and so could not try renaming it again to a conflict DN. Andrew Bartlett
Diffstat (limited to 'lib/ldb/ldb_tdb')
-rw-r--r--lib/ldb/ldb_tdb/ldb_tdb.c42
1 files changed, 40 insertions, 2 deletions
diff --git a/lib/ldb/ldb_tdb/ldb_tdb.c b/lib/ldb/ldb_tdb/ldb_tdb.c
index 14ffcf418c..cc1586dc5c 100644
--- a/lib/ldb/ldb_tdb/ldb_tdb.c
+++ b/lib/ldb/ldb_tdb/ldb_tdb.c
@@ -973,9 +973,12 @@ static int ltdb_modify(struct ltdb_context *ctx)
static int ltdb_rename(struct ltdb_context *ctx)
{
struct ldb_module *module = ctx->module;
+ void *data = ldb_module_get_private(module);
+ struct ltdb_private *ltdb = talloc_get_type(data, struct ltdb_private);
struct ldb_request *req = ctx->req;
struct ldb_message *msg;
int ret = LDB_SUCCESS;
+ TDB_DATA tdb_key, tdb_key_old;
ldb_request_set_state(req, LDB_ASYNC_PENDING);
@@ -988,25 +991,58 @@ static int ltdb_rename(struct ltdb_context *ctx)
return LDB_ERR_OPERATIONS_ERROR;
}
- /* in case any attribute of the message was indexed, we need
- to fetch the old record */
+ /* we need to fetch the old record to re-add under the new name */
ret = ltdb_search_dn1(module, req->op.rename.olddn, msg);
if (ret != LDB_SUCCESS) {
/* not finding the old record is an error */
return ret;
}
+ /* We need to, before changing the DB, check if the new DN
+ * exists, so we can return this error to the caller with an
+ * unmodified DB */
+ tdb_key = ltdb_key(module, req->op.rename.newdn);
+ if (!tdb_key.dptr) {
+ talloc_free(msg);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ tdb_key_old = ltdb_key(module, req->op.rename.olddn);
+ if (!tdb_key_old.dptr) {
+ talloc_free(msg);
+ talloc_free(tdb_key.dptr);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* Only declare a conflict if the new DN already exists, and it isn't a case change on the old DN */
+ if (tdb_key_old.dsize != tdb_key.dsize || memcmp(tdb_key.dptr, tdb_key_old.dptr, tdb_key.dsize) != 0) {
+ if (tdb_exists(ltdb->tdb, tdb_key)) {
+ talloc_free(tdb_key_old.dptr);
+ talloc_free(tdb_key.dptr);
+ ldb_asprintf_errstring(ldb_module_get_ctx(module),
+ "Entry %s already exists",
+ ldb_dn_get_linearized(msg->dn));
+ /* finding the new record already in the DB is an error */
+ talloc_free(msg);
+ return LDB_ERR_ENTRY_ALREADY_EXISTS;
+ }
+ }
+ talloc_free(tdb_key_old.dptr);
+ talloc_free(tdb_key.dptr);
+
/* Always delete first then add, to avoid conflicts with
* unique indexes. We rely on the transaction to make this
* atomic
*/
ret = ltdb_delete_internal(module, msg->dn);
if (ret != LDB_SUCCESS) {
+ talloc_free(msg);
return ret;
}
msg->dn = ldb_dn_copy(msg, req->op.rename.newdn);
if (msg->dn == NULL) {
+ talloc_free(msg);
return LDB_ERR_OPERATIONS_ERROR;
}
@@ -1016,6 +1052,8 @@ static int ltdb_rename(struct ltdb_context *ctx)
*/
ret = ltdb_add_internal(module, msg, false);
+ talloc_free(msg);
+
return ret;
}