1 /* $NetBSD: add.c,v 1.2 2021/08/14 16:15:02 christos Exp $ */
2
3 /* OpenLDAP WiredTiger backend */
4 /* $OpenLDAP$ */
5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6 *
7 * Copyright 2002-2021 The OpenLDAP Foundation.
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted only as authorized by the OpenLDAP
12 * Public License.
13 *
14 * A copy of this license is available in the file LICENSE in the
15 * top-level directory of the distribution or, alternatively, at
16 * <http://www.OpenLDAP.org/license.html>.
17 */
18 /* ACKNOWLEDGEMENTS:
19 * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp>
20 * based on back-bdb for inclusion in OpenLDAP Software.
21 * WiredTiger is a product of MongoDB Inc.
22 */
23
24 #include <sys/cdefs.h>
25 __RCSID("$NetBSD: add.c,v 1.2 2021/08/14 16:15:02 christos Exp $");
26
27 #include "portable.h"
28
29 #include <stdio.h>
30 #include "back-wt.h"
31 #include "slap-config.h"
32
33 int
wt_add(Operation * op,SlapReply * rs)34 wt_add( Operation *op, SlapReply *rs )
35 {
36 struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
37 struct berval pdn;
38 char textbuf[SLAP_TEXT_BUFLEN];
39 size_t textlen = sizeof textbuf;
40 AttributeDescription *children = slap_schema.si_ad_children;
41 AttributeDescription *entry = slap_schema.si_ad_entry;
42 ID eid;
43 int num_retries = 0;
44 int success;
45 LDAPControl **postread_ctrl = NULL;
46 LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
47 int num_ctrls = 0;
48 wt_ctx *wc;
49 Entry *e = NULL;
50 Entry *p = NULL;
51 ID pid;
52 int rc;
53
54 Debug( LDAP_DEBUG_ARGS, "==> " LDAP_XSTRING(wt_add) ": %s\n",
55 op->ora_e->e_name.bv_val );
56
57 ctrls[num_ctrls] = 0;
58
59 /* check entry's schema */
60 rs->sr_err = entry_schema_check(
61 op, op->ora_e, NULL,
62 get_relax(op), 1, NULL, &rs->sr_text, textbuf, textlen );
63 if ( rs->sr_err != LDAP_SUCCESS ) {
64 Debug( LDAP_DEBUG_TRACE,
65 LDAP_XSTRING(wt_add)
66 ": entry failed schema check: %s (%d)\n",
67 rs->sr_text, rs->sr_err );
68 goto return_results;
69 }
70
71 /* add opattrs to shadow as well, only missing attrs will actually
72 * be added; helps compatibility with older OL versions */
73 rs->sr_err = slap_add_opattrs( op, &rs->sr_text, textbuf, textlen, 1 );
74 if ( rs->sr_err != LDAP_SUCCESS ) {
75 Debug( LDAP_DEBUG_TRACE,
76 LDAP_XSTRING(wt_add)
77 ": entry failed op attrs add: %s (%d)\n",
78 rs->sr_text, rs->sr_err );
79 goto return_results;
80 }
81
82 if ( get_assert( op ) &&
83 ( test_filter( op, op->ora_e, get_assertion( op ))
84 != LDAP_COMPARE_TRUE ))
85 {
86 rs->sr_err = LDAP_ASSERTION_FAILED;
87 goto return_results;
88 }
89
90 /* Not used
91 * subentry = is_entry_subentry( op->ora_e );
92 */
93
94 /*
95 * Get the parent dn and see if the corresponding entry exists.
96 */
97 if ( be_issuffix( op->o_bd, &op->ora_e->e_nname ) ) {
98 pdn = slap_empty_bv;
99 } else {
100 dnParent( &op->ora_e->e_nname, &pdn );
101 }
102
103 wc = wt_ctx_get(op, wi);
104 if( !wc ){
105 Debug( LDAP_DEBUG_ANY,
106 LDAP_XSTRING(wt_add)
107 ": wt_ctx_get failed\n" );
108 rs->sr_err = LDAP_OTHER;
109 rs->sr_text = "internal error";
110 send_ldap_result( op, rs );
111 return rs->sr_err;
112 }
113
114 rc = wt_dn2entry(op->o_bd, wc, &op->o_req_ndn, &e);
115 switch( rc ) {
116 case 0:
117 rs->sr_err = LDAP_ALREADY_EXISTS;
118 goto return_results;
119 break;
120 case WT_NOTFOUND:
121 break;
122 default:
123 /* TODO: retry handling */
124 Debug( LDAP_DEBUG_ANY,
125 LDAP_XSTRING(wt_add)
126 ": error at wt_dn2entry() rc=%d\n",
127 rc );
128 rs->sr_err = LDAP_OTHER;
129 rs->sr_text = "internal error";
130 goto return_results;
131 }
132
133 /* get parent entry */
134 rc = wt_dn2pentry(op->o_bd, wc, &op->o_req_ndn, &p);
135 switch( rc ){
136 case 0:
137 case WT_NOTFOUND:
138 break;
139 default:
140 Debug( LDAP_DEBUG_ANY,
141 LDAP_XSTRING(wt_add)
142 ": error at wt_dn2pentry() rc=%d\n",
143 rc );
144 rs->sr_err = LDAP_OTHER;
145 rs->sr_text = "internal error";
146 goto return_results;
147 }
148
149 if ( !p )
150 p = (Entry *)&slap_entry_root;
151
152 if ( !bvmatch( &pdn, &p->e_nname ) ) {
153 rs->sr_matched = ber_strdup_x( p->e_name.bv_val,
154 op->o_tmpmemctx );
155 if ( p != (Entry *)&slap_entry_root ) {
156 rs->sr_ref = is_entry_referral( p )
157 ? get_entry_referrals( op, p )
158 : NULL;
159 wt_entry_return( p );
160 } else {
161 rs->sr_ref = NULL;
162 }
163 p = NULL;
164 Debug( LDAP_DEBUG_TRACE,
165 LDAP_XSTRING(wt_add) ": parent "
166 "does not exist\n" );
167 rs->sr_err = LDAP_REFERRAL;
168 rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
169 goto return_results;
170 }
171
172 rs->sr_err = access_allowed( op, p,
173 children, NULL, ACL_WADD, NULL );
174 if ( ! rs->sr_err ) {
175 /*
176 if ( p != (Entry *)&slap_entry_root )
177 wt_entry_return( op, p );
178 */
179 p = NULL;
180
181 Debug( LDAP_DEBUG_TRACE,
182 LDAP_XSTRING(wt_add) ": no write access to parent\n" );
183 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
184 rs->sr_text = "no write access to parent";
185 goto return_results;;
186 }
187
188 if ( p != (Entry *)&slap_entry_root ) {
189 if ( is_entry_subentry( p ) ) {
190 wt_entry_return( p );
191 p = NULL;
192 /* parent is a subentry, don't allow add */
193 Debug( LDAP_DEBUG_TRACE,
194 LDAP_XSTRING(wt_add) ": parent is subentry\n" );
195 rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
196 rs->sr_text = "parent is a subentry";
197 goto return_results;;
198 }
199
200 if ( is_entry_alias( p ) ) {
201 wt_entry_return( p );
202 p = NULL;
203 /* parent is an alias, don't allow add */
204 Debug( LDAP_DEBUG_TRACE,
205 LDAP_XSTRING(wt_add) ": parent is alias\n" );
206 rs->sr_err = LDAP_ALIAS_PROBLEM;
207 rs->sr_text = "parent is an alias";
208 goto return_results;;
209 }
210
211 if ( is_entry_referral( p ) ) {
212 BerVarray ref = get_entry_referrals( op, p );
213 /* parent is a referral, don't allow add */
214 rs->sr_matched = ber_strdup_x( p->e_name.bv_val,
215 op->o_tmpmemctx );
216 rs->sr_ref = referral_rewrite( ref, &p->e_name,
217 &op->o_req_dn, LDAP_SCOPE_DEFAULT );
218 ber_bvarray_free( ref );
219 wt_entry_return( p );
220 p = NULL;
221 Debug( LDAP_DEBUG_TRACE,
222 LDAP_XSTRING(wt_add) ": parent is referral\n" );
223
224 rs->sr_err = LDAP_REFERRAL;
225 rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
226 goto return_results;
227 }
228 }
229
230 #if 0
231 if ( subentry ) {
232 /* FIXME: */
233 /* parent must be an administrative point of the required kind */
234 }
235 #endif
236
237 /* free parent */
238 if ( p != (Entry *)&slap_entry_root ) {
239 pid = p->e_id;
240 if ( p->e_nname.bv_len ) {
241 struct berval ppdn;
242
243 /* ITS#5326: use parent's DN if differs from provided one */
244 dnParent( &op->ora_e->e_name, &ppdn );
245 if ( !dn_match( &p->e_name, &ppdn ) ) {
246 struct berval rdn;
247 struct berval newdn;
248
249 dnRdn( &op->ora_e->e_name, &rdn );
250
251 build_new_dn( &newdn, &p->e_name, &rdn, NULL );
252 if ( op->ora_e->e_name.bv_val != op->o_req_dn.bv_val )
253 ber_memfree( op->ora_e->e_name.bv_val );
254 op->ora_e->e_name = newdn;
255
256 /* FIXME: should check whether
257 * dnNormalize(newdn) == e->e_nname ... */
258 }
259 }
260
261 wt_entry_return( p );
262 }
263 p = NULL;
264
265 rs->sr_err = access_allowed( op, op->ora_e,
266 entry, NULL, ACL_WADD, NULL );
267
268 if ( ! rs->sr_err ) {
269 Debug( LDAP_DEBUG_TRACE,
270 LDAP_XSTRING(wt_add) ": no write access to entry\n" );
271 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
272 rs->sr_text = "no write access to entry";
273 goto return_results;
274 }
275
276 /*
277 * Check ACL for attribute write access
278 */
279 if (!acl_check_modlist(op, op->ora_e, op->ora_modlist)) {
280 Debug( LDAP_DEBUG_TRACE,
281 LDAP_XSTRING(wt_add) ": no write access to attribute\n" );
282 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
283 rs->sr_text = "no write access to attribute";
284 goto return_results;
285 }
286
287 rc = wc->session->begin_transaction(wc->session, NULL);
288 if( rc ) {
289 Debug( LDAP_DEBUG_TRACE,
290 LDAP_XSTRING(wt_add) ": begin_transaction failed: %s (%d)\n",
291 wiredtiger_strerror(rc), rc );
292 rs->sr_err = LDAP_OTHER;
293 rs->sr_text = "begin_transaction failed";
294 goto return_results;
295 }
296 Debug( LDAP_DEBUG_TRACE, LDAP_XSTRING(wt_add) ": session id: %p\n",
297 wc->session );
298
299 wt_next_id( op->o_bd, &eid );
300 op->ora_e->e_id = eid;
301
302 rc = wt_dn2id_add( op, wc->session, pid, op->ora_e );
303 if( rc ){
304 Debug( LDAP_DEBUG_TRACE,
305 LDAP_XSTRING(wt_add)
306 ": dn2id_add failed: %s (%d)\n",
307 wiredtiger_strerror(rc), rc );
308 switch( rc ) {
309 case WT_DUPLICATE_KEY:
310 rs->sr_err = LDAP_ALREADY_EXISTS;
311 break;
312 default:
313 rs->sr_err = LDAP_OTHER;
314 }
315 wc->session->rollback_transaction(wc->session, NULL);
316 goto return_results;
317 }
318
319 rc = wt_id2entry_add( op, wc->session, op->ora_e );
320 if ( rc ) {
321 Debug( LDAP_DEBUG_TRACE,
322 LDAP_XSTRING(wt_add)
323 ": id2entry_add failed: %s (%d)\n",
324 wiredtiger_strerror(rc), rc );
325 if ( rc == LDAP_ADMINLIMIT_EXCEEDED ) {
326 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
327 rs->sr_text = "entry is too big";
328 } else {
329 rs->sr_err = LDAP_OTHER;
330 rs->sr_text = "entry store failed";
331 }
332 wc->session->rollback_transaction(wc->session, NULL);
333 goto return_results;
334 }
335
336 /* add indices */
337 rc = wt_index_entry_add( op, wc, op->ora_e );
338 if ( rc ) {
339 Debug(LDAP_DEBUG_TRACE,
340 "<== " LDAP_XSTRING(wt_add)
341 ": index add failed: %s (%d)\n",
342 wiredtiger_strerror(rc), rc );
343 rs->sr_err = LDAP_OTHER;
344 rs->sr_text = "index add failed";
345 wc->session->rollback_transaction(wc->session, NULL);
346 goto return_results;
347 }
348
349 rc = wc->session->commit_transaction(wc->session, NULL);
350 if( rc ) {
351 Debug( LDAP_DEBUG_TRACE,
352 "<== " LDAP_XSTRING(wt_add)
353 ": commit_transaction failed: %s (%d)\n",
354 wiredtiger_strerror(rc), rc );
355 rs->sr_err = LDAP_OTHER;
356 rs->sr_text = "commit_transaction failed";
357 goto return_results;
358 }
359
360 rs->sr_err = LDAP_SUCCESS;
361
362 /* post-read */
363 if( op->o_postread ) {
364 if( postread_ctrl == NULL ) {
365 postread_ctrl = &ctrls[num_ctrls++];
366 ctrls[num_ctrls] = NULL;
367 }
368 if ( slap_read_controls( op, rs, op->ora_e,
369 &slap_post_read_bv, postread_ctrl ) )
370 {
371 Debug( LDAP_DEBUG_TRACE,
372 "<=- " LDAP_XSTRING(wt_add) ": post-read "
373 "failed!\n" );
374 if ( op->o_postread & SLAP_CONTROL_CRITICAL ) {
375 /* FIXME: is it correct to abort
376 * operation if control fails? */
377 goto return_results;
378 }
379 }
380 }
381
382 Debug(LDAP_DEBUG_TRACE,
383 LDAP_XSTRING(wt_add) ": added%s id=%08lx dn=\"%s\"\n",
384 op->o_noop ? " (no-op)" : "",
385 op->ora_e->e_id, op->ora_e->e_dn );
386
387 return_results:
388 success = rs->sr_err;
389 send_ldap_result( op, rs );
390
391 slap_graduate_commit_csn( op );
392
393 if( postread_ctrl != NULL && (*postread_ctrl) != NULL ) {
394 slap_sl_free( (*postread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
395 slap_sl_free( *postread_ctrl, op->o_tmpmemctx );
396 }
397 return rs->sr_err;
398 }
399
400 /*
401 * Local variables:
402 * indent-tabs-mode: t
403 * tab-width: 4
404 * c-basic-offset: 4
405 * End:
406 */
407