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 "ldb/include/includes.h"
44 #include "librpc/gen_ndr/ndr_misc.h"
46 static struct ldb_message_element *replmd_find_attribute(const struct ldb_message *msg, const char *name)
50 for (i = 0; i < msg->num_elements; i++) {
51 if (ldb_attr_cmp(name, msg->elements[i].name) == 0) {
52 return &msg->elements[i];
60 add a time element to a record
62 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
64 struct ldb_message_element *el;
67 if (ldb_msg_find_element(msg, attr) != NULL) {
71 s = ldb_timestring(msg, t);
76 if (ldb_msg_add_string(msg, attr, s) != 0) {
80 el = ldb_msg_find_element(msg, attr);
81 /* always set as replace. This works because on add ops, the flag
83 el->flags = LDB_FLAG_MOD_REPLACE;
89 add a uint64_t element to a record
91 static int add_uint64_element(struct ldb_message *msg, const char *attr, uint64_t v)
93 struct ldb_message_element *el;
95 if (ldb_msg_find_element(msg, attr) != NULL) {
99 if (ldb_msg_add_fmt(msg, attr, "%llu", (unsigned long long)v) != 0) {
103 el = ldb_msg_find_element(msg, attr);
104 /* always set as replace. This works because on add ops, the flag
106 el->flags = LDB_FLAG_MOD_REPLACE;
111 /* add_record: add objectGUID attribute */
112 static int replmd_add(struct ldb_module *module, struct ldb_request *req)
114 struct ldb_request *down_req;
115 struct ldb_message_element *attribute;
116 struct ldb_message *msg;
122 time_t t = time(NULL);
124 ldb_debug(module->ldb, LDB_DEBUG_TRACE, "replmd_add_record\n");
126 /* do not manipulate our control entries */
127 if (ldb_dn_is_special(req->op.add.message->dn)) {
128 return ldb_next_request(module, req);
131 if ((attribute = replmd_find_attribute(req->op.add.message, "objectGUID")) != NULL ) {
132 return ldb_next_request(module, req);
135 down_req = talloc(req, struct ldb_request);
136 if (down_req == NULL) {
137 return LDB_ERR_OPERATIONS_ERROR;
142 /* we have to copy the message as the caller might have it as a const */
143 down_req->op.add.message = msg = ldb_msg_copy_shallow(down_req, req->op.add.message);
145 talloc_free(down_req);
146 return LDB_ERR_OPERATIONS_ERROR;
150 guid = GUID_random();
152 nt_status = ndr_push_struct_blob(&v, msg, &guid,
153 (ndr_push_flags_fn_t)ndr_push_GUID);
154 if (!NT_STATUS_IS_OK(nt_status)) {
155 talloc_free(down_req);
156 return LDB_ERR_OPERATIONS_ERROR;
159 ret = ldb_msg_add_value(msg, "objectGUID", &v, NULL);
161 talloc_free(down_req);
165 if (add_time_element(msg, "whenCreated", t) != 0 ||
166 add_time_element(msg, "whenChanged", t) != 0) {
167 talloc_free(down_req);
168 return LDB_ERR_OPERATIONS_ERROR;
171 /* Get a sequence number from the backend */
172 ret = ldb_sequence_number(module->ldb, LDB_SEQ_NEXT, &seq_num);
173 if (ret == LDB_SUCCESS) {
174 if (add_uint64_element(msg, "uSNCreated", seq_num) != 0 ||
175 add_uint64_element(msg, "uSNChanged", seq_num) != 0) {
176 talloc_free(down_req);
177 return LDB_ERR_OPERATIONS_ERROR;
181 ldb_set_timeout_from_prev_req(module->ldb, req, down_req);
183 /* go on with the call chain */
184 ret = ldb_next_request(module, down_req);
186 /* do not free down_req as the call results may be linked to it,
187 * it will be freed when the upper level request get freed */
188 if (ret == LDB_SUCCESS) {
189 req->handle = down_req->handle;
195 /* modify_record: update timestamps */
196 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
198 struct ldb_request *down_req;
199 struct ldb_message *msg;
201 time_t t = time(NULL);
204 ldb_debug(module->ldb, LDB_DEBUG_TRACE, "replmd_modify\n");
206 /* do not manipulate our control entries */
207 if (ldb_dn_is_special(req->op.add.message->dn)) {
208 return ldb_next_request(module, req);
211 down_req = talloc(req, struct ldb_request);
212 if (down_req == NULL) {
213 return LDB_ERR_OPERATIONS_ERROR;
218 /* we have to copy the message as the caller might have it as a const */
219 down_req->op.mod.message = msg = ldb_msg_copy_shallow(down_req, req->op.mod.message);
221 talloc_free(down_req);
222 return LDB_ERR_OPERATIONS_ERROR;
225 if (add_time_element(msg, "whenChanged", t) != 0) {
226 talloc_free(down_req);
227 return LDB_ERR_OPERATIONS_ERROR;
230 /* Get a sequence number from the backend */
231 ret = ldb_sequence_number(module->ldb, LDB_SEQ_NEXT, &seq_num);
232 if (ret == LDB_SUCCESS) {
233 if (add_uint64_element(msg, "uSNChanged", seq_num) != 0) {
234 talloc_free(down_req);
235 return LDB_ERR_OPERATIONS_ERROR;
239 ldb_set_timeout_from_prev_req(module->ldb, req, down_req);
241 /* go on with the call chain */
242 ret = ldb_next_request(module, down_req);
244 /* do not free down_req as the call results may be linked to it,
245 * it will be freed when the upper level request get freed */
246 if (ret == LDB_SUCCESS) {
247 req->handle = down_req->handle;
253 static const struct ldb_module_ops replmd_ops = {
254 .name = "repl_meta_data",
256 .modify = replmd_modify,
259 int repl_meta_data_module_init(void)
261 return ldb_register_module(&replmd_ops);