summaryrefslogtreecommitdiff
path: root/source4
diff options
context:
space:
mode:
Diffstat (limited to 'source4')
-rw-r--r--source4/dsdb/samdb/ldb_modules/entryUUID.c262
-rw-r--r--source4/dsdb/samdb/ldb_modules/objectguid.c45
-rw-r--r--source4/dsdb/samdb/ldb_modules/partition.c105
-rw-r--r--source4/dsdb/samdb/ldb_modules/rootdse.c2
-rw-r--r--source4/lib/ldb/common/ldb.c3
-rw-r--r--source4/lib/ldb/include/ldb.h11
-rw-r--r--source4/lib/ldb/include/ldb_private.h10
-rw-r--r--source4/lib/ldb/ldb_tdb/ldb_cache.c38
-rw-r--r--source4/lib/ldb/ldb_tdb/ldb_tdb.c23
-rw-r--r--source4/lib/ldb/ldb_tdb/ldb_tdb.h3
-rw-r--r--source4/lib/ldb/modules/operational.c127
-rwxr-xr-xsource4/script/tests/mktestsetup.sh4
12 files changed, 471 insertions, 162 deletions
diff --git a/source4/dsdb/samdb/ldb_modules/entryUUID.c b/source4/dsdb/samdb/ldb_modules/entryUUID.c
index ebe78f9fc4..04beac7a94 100644
--- a/source4/dsdb/samdb/ldb_modules/entryUUID.c
+++ b/source4/dsdb/samdb/ldb_modules/entryUUID.c
@@ -38,6 +38,7 @@
struct entryUUID_private {
struct ldb_result *objectclass_res;
+ struct ldb_dn **base_dns;
};
static struct ldb_val encode_guid(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
@@ -188,6 +189,80 @@ static struct ldb_val normalise_to_signed32(struct ldb_module *module, TALLOC_CT
return val_copy(module, ctx, val);
}
+static struct ldb_val usn_to_entryCSN(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
+{
+ struct ldb_val out;
+ unsigned long long usn = strtoull(val->data, NULL, 10);
+ time_t t = (usn >> 24);
+ out = data_blob_string_const(talloc_asprintf(ctx, "%s#%06x#00#000000", ldb_timestring(ctx, t), (unsigned int)(usn & 0xFFFFFF)));
+ return out;
+}
+
+static unsigned long long entryCSN_to_usn_int(TALLOC_CTX *ctx, const struct ldb_val *val)
+{
+ char *entryCSN = talloc_strdup(ctx, val->data);
+ char *mod_per_sec;
+ time_t t;
+ unsigned long long usn;
+ char *p;
+ if (!entryCSN) {
+ return 0;
+ }
+ p = strchr(entryCSN, '#');
+ if (!p) {
+ return 0;
+ }
+ p[0] = '\0';
+ p++;
+ mod_per_sec = p;
+
+ p = strchr(p, '#');
+ if (!p) {
+ return 0;
+ }
+ p[0] = '\0';
+ p++;
+
+ usn = strtol(mod_per_sec, NULL, 16);
+
+ t = ldb_string_to_time(entryCSN);
+
+ usn = usn | ((unsigned long long)t <<24);
+ return usn;
+}
+
+static struct ldb_val entryCSN_to_usn(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
+{
+ struct ldb_val out;
+ unsigned long long usn = entryCSN_to_usn_int(ctx, val);
+ out = data_blob_string_const(talloc_asprintf(ctx, "%lld", usn));
+ return out;
+}
+
+static struct ldb_val usn_to_timestamp(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
+{
+ struct ldb_val out;
+ unsigned long long usn = strtoull(val->data, NULL, 10);
+ time_t t = (usn >> 24);
+ out = data_blob_string_const(ldb_timestring(ctx, t));
+ return out;
+}
+
+static struct ldb_val timestamp_to_usn(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
+{
+ struct ldb_val out;
+ time_t t;
+ unsigned long long usn;
+
+ t = ldb_string_to_time(val->data);
+
+ usn = ((unsigned long long)t <<24);
+
+ out = data_blob_string_const(talloc_asprintf(ctx, "%lld", usn));
+ return out;
+}
+
+
const struct ldb_map_attribute entryUUID_attributes[] =
{
/* objectGUID */
@@ -295,6 +370,28 @@ const struct ldb_map_attribute entryUUID_attributes[] =
}
},
{
+ .local_name = "usnChanged",
+ .type = MAP_CONVERT,
+ .u = {
+ .convert = {
+ .remote_name = "entryCSN",
+ .convert_local = usn_to_entryCSN,
+ .convert_remote = entryCSN_to_usn
+ },
+ },
+ },
+ {
+ .local_name = "usnCreated",
+ .type = MAP_CONVERT,
+ .u = {
+ .convert = {
+ .remote_name = "createTimestamp",
+ .convert_local = usn_to_timestamp,
+ .convert_remote = timestamp_to_usn,
+ },
+ },
+ },
+ {
.local_name = "*",
.type = MAP_KEEP,
},
@@ -309,6 +406,8 @@ const char * const wildcard_attributes[] = {
"objectGUID",
"whenCreated",
"whenChanged",
+ "usnCreated",
+ "usnChanged",
NULL
};
@@ -373,6 +472,75 @@ static int fetch_objectclass_schema(struct ldb_context *ldb, struct ldb_dn *sche
return ret;
}
+
+static int get_remote_rootdse(struct ldb_context *ldb, void *context,
+ struct ldb_reply *ares)
+{
+ struct entryUUID_private *entryUUID_private;
+ entryUUID_private = talloc_get_type(context,
+ struct entryUUID_private);
+ if (ares->type == LDB_REPLY_ENTRY) {
+ int i;
+ struct ldb_message_element *el = ldb_msg_find_element(ares->message, "namingContexts");
+ entryUUID_private->base_dns = talloc_realloc(entryUUID_private, entryUUID_private->base_dns, struct ldb_dn *,
+ el->num_values + 1);
+ for (i=0; i < el->num_values; i++) {
+ if (!entryUUID_private->base_dns) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ entryUUID_private->base_dns[i] = ldb_dn_explode(entryUUID_private->base_dns, (const char *)el->values[i].data);
+ if (!entryUUID_private->base_dns[i]) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ }
+ entryUUID_private->base_dns[i] = NULL;
+ }
+}
+
+static int find_base_dns(struct ldb_module *module,
+ struct entryUUID_private *entryUUID_private)
+{
+ int ret;
+ struct ldb_request *req;
+ const char *naming_context_attr[] = {
+ "namingContexts",
+ NULL
+ };
+ req = talloc(module, struct ldb_request);
+ if (req == NULL) {
+ ldb_set_errstring(module->ldb, "Out of Memory");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ req->operation = LDB_SEARCH;
+ req->op.search.base = ldb_dn_new(req);
+ req->op.search.scope = LDB_SCOPE_BASE;
+
+ req->op.search.tree = ldb_parse_tree(req, "objectClass=*");
+ if (req->op.search.tree == NULL) {
+ ldb_set_errstring(module->ldb, "Unable to parse search expression");
+ talloc_free(req);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ req->op.search.attrs = naming_context_attr;
+ req->controls = NULL;
+ req->context = entryUUID_private;
+ req->callback = get_remote_rootdse;
+ ldb_set_timeout(module->ldb, req, 0); /* use default timeout */
+
+ ret = ldb_next_request(module, req);
+
+ if (ret == LDB_SUCCESS) {
+ ret = ldb_wait(req->handle, LDB_WAIT_ALL);
+ }
+
+ talloc_free(req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+}
+
/* the context init function */
static int entryUUID_init(struct ldb_module *module)
{
@@ -402,13 +570,104 @@ static int entryUUID_init(struct ldb_module *module)
ldb_asprintf_errstring(module->ldb, "Failed to fetch objectClass schema elements: %s\n", ldb_errstring(module->ldb));
return ret;
}
-
+
+ ret = find_base_dns(module, entryUUID_private);
+
return ldb_next_init(module);
}
+static int get_seq(struct ldb_context *ldb, void *context,
+ struct ldb_reply *ares)
+{
+ unsigned long long *max_seq = context;
+ unsigned long long seq;
+ if (ares->type == LDB_REPLY_ENTRY) {
+ struct ldb_message_element *el = ldb_msg_find_element(ares->message, "contextCSN");
+ if (el) {
+ seq = entryCSN_to_usn_int(ares, &el->values[0]);
+ *max_seq = MAX(seq, *max_seq);
+ }
+ }
+}
+
+static int entryUUID_sequence_number(struct ldb_module *module, struct ldb_request *req)
+{
+ int i, ret;
+ struct map_private *map_private;
+ struct entryUUID_private *entryUUID_private;
+ unsigned long long max_seq = 0;
+ struct ldb_request *search_req;
+ map_private = talloc_get_type(module->private_data, struct map_private);
+
+ entryUUID_private = talloc_get_type(map_private->caller_private, struct entryUUID_private);
+
+ /* Search the baseDNs for a sequence number */
+ for (i=0; entryUUID_private &&
+ entryUUID_private->base_dns &&
+ entryUUID_private->base_dns[i];
+ i++) {
+ static const char *contextCSN_attr[] = {
+ "contextCSN", NULL
+ };
+ search_req = talloc(req, struct ldb_request);
+ if (search_req == NULL) {
+ ldb_set_errstring(module->ldb, "Out of Memory");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ search_req->operation = LDB_SEARCH;
+ search_req->op.search.base = entryUUID_private->base_dns[i];
+ search_req->op.search.scope = LDB_SCOPE_BASE;
+
+ search_req->op.search.tree = ldb_parse_tree(search_req, "objectClass=*");
+ if (search_req->op.search.tree == NULL) {
+ ldb_set_errstring(module->ldb, "Unable to parse search expression");
+ talloc_free(search_req);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ search_req->op.search.attrs = contextCSN_attr;
+ search_req->controls = NULL;
+ search_req->context = &max_seq;
+ search_req->callback = get_seq;
+ ldb_set_timeout(module->ldb, search_req, 0); /* use default timeout */
+
+ ret = ldb_next_request(module, search_req);
+
+ if (ret == LDB_SUCCESS) {
+ ret = ldb_wait(search_req->handle, LDB_WAIT_ALL);
+ }
+
+ talloc_free(search_req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+
+ switch (req->op.seq_num.type) {
+ case LDB_SEQ_HIGHEST_SEQ:
+ req->op.seq_num.seq_num = max_seq;
+ break;
+ case LDB_SEQ_NEXT:
+ req->op.seq_num.seq_num = max_seq;
+ req->op.seq_num.seq_num++;
+ break;
+ case LDB_SEQ_HIGHEST_TIMESTAMP:
+ {
+ req->op.seq_num.seq_num = (max_seq >> 24);
+ break;
+ }
+ }
+ req->op.seq_num.flags = 0;
+ req->op.seq_num.flags |= LDB_SEQ_TIMESTAMP_SEQUENCE;
+ req->op.seq_num.flags |= LDB_SEQ_GLOBAL_SEQUENCE;
+ return LDB_SUCCESS;
+}
+
static struct ldb_module_ops entryUUID_ops = {
.name = "entryUUID",
.init_context = entryUUID_init,
+ .sequence_number = entryUUID_sequence_number
};
/* the init function */
@@ -421,6 +680,5 @@ int ldb_entryUUID_module_init(void)
entryUUID_ops.rename = ops.rename;
entryUUID_ops.search = ops.search;
entryUUID_ops.wait = ops.wait;
-
return ldb_register_module(&entryUUID_ops);
}
diff --git a/source4/dsdb/samdb/ldb_modules/objectguid.c b/source4/dsdb/samdb/ldb_modules/objectguid.c
index 7e475d1ef4..0c4a493adb 100644
--- a/source4/dsdb/samdb/ldb_modules/objectguid.c
+++ b/source4/dsdb/samdb/ldb_modules/objectguid.c
@@ -3,6 +3,7 @@
Copyright (C) Simo Sorce 2004-2006
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
+ Copyright (C) Andrew Tridgell 2005
** NOTE! The following LGPL license applies to the ldb
** library. This does NOT imply that all of Samba is released
@@ -79,6 +80,29 @@ static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
return 0;
}
+/*
+ add a uint64_t element to a record
+*/
+static int add_uint64_element(struct ldb_message *msg, const char *attr, uint64_t v)
+{
+ struct ldb_message_element *el;
+
+ if (ldb_msg_find_element(msg, attr) != NULL) {
+ return 0;
+ }
+
+ if (ldb_msg_add_fmt(msg, attr, "%llu", (unsigned long long)v) != 0) {
+ return -1;
+ }
+
+ el = ldb_msg_find_element(msg, attr);
+ /* always set as replace. This works because on add ops, the flag
+ is ignored */
+ el->flags = LDB_FLAG_MOD_REPLACE;
+
+ return 0;
+}
+
/* add_record: add objectGUID attribute */
static int objectguid_add(struct ldb_module *module, struct ldb_request *req)
{
@@ -87,6 +111,7 @@ static int objectguid_add(struct ldb_module *module, struct ldb_request *req)
struct ldb_message *msg;
struct ldb_val v;
struct GUID guid;
+ uint64_t seq_num;
NTSTATUS nt_status;
int ret;
time_t t = time(NULL);
@@ -138,6 +163,16 @@ static int objectguid_add(struct ldb_module *module, struct ldb_request *req)
return LDB_ERR_OPERATIONS_ERROR;
}
+ /* Get a sequence number from the backend */
+ ret = ldb_sequence_number(module->ldb, LDB_SEQ_NEXT, &seq_num);
+ if (ret == LDB_SUCCESS) {
+ if (add_uint64_element(msg, "uSNCreated", seq_num) != 0 ||
+ add_uint64_element(msg, "uSNChanged", seq_num) != 0) {
+ talloc_free(down_req);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ }
+
ldb_set_timeout_from_prev_req(module->ldb, req, down_req);
/* go on with the call chain */
@@ -159,6 +194,7 @@ static int objectguid_modify(struct ldb_module *module, struct ldb_request *req)
struct ldb_message *msg;
int ret;
time_t t = time(NULL);
+ uint64_t seq_num;
ldb_debug(module->ldb, LDB_DEBUG_TRACE, "objectguid_add_record\n");
@@ -186,6 +222,15 @@ static int objectguid_modify(struct ldb_module *module, struct ldb_request *req)
return LDB_ERR_OPERATIONS_ERROR;
}
+ /* Get a sequence number from the backend */
+ ret = ldb_sequence_number(module->ldb, LDB_SEQ_NEXT, &seq_num);
+ if (ret == LDB_SUCCESS) {
+ if (add_uint64_element(msg, "uSNChanged", seq_num) != 0) {
+ talloc_free(down_req);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ }
+
ldb_set_timeout_from_prev_req(module->ldb, req, down_req);
/* go on with the call chain */
diff --git a/source4/dsdb/samdb/ldb_modules/partition.c b/source4/dsdb/samdb/ldb_modules/partition.c
index bb085e0b11..437e288be5 100644
--- a/source4/dsdb/samdb/ldb_modules/partition.c
+++ b/source4/dsdb/samdb/ldb_modules/partition.c
@@ -474,28 +474,103 @@ static int partition_sequence_number(struct ldb_module *module, struct ldb_reque
{
int i, ret;
uint64_t seq_number = 0;
+ uint64_t timestamp_sequence = 0;
+ uint64_t timestamp = 0;
struct partition_private_data *data = talloc_get_type(module->private_data,
struct partition_private_data);
- ret = ldb_next_request(module, req);
- if (ret != LDB_SUCCESS) {
- return ret;
- }
- seq_number = seq_number + req->op.seq_num.seq_num;
- /* Look at base DN */
- /* Figure out which partition it is under */
- /* Skip the lot if 'data' isn't here yet (initialistion) */
- for (i=0; data && data->partitions && data->partitions[i]; i++) {
- struct ldb_module *next = make_module_for_next_request(req, module->ldb, data->partitions[i]->module);
-
- ret = ldb_next_request(next, req);
- talloc_free(next);
+ switch (req->op.seq_num.type) {
+ case LDB_SEQ_NEXT:
+ case LDB_SEQ_HIGHEST_SEQ:
+ ret = ldb_next_request(module, req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ if (req->op.seq_num.flags & LDB_SEQ_TIMESTAMP_SEQUENCE) {
+ timestamp_sequence = req->op.seq_num.seq_num;
+ } else {
+ seq_number = seq_number + req->op.seq_num.seq_num;
+ }
+
+ /* Look at base DN */
+ /* Figure out which partition it is under */
+ /* Skip the lot if 'data' isn't here yet (initialistion) */
+ for (i=0; data && data->partitions && data->partitions[i]; i++) {
+ struct ldb_module *next = make_module_for_next_request(req, module->ldb, data->partitions[i]->module);
+
+ ret = ldb_next_request(next, req);
+ talloc_free(next);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ if (req->op.seq_num.flags & LDB_SEQ_TIMESTAMP_SEQUENCE) {
+ timestamp_sequence = MAX(timestamp_sequence, req->op.seq_num.seq_num);
+ } else {
+ seq_number = seq_number + req->op.seq_num.seq_num;
+ }
+ }
+ /* fall though */
+ case LDB_SEQ_HIGHEST_TIMESTAMP:
+ {
+ struct ldb_request *date_req = talloc(req, struct ldb_request);
+ if (!date_req) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ *date_req = *req;
+ date_req->op.seq_num.flags = LDB_SEQ_HIGHEST_TIMESTAMP;
+
+ ret = ldb_next_request(module, date_req);
if (ret != LDB_SUCCESS) {
return ret;
}
- seq_number = seq_number + req->op.seq_num.seq_num;
+ timestamp = date_req->op.seq_num.seq_num;
+
+ /* Look at base DN */
+ /* Figure out which partition it is under */
+ /* Skip the lot if 'data' isn't here yet (initialistion) */
+ for (i=0; data && data->partitions && data->partitions[i]; i++) {
+ struct ldb_module *next = make_module_for_next_request(req, module->ldb, data->partitions[i]->module);
+
+ ret = ldb_next_request(next, date_req);
+ talloc_free(next);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ timestamp = MAX(timestamp, date_req->op.seq_num.seq_num);
+ }
+ break;
+ }
+ }
+
+ switch (req->op.seq_num.flags) {
+ case LDB_SEQ_NEXT:
+ case LDB_SEQ_HIGHEST_SEQ:
+
+ req->op.seq_num.flags = 0;
+
+ /* Has someone above set a timebase sequence? */
+ if (timestamp_sequence) {
+ req->op.seq_num.seq_num = (((unsigned long long)timestamp << 24) | (seq_number & 0xFFFFFF));
+ } else {
+ req->op.seq_num.seq_num = seq_number;
+ }
+
+ if (timestamp_sequence > req->op.seq_num.seq_num) {
+ req->op.seq_num.seq_num = timestamp_sequence;
+ req->op.seq_num.flags |= LDB_SEQ_TIMESTAMP_SEQUENCE;
+ }
+
+ req->op.seq_num.flags |= LDB_SEQ_GLOBAL_SEQUENCE;
+ break;
+ case LDB_SEQ_HIGHEST_TIMESTAMP:
+ req->op.seq_num.seq_num = timestamp;
+ break;
+ }
+
+ switch (req->op.seq_num.flags) {
+ case LDB_SEQ_NEXT:
+ req->op.seq_num.seq_num++;
}
- req->op.seq_num.seq_num = seq_number;
return LDB_SUCCESS;
}
diff --git a/source4/dsdb/samdb/ldb_modules/rootdse.c b/source4/dsdb/samdb/ldb_modules/rootdse.c
index c180e2f1b0..a8bc3fbdc2 100644
--- a/source4/dsdb/samdb/ldb_modules/rootdse.c
+++ b/source4/dsdb/samdb/ldb_modules/rootdse.c
@@ -118,7 +118,7 @@ static int rootdse_add_dynamic(struct ldb_module *module, struct ldb_message *ms
if (do_attribute(attrs, "highestCommittedUSN")) {
uint64_t seq_num;
- int ret = ldb_sequence_number(module->ldb, &seq_num);
+ int ret = ldb_sequence_number(module->ldb, LDB_SEQ_HIGHEST_SEQ, &seq_num);
if (ret == LDB_SUCCESS) {
if (ldb_msg_add_fmt(msg, "highestCommittedUSN",
"%llu", (unsigned long long)seq_num) != 0) {
diff --git a/source4/lib/ldb/common/ldb.c b/source4/lib/ldb/common/ldb.c
index a68733244d..1aae76bad6 100644
--- a/source4/lib/ldb/common/ldb.c
+++ b/source4/lib/ldb/common/ldb.c
@@ -888,7 +888,7 @@ int ldb_rename(struct ldb_context *ldb, const struct ldb_dn *olddn, const struct
/*
return the global sequence number
*/
-int ldb_sequence_number(struct ldb_context *ldb, uint64_t *seq_num)
+int ldb_sequence_number(struct ldb_context *ldb, enum ldb_sequence_type type, uint64_t *seq_num)
{
struct ldb_request *req;
int ret;
@@ -905,6 +905,7 @@ int ldb_sequence_number(struct ldb_context *ldb, uint64_t *seq_num)
req->callback = NULL;
ldb_set_timeout(ldb, req, 0); /* use default timeout */
+ req->op.seq_num.type = type;
/* do request and autostart a transaction */
ret = ldb_request(ldb, req);
diff --git a/source4/lib/ldb/include/ldb.h b/source4/lib/ldb/include/ldb.h
index 78d701689d..cf4a1f282b 100644
--- a/source4/lib/ldb/include/ldb.h
+++ b/source4/lib/ldb/include/ldb.h
@@ -740,7 +740,13 @@ struct ldb_register_partition {
};
struct ldb_sequence_number {
+ enum ldb_sequence_type {
+ LDB_SEQ_HIGHEST_SEQ,
+ LDB_SEQ_HIGHEST_TIMESTAMP,
+ LDB_SEQ_NEXT
+ } type;
uint64_t seq_num;
+ uint32_t flags;
};
typedef int (*ldb_request_callback_t)(struct ldb_context *, void *, struct ldb_reply *);
@@ -923,11 +929,6 @@ int ldb_rename(struct ldb_context *ldb, const struct ldb_dn *olddn, const struct
int ldb_delete(struct ldb_context *ldb, const struct ldb_dn *dn);
/**
- Obtain current database sequence number
-*/
-int ldb_sequence_number(struct ldb_context *ldb, uint64_t *seq_num);
-
-/**
start a transaction
*/
int ldb_transaction_start(struct ldb_context *ldb);
diff --git a/source4/lib/ldb/include/ldb_private.h b/source4/lib/ldb/include/ldb_private.h
index d66b6f1cc7..96b71ff3b4 100644
--- a/source4/lib/ldb/include/ldb_private.h
+++ b/source4/lib/ldb/include/ldb_private.h
@@ -207,4 +207,14 @@ int check_critical_controls(struct ldb_control **controls);
/* The following definitions come from lib/ldb/common/ldb_utf8.c */
char *ldb_casefold_default(void *context, void *mem_ctx, const char *s);
+
+/**
+ Obtain current/next database sequence number
+*/
+int ldb_sequence_number(struct ldb_context *ldb, enum ldb_sequence_type type, uint64_t *seq_num);
+
+#define LDB_SEQ_GLOBAL_SEQUENCE 0x01
+#define LDB_SEQ_TIMESTAMP_SEQUENCE 0x02
+
+
#endif
diff --git a/source4/lib/ldb/ldb_tdb/ldb_cache.c b/source4/lib/ldb/ldb_tdb/ldb_cache.c
index 5634e9ad16..d6d66dd37f 100644
--- a/source4/lib/ldb/ldb_tdb/ldb_cache.c
+++ b/source4/lib/ldb/ldb_tdb/ldb_cache.c
@@ -413,8 +413,10 @@ int ltdb_increase_sequence_number(struct ldb_module *module)
{
struct ltdb_private *ltdb = module->private_data;
struct ldb_message *msg;
- struct ldb_message_element el;
+ struct ldb_message_element el[2];
struct ldb_val val;
+ struct ldb_val val_time;
+ time_t t = time(NULL);
char *s = NULL;
int ret;
@@ -424,32 +426,50 @@ int ltdb_increase_sequence_number(struct ldb_module *module)
return -1;
}
- s = talloc_asprintf(msg, "%.0f", ltdb->sequence_number+1);
+ s = talloc_asprintf(msg, "%llu", ltdb->sequence_number+1);
if (!s) {
errno = ENOMEM;
return -1;
}
- msg->num_elements = 1;
- msg->elements = &el;
+ msg->num_elements = ARRAY_SIZE(el);
+ msg->elements = el;
msg->dn = ldb_dn_explode(msg, LTDB_BASEINFO);
if (msg->dn == NULL) {
talloc_free(msg);
errno = ENOMEM;
return -1;
}
- el.name = talloc_strdup(msg, LTDB_SEQUENCE_NUMBER);
- if (el.name == NULL) {
+ el[0].name = talloc_strdup(msg, LTDB_SEQUENCE_NUMBER);
+ if (el[0].name == NULL) {
talloc_free(msg);
errno = ENOMEM;
return -1;
}
- el.values = &val;
- el.num_values = 1;
- el.flags = LDB_FLAG_MOD_REPLACE;
+ el[0].values = &val;
+ el[0].num_values = 1;
+ el[0].flags = LDB_FLAG_MOD_REPLACE;
val.data = (uint8_t *)s;
val.length = strlen(s);
+ el[1].name = talloc_strdup(msg, LTDB_MOD_TIMESTAMP);
+ if (el[1].name == NULL) {
+ talloc_free(msg);
+ errno = ENOMEM;
+ return -1;
+ }
+ el[1].values = &val_time;
+ el[1].num_values = 1;
+ el[1].flags = LDB_FLAG_MOD_REPLACE;
+
+ s = ldb_timestring(msg, t);
+ if (s == NULL) {
+ return -1;
+ }
+
+ val_time.data = (uint8_t *)s;
+ val_time.length = strlen(s);
+
ret = ltdb_modify_internal(module, msg);
talloc_free(msg);
diff --git a/source4/lib/ldb/ldb_tdb/ldb_tdb.c b/source4/lib/ldb/ldb_tdb/ldb_tdb.c
index 5a19dd96fc..8f676654a6 100644
--- a/source4/lib/ldb/ldb_tdb/ldb_tdb.c
+++ b/source4/lib/ldb/ldb_tdb/ldb_tdb.c
@@ -944,6 +944,8 @@ static int ltdb_sequence_number(struct ldb_module *module, struct ldb_request *r
return LDB_ERR_OPERATIONS_ERROR;
}
+ req->op.seq_num.flags = 0;
+
tret = ltdb_search_dn1(module, dn, msg);
if (tret != 1) {
talloc_free(tmp_ctx);
@@ -952,7 +954,26 @@ static int ltdb_sequence_number(struct ldb_module *module, struct ldb_request *r
return LDB_SUCCESS;
}
- req->op.seq_num.seq_num = ldb_msg_find_attr_as_uint64(msg, LTDB_SEQUENCE_NUMBER, 0);
+ switch (req->op.seq_num.type) {
+ case LDB_SEQ_HIGHEST_SEQ:
+ req->op.seq_num.seq_num = ldb_msg_find_attr_as_uint64(msg, LTDB_SEQUENCE_NUMBER, 0);
+ break;
+ case LDB_SEQ_NEXT:
+ req->op.seq_num.seq_num = ldb_msg_find_attr_as_uint64(msg, LTDB_SEQUENCE_NUMBER, 0);
+ req->op.seq_num.seq_num++;
+ break;
+ case LDB_SEQ_HIGHEST_TIMESTAMP:
+ {
+ const char *date = ldb_msg_find_attr_as_string(msg, LTDB_MOD_TIMESTAMP, NULL);
+ if (date) {
+ req->op.seq_num.seq_num = ldb_string_to_time(date);
+ } else {
+ req->op.seq_num.seq_num = 0;
+ /* zero is as good as anything when we don't know */
+ }
+ break;
+ }
+ }
talloc_free(tmp_ctx);
return LDB_SUCCESS;
}
diff --git a/source4/lib/ldb/ldb_tdb/ldb_tdb.h b/source4/lib/ldb/ldb_tdb/ldb_tdb.h
index fb28d00847..7b98b9ddee 100644
--- a/source4/lib/ldb/ldb_tdb/ldb_tdb.h
+++ b/source4/lib/ldb/ldb_tdb/ldb_tdb.h
@@ -14,7 +14,7 @@ struct ltdb_private {
/* a double is used for portability and ease of string
handling. It has plenty of digits of precision */
- double sequence_number;
+ unsigned long long sequence_number;
struct ltdb_cache {
struct ldb_message *baseinfo;
@@ -58,6 +58,7 @@ struct ltdb_context {
/* special attribute types */
#define LTDB_SEQUENCE_NUMBER "sequenceNumber"
+#define LTDB_MOD_TIMESTAMP "whenChanged"
#define LTDB_OBJECTCLASS "objectClass"
/* The following definitions come from lib/ldb/ldb_tdb/ldb_cache.c */
diff --git a/source4/lib/ldb/modules/operational.c b/source4/lib/ldb/modules/operational.c
index 1e405355c4..c327a96f90 100644
--- a/source4/lib/ldb/modules/operational.c
+++ b/source4/lib/ldb/modules/operational.c
@@ -169,29 +169,6 @@ failed:
return -1;
}
-/*
- add a uint64_t element to a record
-*/
-static int add_uint64_element(struct ldb_message *msg, const char *attr, uint64_t v)
-{
- struct ldb_message_element *el;
-
- if (ldb_msg_find_element(msg, attr) != NULL) {
- return 0;
- }
-
- if (ldb_msg_add_fmt(msg, attr, "%llu", (unsigned long long)v) != 0) {
- return -1;
- }
-
- el = ldb_msg_find_element(msg, attr);
- /* always set as replace. This works because on add ops, the flag
- is ignored */
- el->flags = LDB_FLAG_MOD_REPLACE;
-
- return 0;
-}
-
/*
hook search operations
@@ -312,108 +289,6 @@ static int operational_search(struct ldb_module *module, struct ldb_request *req
return ret;
}
-/*
- hook add record ops
-*/
-static int operational_add(struct ldb_module *module, struct ldb_request *req)
-{
- struct ldb_request *down_req;
- struct ldb_message *msg;
- uint64_t seq_num;
- int ret;
-
- if (ldb_dn_is_special(req->op.add.message->dn)) {
- return ldb_next_request(module, req);
- }
-
- down_req = talloc(req, struct ldb_request);
- if (down_req == NULL) {
- return LDB_ERR_OPERATIONS_ERROR;
- }
-
- *down_req = *req;
-
- /* we have to copy the message as the caller might have it as a const */
- down_req->op.mod.message = msg = ldb_msg_copy_shallow(down_req, req->op.mod.message);
- if (msg == NULL) {
- return LDB_ERR_OPERATIONS_ERROR;
- }
-
- /* Get a sequence number from the backend */
- ret = ldb_sequence_number(module->ldb, &seq_num);
- if (ret == LDB_SUCCESS) {
- if (add_uint64_element(msg, "uSNCreated", seq_num) != 0 ||
- add_uint64_element(msg, "uSNChanged", seq_num) != 0) {
- talloc_free(down_req);
- return LDB_ERR_OPERATIONS_ERROR;
- }
- }
-
- ldb_set_timeout_from_prev_req(module->ldb, req, down_req);
-
- /* go on with the call chain */
- ret = ldb_next_request(module, down_req);
-
- /* do not free down_req as the call results may be linked to it,
- * it will be freed when the upper level request get freed */
- if (ret == LDB_SUCCESS) {
- req->handle = down_req->handle;
- }
-
- return ret;
-}
-
-/*
- hook modify record ops
-*/
-static int operational_modify(struct ldb_module *module, struct ldb_request *req)
-{
- struct ldb_request *down_req;
- struct ldb_message *msg;
- uint64_t seq_num;
- int ret;
-
- if (ldb_dn_is_special(req->op.mod.message->dn)) {
- return ldb_next_request(module, req);
- }
-
- down_req = talloc(req, struct ldb_request);
- if (down_req == NULL) {
- return LDB_ERR_OPERATIONS_ERROR;
- }
-
- *down_req = *req;
-
- /* we have to copy the message as the caller might have it as a const */
- down_req->op.mod.message = msg = ldb_msg_copy_shallow(down_req, req->op.mod.message);
- if (msg == NULL) {
- return LDB_ERR_OPERATIONS_ERROR;
- }
- /* Get a sequence number from the backend */
- ret = ldb_sequence_number(module->ldb, &seq_num);
- if (ret == LDB_SUCCESS) {
- /* update the records USN if possible */
- if (add_uint64_element(msg, "uSNChanged",
- seq_num) != 0) {
- talloc_free(down_req);
- return -1;
- }
- }
-
- ldb_set_timeout_from_prev_req(module->ldb, req, down_req);
-
- /* go on with the call chain */
- ret = ldb_next_request(module, down_req);
-
- /* do not free down_req as the call results may be linked to it,
- * it will be freed when the upper level request get freed */
- if (ret == LDB_SUCCESS) {
- req->handle = down_req->handle;
- }
-
- return ret;
-}
-
static int operational_init(struct ldb_module *ctx)
{
/* setup some standard attribute handlers */
@@ -428,8 +303,6 @@ static int operational_init(struct ldb_module *ctx)
static const struct ldb_module_ops operational_ops = {
.name = "operational",
.search = operational_search,
- .add = operational_add,
- .modify = operational_modify,
.init_context = operational_init
};
diff --git a/source4/script/tests/mktestsetup.sh b/source4/script/tests/mktestsetup.sh
index 4f83003358..9a4528379f 100755
--- a/source4/script/tests/mktestsetup.sh
+++ b/source4/script/tests/mktestsetup.sh
@@ -328,6 +328,10 @@ directory $LDAPDIR/db
index objectClass eq
index samAccountName eq
+overlay syncprov
+syncprov-checkpoint 100 10
+syncprov-sessionlog 100
+
EOF
PROVISION_OPTIONS="$CONFIGURATION --host-name=$NETBIOSNAME --host-ip=127.0.0.1"