4 Copyright (C) Simo Sorce 2004-2006
5 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
6 Copyright (C) Andrew Tridgell 2005
7 Copyright (C) Stefan Metzmacher 2007
9 ** NOTE! The following LGPL license applies to the ldb
10 ** library. This does NOT imply that all of Samba is released
13 This library is free software; you can redistribute it and/or
14 modify it under the terms of the GNU Lesser General Public
15 License as published by the Free Software Foundation; either
16 version 2 of the License, or (at your option) any later version.
18 This library is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 Lesser General Public License for more details.
23 You should have received a copy of the GNU Lesser General Public
24 License along with this library; if not, write to the Free Software
25 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
31 * Component: ldb repl_meta_data module
33 * Description: - add a unique objectGUID onto every new record,
34 * - handle whenCreated, whenChanged timestamps
35 * - handle uSNCreated, uSNChanged numbers
36 * - handle replPropertyMetaData attribute
39 * Author: Stefan Metzmacher
43 #include "lib/ldb/include/ldb.h"
44 #include "lib/ldb/include/ldb_errors.h"
45 #include "lib/ldb/include/ldb_private.h"
46 #include "dsdb/samdb/samdb.h"
47 #include "librpc/gen_ndr/ndr_misc.h"
48 #include "librpc/gen_ndr/ndr_drsblobs.h"
50 struct replmd_replicated_request {
51 struct ldb_module *module;
52 struct ldb_handle *handle;
53 struct ldb_request *orig_req;
55 struct dsdb_extended_replicated_objects *objs;
57 uint32_t index_current;
61 struct ldb_request *search_req;
62 struct ldb_message *search_msg;
64 struct ldb_request *change_req;
69 static struct replmd_replicated_request *replmd_replicated_init_handle(struct ldb_module *module,
70 struct ldb_request *req,
71 struct dsdb_extended_replicated_objects *objs)
73 struct replmd_replicated_request *ar;
76 h = talloc_zero(req, struct ldb_handle);
78 ldb_set_errstring(module->ldb, "Out of Memory");
83 h->state = LDB_ASYNC_PENDING;
84 h->status = LDB_SUCCESS;
86 ar = talloc_zero(h, struct replmd_replicated_request);
88 ldb_set_errstring(module->ldb, "Out of Memory");
105 static struct ldb_message_element *replmd_find_attribute(const struct ldb_message *msg, const char *name)
109 for (i = 0; i < msg->num_elements; i++) {
110 if (ldb_attr_cmp(name, msg->elements[i].name) == 0) {
111 return &msg->elements[i];
119 add a time element to a record
121 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
123 struct ldb_message_element *el;
126 if (ldb_msg_find_element(msg, attr) != NULL) {
130 s = ldb_timestring(msg, t);
135 if (ldb_msg_add_string(msg, attr, s) != 0) {
139 el = ldb_msg_find_element(msg, attr);
140 /* always set as replace. This works because on add ops, the flag
142 el->flags = LDB_FLAG_MOD_REPLACE;
148 add a uint64_t element to a record
150 static int add_uint64_element(struct ldb_message *msg, const char *attr, uint64_t v)
152 struct ldb_message_element *el;
154 if (ldb_msg_find_element(msg, attr) != NULL) {
158 if (ldb_msg_add_fmt(msg, attr, "%llu", (unsigned long long)v) != 0) {
162 el = ldb_msg_find_element(msg, attr);
163 /* always set as replace. This works because on add ops, the flag
165 el->flags = LDB_FLAG_MOD_REPLACE;
170 static int replmd_add_replicated(struct ldb_module *module, struct ldb_request *req, struct ldb_control *ctrl)
172 struct ldb_control **saved_ctrls;
175 ldb_debug(module->ldb, LDB_DEBUG_TRACE, "replmd_add_replicated\n");
177 if (!save_controls(ctrl, req, &saved_ctrls)) {
178 return LDB_ERR_OPERATIONS_ERROR;
181 ret = ldb_next_request(module, req);
182 req->controls = saved_ctrls;
187 static int replmd_add_originating(struct ldb_module *module, struct ldb_request *req)
189 struct ldb_request *down_req;
190 struct ldb_message_element *attribute;
191 struct ldb_message *msg;
197 time_t t = time(NULL);
199 ldb_debug(module->ldb, LDB_DEBUG_TRACE, "replmd_add_originating\n");
201 if ((attribute = replmd_find_attribute(req->op.add.message, "objectGUID")) != NULL ) {
202 return ldb_next_request(module, req);
205 down_req = talloc(req, struct ldb_request);
206 if (down_req == NULL) {
207 return LDB_ERR_OPERATIONS_ERROR;
212 /* we have to copy the message as the caller might have it as a const */
213 down_req->op.add.message = msg = ldb_msg_copy_shallow(down_req, req->op.add.message);
215 talloc_free(down_req);
216 return LDB_ERR_OPERATIONS_ERROR;
220 guid = GUID_random();
222 nt_status = ndr_push_struct_blob(&v, msg, &guid,
223 (ndr_push_flags_fn_t)ndr_push_GUID);
224 if (!NT_STATUS_IS_OK(nt_status)) {
225 talloc_free(down_req);
226 return LDB_ERR_OPERATIONS_ERROR;
229 ret = ldb_msg_add_value(msg, "objectGUID", &v, NULL);
231 talloc_free(down_req);
235 if (add_time_element(msg, "whenCreated", t) != 0 ||
236 add_time_element(msg, "whenChanged", t) != 0) {
237 talloc_free(down_req);
238 return LDB_ERR_OPERATIONS_ERROR;
241 /* Get a sequence number from the backend */
242 ret = ldb_sequence_number(module->ldb, LDB_SEQ_NEXT, &seq_num);
243 if (ret == LDB_SUCCESS) {
244 if (add_uint64_element(msg, "uSNCreated", seq_num) != 0 ||
245 add_uint64_element(msg, "uSNChanged", seq_num) != 0) {
246 talloc_free(down_req);
247 return LDB_ERR_OPERATIONS_ERROR;
251 ldb_set_timeout_from_prev_req(module->ldb, req, down_req);
253 /* go on with the call chain */
254 ret = ldb_next_request(module, down_req);
256 /* do not free down_req as the call results may be linked to it,
257 * it will be freed when the upper level request get freed */
258 if (ret == LDB_SUCCESS) {
259 req->handle = down_req->handle;
265 static int replmd_add(struct ldb_module *module, struct ldb_request *req)
267 struct ldb_control *ctrl;
269 /* do not manipulate our control entries */
270 if (ldb_dn_is_special(req->op.add.message->dn)) {
271 return ldb_next_request(module, req);
274 ctrl = get_control_from_list(req->controls, DSDB_CONTROL_REPLICATED_OBJECT_OID);
276 /* handle replicated objects different */
277 return replmd_add_replicated(module, req, ctrl);
280 return replmd_add_originating(module, req);
283 static int replmd_modify_replicated(struct ldb_module *module, struct ldb_request *req, struct ldb_control *ctrl)
285 struct ldb_control **saved_ctrls;
288 ldb_debug(module->ldb, LDB_DEBUG_TRACE, "replmd_modify_replicated\n");
290 if (!save_controls(ctrl, req, &saved_ctrls)) {
291 return LDB_ERR_OPERATIONS_ERROR;
294 ret = ldb_next_request(module, req);
295 req->controls = saved_ctrls;
300 static int replmd_modify_originating(struct ldb_module *module, struct ldb_request *req)
302 struct ldb_request *down_req;
303 struct ldb_message *msg;
305 time_t t = time(NULL);
308 ldb_debug(module->ldb, LDB_DEBUG_TRACE, "replmd_modify_originating\n");
310 down_req = talloc(req, struct ldb_request);
311 if (down_req == NULL) {
312 return LDB_ERR_OPERATIONS_ERROR;
317 /* we have to copy the message as the caller might have it as a const */
318 down_req->op.mod.message = msg = ldb_msg_copy_shallow(down_req, req->op.mod.message);
320 talloc_free(down_req);
321 return LDB_ERR_OPERATIONS_ERROR;
324 if (add_time_element(msg, "whenChanged", t) != 0) {
325 talloc_free(down_req);
326 return LDB_ERR_OPERATIONS_ERROR;
329 /* Get a sequence number from the backend */
330 ret = ldb_sequence_number(module->ldb, LDB_SEQ_NEXT, &seq_num);
331 if (ret == LDB_SUCCESS) {
332 if (add_uint64_element(msg, "uSNChanged", seq_num) != 0) {
333 talloc_free(down_req);
334 return LDB_ERR_OPERATIONS_ERROR;
338 ldb_set_timeout_from_prev_req(module->ldb, req, down_req);
340 /* go on with the call chain */
341 ret = ldb_next_request(module, down_req);
343 /* do not free down_req as the call results may be linked to it,
344 * it will be freed when the upper level request get freed */
345 if (ret == LDB_SUCCESS) {
346 req->handle = down_req->handle;
352 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
354 struct ldb_control *ctrl;
356 /* do not manipulate our control entries */
357 if (ldb_dn_is_special(req->op.mod.message->dn)) {
358 return ldb_next_request(module, req);
361 ctrl = get_control_from_list(req->controls, DSDB_CONTROL_REPLICATED_OBJECT_OID);
363 /* handle replicated objects different */
364 return replmd_modify_replicated(module, req, ctrl);
367 return replmd_modify_originating(module, req);
370 static int replmd_replicated_request_reply_helper(struct replmd_replicated_request *ar, int ret)
372 struct ldb_reply *ares = NULL;
374 ar->handle->status = ret;
375 ar->handle->state = LDB_ASYNC_DONE;
377 if (!ar->orig_req->callback) {
381 /* we're done and need to report the success to the caller */
382 ares = talloc_zero(ar, struct ldb_reply);
384 ar->handle->status = LDB_ERR_OPERATIONS_ERROR;
385 ar->handle->state = LDB_ASYNC_DONE;
386 return LDB_ERR_OPERATIONS_ERROR;
389 ares->type = LDB_REPLY_EXTENDED;
390 ares->response = NULL;
392 return ar->orig_req->callback(ar->module->ldb, ar->orig_req->context, ares);
395 static int replmd_replicated_request_done(struct replmd_replicated_request *ar)
397 return replmd_replicated_request_reply_helper(ar, LDB_SUCCESS);
400 static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
402 return replmd_replicated_request_reply_helper(ar, ret);
405 static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
407 int ret = LDB_ERR_OTHER;
408 /* TODO: do some error mapping */
409 return replmd_replicated_request_reply_helper(ar, ret);
412 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar);
414 static int replmd_replicated_apply_add_callback(struct ldb_context *ldb,
416 struct ldb_reply *ares)
418 #ifdef REPLMD_FULL_ASYNC /* TODO: active this code when ldb support full async code */
419 struct replmd_replicated_request *ar = talloc_get_type(private_data,
420 struct replmd_replicated_request);
422 ar->sub.change_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
423 if (ar->sub.change_ret != LDB_SUCCESS) {
424 return replmd_replicated_request_error(ar, ar->sub.change_ret);
427 talloc_free(ar->sub.mem_ctx);
428 ZERO_STRUCT(ar->sub);
432 return replmd_replicated_apply_next(ar);
438 static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
441 struct ldb_message *msg;
442 struct replPropertyMetaDataBlob *md;
443 struct ldb_val md_value;
448 msg = ar->objs->objects[ar->index_current].msg;
449 md = ar->objs->objects[ar->index_current].meta_data;
451 ret = ldb_sequence_number(ar->module->ldb, LDB_SEQ_NEXT, &seq_num);
452 if (ret != LDB_SUCCESS) {
453 return replmd_replicated_request_error(ar, ret);
456 ret = samdb_msg_add_uint64(ar->module->ldb, msg, msg, "uSNCreated", seq_num);
457 if (ret != LDB_SUCCESS) {
458 return replmd_replicated_request_error(ar, ret);
461 ret = samdb_msg_add_uint64(ar->module->ldb, msg, msg, "uSNChanged", seq_num);
462 if (ret != LDB_SUCCESS) {
463 return replmd_replicated_request_error(ar, ret);
466 md = ar->objs->objects[ar->index_current].meta_data;
467 for (i=0; i < md->ctr.ctr1.count; i++) {
468 md->ctr.ctr1.array[i].local_usn = seq_num;
470 nt_status = ndr_push_struct_blob(&md_value, msg, md,
471 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
472 if (!NT_STATUS_IS_OK(nt_status)) {
473 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
475 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
476 if (ret != LDB_SUCCESS) {
477 return replmd_replicated_request_error(ar, ret);
480 ret = ldb_build_add_req(&ar->sub.change_req,
486 replmd_replicated_apply_add_callback);
487 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
489 #ifdef REPLMD_FULL_ASYNC /* TODO: active this code when ldb support full async code */
490 return ldb_next_request(ar->module, ar->sub.change_req);
492 ret = ldb_next_request(ar->module, ar->sub.change_req);
493 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
495 ar->sub.change_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
496 if (ar->sub.change_ret != LDB_SUCCESS) {
497 return replmd_replicated_request_error(ar, ar->sub.change_ret);
500 talloc_free(ar->sub.mem_ctx);
501 ZERO_STRUCT(ar->sub);
509 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
511 #ifdef REPLMD_FULL_ASYNC /* TODO: active this code when ldb support full async code */
512 #error sorry replmd_replicated_apply_merge not implemented
514 ldb_debug(ar->module->ldb, LDB_DEBUG_FATAL,
515 "replmd_replicated_apply_merge: ignore [%u]\n",
518 talloc_free(ar->sub.mem_ctx);
519 ZERO_STRUCT(ar->sub);
527 static int replmd_replicated_apply_search_callback(struct ldb_context *ldb,
529 struct ldb_reply *ares)
531 struct replmd_replicated_request *ar = talloc_get_type(private_data,
532 struct replmd_replicated_request);
533 bool is_done = false;
535 switch (ares->type) {
536 case LDB_REPLY_ENTRY:
537 ar->sub.search_msg = talloc_steal(ar->sub.mem_ctx, ares->message);
539 case LDB_REPLY_REFERRAL:
540 /* we ignore referrals */
542 case LDB_REPLY_EXTENDED:
549 #ifdef REPLMD_FULL_ASYNC /* TODO: active this code when ldb support full async code */
551 ar->sub.search_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
552 if (ar->sub.search_ret != LDB_SUCCESS) {
553 return replmd_replicated_request_error(ar, ar->sub.search_ret);
555 if (ar->sub.search_msg) {
556 return replmd_replicated_apply_merge(ar);
558 return replmd_replicated_apply_add(ar);
564 static int replmd_replicated_apply_search(struct replmd_replicated_request *ar)
570 tmp_str = ldb_binary_encode(ar->sub.mem_ctx, ar->objs->objects[ar->index_current].guid_value);
571 if (!tmp_str) return replmd_replicated_request_werror(ar, WERR_NOMEM);
573 filter = talloc_asprintf(ar->sub.mem_ctx, "(objectGUID=%s)", tmp_str);
574 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM);
575 talloc_free(tmp_str);
577 ret = ldb_build_search_req(&ar->sub.search_req,
580 ar->objs->partition_dn,
586 replmd_replicated_apply_search_callback);
587 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
589 #ifdef REPLMD_FULL_ASYNC /* TODO: active this code when ldb support full async code */
590 return ldb_next_request(ar->module, ar->sub.search_req);
592 ret = ldb_next_request(ar->module, ar->sub.search_req);
593 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
595 ar->sub.search_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
596 if (ar->sub.search_ret != LDB_SUCCESS) {
597 return replmd_replicated_request_error(ar, ar->sub.search_ret);
599 if (ar->sub.search_msg) {
600 return replmd_replicated_apply_merge(ar);
603 return replmd_replicated_apply_add(ar);
607 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
609 if (ar->index_current >= ar->objs->num_objects) {
610 return replmd_replicated_request_done(ar);
613 ar->sub.mem_ctx = talloc_new(ar);
614 if (!ar->sub.mem_ctx) return replmd_replicated_request_werror(ar, WERR_NOMEM);
616 return replmd_replicated_apply_search(ar);
619 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
621 struct dsdb_extended_replicated_objects *objs;
622 struct replmd_replicated_request *ar;
624 ldb_debug(module->ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
626 objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
628 return LDB_ERR_PROTOCOL_ERROR;
631 ar = replmd_replicated_init_handle(module, req, objs);
633 return LDB_ERR_OPERATIONS_ERROR;
636 #ifdef REPLMD_FULL_ASYNC /* TODO: active this code when ldb support full async code */
637 return replmd_replicated_apply_next(ar);
639 while (req->handle->state != LDB_ASYNC_DONE) {
640 replmd_replicated_apply_next(ar);
647 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
649 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
650 return replmd_extended_replicated_objects(module, req);
653 return ldb_next_request(module, req);
656 static int replmd_wait_none(struct ldb_handle *handle) {
657 struct replmd_replicated_request *ar;
659 if (!handle || !handle->private_data) {
660 return LDB_ERR_OPERATIONS_ERROR;
663 ar = talloc_get_type(handle->private_data, struct replmd_replicated_request);
665 return LDB_ERR_OPERATIONS_ERROR;
668 /* we do only sync calls */
669 if (handle->state != LDB_ASYNC_DONE) {
670 return LDB_ERR_OPERATIONS_ERROR;
673 return handle->status;
676 static int replmd_wait_all(struct ldb_handle *handle) {
680 while (handle->state != LDB_ASYNC_DONE) {
681 ret = replmd_wait_none(handle);
682 if (ret != LDB_SUCCESS) {
687 return handle->status;
690 static int replmd_wait(struct ldb_handle *handle, enum ldb_wait_type type)
692 if (type == LDB_WAIT_ALL) {
693 return replmd_wait_all(handle);
695 return replmd_wait_none(handle);
699 static const struct ldb_module_ops replmd_ops = {
700 .name = "repl_meta_data",
702 .modify = replmd_modify,
703 .extended = replmd_extended,
707 int repl_meta_data_module_init(void)
709 return ldb_register_module(&replmd_ops);