r18781: Move the usnCreated and usnChanged handling around again.
authorAndrew Bartlett <abartlet@samba.org>
Thu, 21 Sep 2006 06:44:12 +0000 (06:44 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 19:19:11 +0000 (14:19 -0500)
This moves these attributes from objectguid into an optional backend
(objectguid), used by ltdb.  For OpenLDAP, the entryUUID module
converts entryCSN into usnChanged.

This also changes the sequence number API, and uses 'time based'
sequence numbers, when an LDAP or similar backend is detected.

To assist this, we also store the last modified time in the TDB,
whenever we change a value.

Andrew Bartlett

12 files changed:
source/dsdb/samdb/ldb_modules/entryUUID.c
source/dsdb/samdb/ldb_modules/objectguid.c
source/dsdb/samdb/ldb_modules/partition.c
source/dsdb/samdb/ldb_modules/rootdse.c
source/lib/ldb/common/ldb.c
source/lib/ldb/include/ldb.h
source/lib/ldb/include/ldb_private.h
source/lib/ldb/ldb_tdb/ldb_cache.c
source/lib/ldb/ldb_tdb/ldb_tdb.c
source/lib/ldb/ldb_tdb/ldb_tdb.h
source/lib/ldb/modules/operational.c
source/script/tests/mktestsetup.sh

index ebe78f9fc4ad5a61a497581beaa422763c2e0d07..04beac7a94885ce2cc1a8eaf85a3c4a8c9ffbfdd 100644 (file)
@@ -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 */
@@ -294,6 +369,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);
 }
index 7e475d1ef44df051315b4e78c9952c03bb4c1d5f..0c4a493adb93ab7544043281336ddeedb2303aed 100644 (file)
@@ -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 */
index bb085e0b11a10f42b75c47edeaed8255f6499b0e..437e288be500e948bfe3891710176b15ed514626 100644 (file)
@@ -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;
 }
 
index c180e2f1b0ffece234399832eefb5ebb70bd7bf8..a8bc3fbdc278b83effb2265226f60abdb0ba9f62 100644 (file)
@@ -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) {
index a68733244d9bd7c9d00d8de780743df56f8babe8..1aae76bad61607326b429c20e7b88bee022d707c 100644 (file)
@@ -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);
        
index 78d701689d55c5c6437fa79d3a04572c81dd2483..cf4a1f282bbd01401de867c36866d9eaac59b2d6 100644 (file)
@@ -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 *);
@@ -922,11 +928,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
 */
index d66b6f1cc73a8c3fe3aeb2e9848bd4ce2caa74ba..96b71ff3b470c5ca5f9d13bd8b1a15a9bb9e2155 100644 (file)
@@ -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
index 5634e9ad16328f14ba486bcac33e91ebb7ffe07c..d6d66dd37fd83e206d16bcf32c728bfbafb4840c 100644 (file)
@@ -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);
index 5a19dd96fc9e042b1f7d4b45f9ca55d152f2ac65..8f676654a6540c635c060f3060f86ffe610170dc 100644 (file)
@@ -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;
 }
index fb28d00847d83f0bfac6e0bbc83702411553bea0..7b98b9ddeeced521e2b5138d58a6ca264a5c8e74 100644 (file)
@@ -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  */
index 1e405355c4a1b868c3491ff7f8656281ae23e3a7..c327a96f90b0ef67b78fe67f6874ab992f941446 100644 (file)
@@ -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
 };
 
index 4f830033584f5f2710ca0172d1eed8ea245f1ac8..9a4528379fcd99d78c76785b4bb21d99d55e5434 100755 (executable)
@@ -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"