diff options
Diffstat (limited to 'source4')
-rw-r--r-- | source4/dsdb/samdb/ldb_modules/entryUUID.c | 262 | ||||
-rw-r--r-- | source4/dsdb/samdb/ldb_modules/objectguid.c | 45 | ||||
-rw-r--r-- | source4/dsdb/samdb/ldb_modules/partition.c | 105 | ||||
-rw-r--r-- | source4/dsdb/samdb/ldb_modules/rootdse.c | 2 | ||||
-rw-r--r-- | source4/lib/ldb/common/ldb.c | 3 | ||||
-rw-r--r-- | source4/lib/ldb/include/ldb.h | 11 | ||||
-rw-r--r-- | source4/lib/ldb/include/ldb_private.h | 10 | ||||
-rw-r--r-- | source4/lib/ldb/ldb_tdb/ldb_cache.c | 38 | ||||
-rw-r--r-- | source4/lib/ldb/ldb_tdb/ldb_tdb.c | 23 | ||||
-rw-r--r-- | source4/lib/ldb/ldb_tdb/ldb_tdb.h | 3 | ||||
-rw-r--r-- | source4/lib/ldb/modules/operational.c | 127 | ||||
-rwxr-xr-x | source4/script/tests/mktestsetup.sh | 4 |
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 = ⪙ + 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" |