r20522: make a copy of the objectguid ldb module because:
[samba.git] / source4 / dsdb / samdb / ldb_modules / repl_meta_data.c
1 /* 
2    ldb database library
3
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
8
9      ** NOTE! The following LGPL license applies to the ldb
10      ** library. This does NOT imply that all of Samba is released
11      ** under the LGPL
12    
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.
17
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.
22
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
26 */
27
28 /*
29  *  Name: ldb
30  *
31  *  Component: ldb repl_meta_data module
32  *
33  *  Description: - add a unique objectGUID onto every new record,
34  *               - handle whenCreated, whenChanged timestamps
35  *               - handle uSNCreated, uSNChanged numbers
36  *               - handle replPropertyMetaData attribute
37  *
38  *  Author: Simo Sorce
39  *  Author: Stefan Metzmacher
40  */
41
42 #include "includes.h"
43 #include "ldb/include/includes.h"
44 #include "librpc/gen_ndr/ndr_misc.h"
45
46 static struct ldb_message_element *replmd_find_attribute(const struct ldb_message *msg, const char *name)
47 {
48         int i;
49
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];
53                 }
54         }
55
56         return NULL;
57 }
58
59 /*
60   add a time element to a record
61 */
62 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
63 {
64         struct ldb_message_element *el;
65         char *s;
66
67         if (ldb_msg_find_element(msg, attr) != NULL) {
68                 return 0;
69         }
70
71         s = ldb_timestring(msg, t);
72         if (s == NULL) {
73                 return -1;
74         }
75
76         if (ldb_msg_add_string(msg, attr, s) != 0) {
77                 return -1;
78         }
79
80         el = ldb_msg_find_element(msg, attr);
81         /* always set as replace. This works because on add ops, the flag
82            is ignored */
83         el->flags = LDB_FLAG_MOD_REPLACE;
84
85         return 0;
86 }
87
88 /*
89   add a uint64_t element to a record
90 */
91 static int add_uint64_element(struct ldb_message *msg, const char *attr, uint64_t v)
92 {
93         struct ldb_message_element *el;
94
95         if (ldb_msg_find_element(msg, attr) != NULL) {
96                 return 0;
97         }
98
99         if (ldb_msg_add_fmt(msg, attr, "%llu", (unsigned long long)v) != 0) {
100                 return -1;
101         }
102
103         el = ldb_msg_find_element(msg, attr);
104         /* always set as replace. This works because on add ops, the flag
105            is ignored */
106         el->flags = LDB_FLAG_MOD_REPLACE;
107
108         return 0;
109 }
110
111 /* add_record: add objectGUID attribute */
112 static int replmd_add(struct ldb_module *module, struct ldb_request *req)
113 {
114         struct ldb_request *down_req;
115         struct ldb_message_element *attribute;
116         struct ldb_message *msg;
117         struct ldb_val v;
118         struct GUID guid;
119         uint64_t seq_num;
120         NTSTATUS nt_status;
121         int ret;
122         time_t t = time(NULL);
123
124         ldb_debug(module->ldb, LDB_DEBUG_TRACE, "replmd_add_record\n");
125
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);
129         }
130
131         if ((attribute = replmd_find_attribute(req->op.add.message, "objectGUID")) != NULL ) {
132                 return ldb_next_request(module, req);
133         }
134
135         down_req = talloc(req, struct ldb_request);
136         if (down_req == NULL) {
137                 return LDB_ERR_OPERATIONS_ERROR;
138         }
139
140         *down_req = *req;
141
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);
144         if (msg == NULL) {
145                 talloc_free(down_req);
146                 return LDB_ERR_OPERATIONS_ERROR;
147         }
148
149         /* a new GUID */
150         guid = GUID_random();
151
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;
157         }
158
159         ret = ldb_msg_add_value(msg, "objectGUID", &v, NULL);
160         if (ret) {
161                 talloc_free(down_req);
162                 return ret;
163         }
164         
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;
169         }
170
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;
178                 }
179         }
180
181         ldb_set_timeout_from_prev_req(module->ldb, req, down_req);
182
183         /* go on with the call chain */
184         ret = ldb_next_request(module, down_req);
185
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;
190         }
191
192         return ret;
193 }
194
195 /* modify_record: update timestamps */
196 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
197 {
198         struct ldb_request *down_req;
199         struct ldb_message *msg;
200         int ret;
201         time_t t = time(NULL);
202         uint64_t seq_num;
203
204         ldb_debug(module->ldb, LDB_DEBUG_TRACE, "replmd_modify\n");
205
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);
209         }
210
211         down_req = talloc(req, struct ldb_request);
212         if (down_req == NULL) {
213                 return LDB_ERR_OPERATIONS_ERROR;
214         }
215
216         *down_req = *req;
217
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);
220         if (msg == NULL) {
221                 talloc_free(down_req);
222                 return LDB_ERR_OPERATIONS_ERROR;
223         }
224
225         if (add_time_element(msg, "whenChanged", t) != 0) {
226                 talloc_free(down_req);
227                 return LDB_ERR_OPERATIONS_ERROR;
228         }
229
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;
236                 }
237         }
238
239         ldb_set_timeout_from_prev_req(module->ldb, req, down_req);
240
241         /* go on with the call chain */
242         ret = ldb_next_request(module, down_req);
243
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;
248         }
249
250         return ret;
251 }
252
253 static const struct ldb_module_ops replmd_ops = {
254         .name          = "repl_meta_data",
255         .add           = replmd_add,
256         .modify        = replmd_modify,
257 };
258
259 int repl_meta_data_module_init(void)
260 {
261         return ldb_register_module(&replmd_ops);
262 }