1 /* OpenLDAP WiredTiger backend */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 2002-2018 The OpenLDAP Foundation.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted only as authorized by the OpenLDAP
12 * A copy of this license is available in the file LICENSE in the
13 * top-level directory of the distribution or, alternatively, at
14 * <http://www.OpenLDAP.org/license.html>.
17 * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp>
18 * based on back-bdb for inclusion in OpenLDAP Software.
19 * WiredTiger is a product of MongoDB Inc.
27 static struct berval scbva[] = {
35 AttributeDescription *desc,
43 /* check if modified attribute was indexed
44 * but not in case of NOOP... */
45 ai = wt_index_mask( op->o_bd, desc, &ix_at );
51 ap = attr_find( oldattrs, desc );
52 if ( ap ) ap->a_flags |= SLAP_ATTR_IXDEL;
54 /* Find all other attrs that index to same slot */
55 for ( ap = newattrs; ap; ap = ap->a_next ) {
56 ai = wt_index_mask( op->o_bd, ap->a_desc, &ix2 );
57 if ( ai && ix2.bv_val == ix_at.bv_val )
58 ap->a_flags |= SLAP_ATTR_IXADD;
64 ap = attr_find( newattrs, desc );
65 if ( ap ) ap->a_flags |= SLAP_ATTR_IXADD;
70 int wt_modify_internal(
73 Modifications *modlist,
82 Attribute *save_attrs;
84 int glue_attr_delete = 0;
87 Debug( LDAP_DEBUG_TRACE, "wt_modify_internal: 0x%08lx: %s\n",
90 if ( !acl_check_modlist( op, e, modlist )) {
91 return LDAP_INSUFFICIENT_ACCESS;
94 /* save_attrs will be disposed of by caller */
95 save_attrs = e->e_attrs;
96 e->e_attrs = attrs_dup( e->e_attrs );
98 for ( ml = modlist; ml != NULL; ml = ml->sml_next ) {
101 switch( mod->sm_op ) {
103 case LDAP_MOD_REPLACE:
104 if ( mod->sm_desc == slap_schema.si_ad_structuralObjectClass ) {
105 value_match( &match, slap_schema.si_ad_structuralObjectClass,
106 slap_schema.si_ad_structuralObjectClass->
107 ad_type->sat_equality,
108 SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
109 &mod->sm_values[0], &scbva[0], text );
110 if ( !match ) glue_attr_delete = 1;
113 if ( glue_attr_delete )
117 if ( glue_attr_delete ) {
118 Attribute **app = &e->e_attrs;
119 while ( *app != NULL ) {
120 if ( !is_at_operational( (*app)->a_desc->ad_type )) {
121 Attribute *save = *app;
122 *app = (*app)->a_next;
126 app = &(*app)->a_next;
130 for ( ml = modlist; ml != NULL; ml = ml->sml_next ) {
134 switch ( mod->sm_op ) {
136 Debug(LDAP_DEBUG_ARGS,
137 "wt_modify_internal: add %s\n",
138 mod->sm_desc->ad_cname.bv_val, 0, 0);
139 err = modify_add_values( e, mod, get_permissiveModify(op),
140 text, textbuf, textlen );
141 if( err != LDAP_SUCCESS ) {
142 Debug(LDAP_DEBUG_ARGS, "wt_modify_internal: %d %s\n",
147 case LDAP_MOD_DELETE:
148 if ( glue_attr_delete ) {
153 Debug(LDAP_DEBUG_ARGS,
154 "wt_modify_internal: delete %s\n",
155 mod->sm_desc->ad_cname.bv_val, 0, 0);
156 err = modify_delete_values( e, mod, get_permissiveModify(op),
157 text, textbuf, textlen );
158 if( err != LDAP_SUCCESS ) {
159 Debug(LDAP_DEBUG_ARGS,
160 "wt_modify_internal: %d %s\n", err, *text, 0);
166 case LDAP_MOD_REPLACE:
167 Debug(LDAP_DEBUG_ARGS,
168 "wt_modify_internal: replace %s\n",
169 mod->sm_desc->ad_cname.bv_val, 0, 0);
170 err = modify_replace_values( e, mod, get_permissiveModify(op),
171 text, textbuf, textlen );
172 if( err != LDAP_SUCCESS ) {
173 Debug(LDAP_DEBUG_ARGS,
174 "wt_modify_internal: %d %s\n", err, *text, 0);
180 case LDAP_MOD_INCREMENT:
181 Debug(LDAP_DEBUG_ARGS,
182 "wt_modify_internal: increment %s\n",
183 mod->sm_desc->ad_cname.bv_val, 0, 0);
184 err = modify_increment_values( e, mod, get_permissiveModify(op),
185 text, textbuf, textlen );
186 if( err != LDAP_SUCCESS ) {
187 Debug(LDAP_DEBUG_ARGS,
188 "wt_modify_internal: %d %s\n",
195 case SLAP_MOD_SOFTADD:
196 Debug(LDAP_DEBUG_ARGS,
197 "wt_modify_internal: softadd %s\n",
198 mod->sm_desc->ad_cname.bv_val, 0, 0);
199 /* Avoid problems in index_add_mods()
200 * We need to add index if necessary.
202 mod->sm_op = LDAP_MOD_ADD;
204 err = modify_add_values( e, mod, get_permissiveModify(op),
205 text, textbuf, textlen );
207 mod->sm_op = SLAP_MOD_SOFTADD;
209 if ( err == LDAP_TYPE_OR_VALUE_EXISTS ) {
213 if( err != LDAP_SUCCESS ) {
214 Debug(LDAP_DEBUG_ARGS, "wt_modify_internal: %d %s\n",
219 case SLAP_MOD_SOFTDEL:
220 Debug(LDAP_DEBUG_ARGS,
221 "wt_modify_internal: softdel %s\n",
222 mod->sm_desc->ad_cname.bv_val, 0, 0);
223 /* Avoid problems in index_delete_mods()
224 * We need to add index if necessary.
226 mod->sm_op = LDAP_MOD_DELETE;
228 err = modify_delete_values( e, mod, get_permissiveModify(op),
229 text, textbuf, textlen );
231 mod->sm_op = SLAP_MOD_SOFTDEL;
233 if ( err == LDAP_SUCCESS ) {
235 } else if ( err == LDAP_NO_SUCH_ATTRIBUTE ) {
239 if( err != LDAP_SUCCESS ) {
240 Debug(LDAP_DEBUG_ARGS, "wt_modify_internal: %d %s\n",
245 case SLAP_MOD_ADD_IF_NOT_PRESENT:
246 if ( attr_find( e->e_attrs, mod->sm_desc ) != NULL ) {
252 Debug(LDAP_DEBUG_ARGS,
253 "wt_modify_internal: add_if_not_present %s\n",
254 mod->sm_desc->ad_cname.bv_val, 0, 0);
255 /* Avoid problems in index_add_mods()
256 * We need to add index if necessary.
258 mod->sm_op = LDAP_MOD_ADD;
260 err = modify_add_values( e, mod, get_permissiveModify(op),
261 text, textbuf, textlen );
263 mod->sm_op = SLAP_MOD_ADD_IF_NOT_PRESENT;
265 if( err != LDAP_SUCCESS ) {
266 Debug(LDAP_DEBUG_ARGS, "wt_modify_internal: %d %s\n",
272 Debug(LDAP_DEBUG_ANY, "wt_modify_internal: invalid op %d\n",
274 *text = "Invalid modify operation";
276 Debug(LDAP_DEBUG_ARGS, "wt_modify_internal: %d %s\n",
280 if ( err != LDAP_SUCCESS ) {
281 attrs_free( e->e_attrs );
282 e->e_attrs = save_attrs;
283 /* unlock entry, delete from cache */
287 /* If objectClass was modified, reset the flags */
288 if ( mod->sm_desc == slap_schema.si_ad_objectClass ) {
292 if ( glue_attr_delete ) e->e_ocflags = 0;
295 /* check if modified attribute was indexed
296 * but not in case of NOOP... */
298 wt_modify_idxflags( op, mod->sm_desc, got_delete, e->e_attrs, save_attrs );
303 /* check that the entry still obeys the schema */
305 rc = entry_schema_check( op, e, save_attrs, get_relax(op), 0, &ap,
306 text, textbuf, textlen );
307 if ( rc != LDAP_SUCCESS || op->o_noop ) {
308 attrs_free( e->e_attrs );
309 /* clear the indexing flags */
310 for ( ap = save_attrs; ap != NULL; ap = ap->a_next ) {
311 ap->a_flags &= ~(SLAP_ATTR_IXADD|SLAP_ATTR_IXDEL);
313 e->e_attrs = save_attrs;
315 if ( rc != LDAP_SUCCESS ) {
316 Debug( LDAP_DEBUG_ANY,
317 "entry failed schema check: %s\n",
321 /* if NOOP then silently revert to saved attrs */
325 /* structuralObjectClass modified! */
327 assert( ap->a_desc == slap_schema.si_ad_structuralObjectClass );
329 wt_modify_idxflags( op, slap_schema.si_ad_structuralObjectClass,
330 1, e->e_attrs, save_attrs );
334 /* update the indices of the modified attributes */
336 /* start with deleting the old index entries */
337 for ( ap = save_attrs; ap != NULL; ap = ap->a_next ) {
338 if ( ap->a_flags & SLAP_ATTR_IXDEL ) {
341 ap->a_flags &= ~SLAP_ATTR_IXDEL;
342 a2 = attr_find( e->e_attrs, ap->a_desc );
344 /* need to detect which values were deleted */
346 /* let add know there were deletes */
347 if ( a2->a_flags & SLAP_ATTR_IXADD )
348 a2->a_flags |= SLAP_ATTR_IXDEL;
349 vals = op->o_tmpalloc( (ap->a_numvals + 1) *
350 sizeof(struct berval), op->o_tmpmemctx );
352 for ( i=0; i < ap->a_numvals; i++ ) {
353 rc = attr_valfind( a2, SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
354 &ap->a_nvals[i], NULL, op->o_tmpmemctx );
355 /* Save deleted values */
356 if ( rc == LDAP_NO_SUCH_ATTRIBUTE )
357 vals[j++] = ap->a_nvals[i];
361 /* attribute was completely deleted */
365 if ( !BER_BVISNULL( vals )) {
366 rc = wt_index_values( op, wc, ap->a_desc,
367 vals, e->e_id, SLAP_INDEX_DELETE_OP );
368 if ( rc != LDAP_SUCCESS ) {
369 Debug( LDAP_DEBUG_ANY,
370 "%s: attribute \"%s\" index delete failure\n",
371 op->o_log_prefix, ap->a_desc->ad_cname.bv_val, 0 );
372 attrs_free( e->e_attrs );
373 e->e_attrs = save_attrs;
376 if ( vals != ap->a_nvals )
377 op->o_tmpfree( vals, op->o_tmpmemctx );
382 /* add the new index entries */
383 for ( ap = e->e_attrs; ap != NULL; ap = ap->a_next ) {
384 if (ap->a_flags & SLAP_ATTR_IXADD) {
385 ap->a_flags &= ~SLAP_ATTR_IXADD;
386 if ( ap->a_flags & SLAP_ATTR_IXDEL ) {
387 /* if any values were deleted, we must readd index
388 * for all remaining values.
390 ap->a_flags &= ~SLAP_ATTR_IXDEL;
391 rc = wt_index_values( op, wc, ap->a_desc, ap->a_nvals,
392 e->e_id, SLAP_INDEX_ADD_OP );
395 /* if this was only an add, we only need to index
398 for ( ml = modlist; ml != NULL; ml = ml->sml_next ) {
400 if ( ml->sml_desc != ap->a_desc || !ml->sml_numvals )
403 switch( ml->sml_op ) {
405 case LDAP_MOD_REPLACE:
406 case LDAP_MOD_INCREMENT:
407 case SLAP_MOD_SOFTADD:
408 case SLAP_MOD_ADD_IF_NOT_PRESENT:
409 if ( ml->sml_op == LDAP_MOD_INCREMENT )
411 else if ( ml->sml_nvalues )
412 vals = ml->sml_nvalues;
414 vals = ml->sml_values;
415 rc = wt_index_values( op, wc, ap->a_desc,
416 vals, e->e_id, SLAP_INDEX_ADD_OP );
422 /* This attr was affected by a modify of a subtype, so
423 * there was no direct match in the modlist. Just readd
427 rc = wt_index_values( op, wc, ap->a_desc, ap->a_nvals,
428 e->e_id, SLAP_INDEX_ADD_OP );
431 if ( rc != LDAP_SUCCESS ) {
432 Debug( LDAP_DEBUG_ANY,
433 "%s: attribute \"%s\" index add failure\n",
434 op->o_log_prefix, ap->a_desc->ad_cname.bv_val, 0 );
435 attrs_free( e->e_attrs );
436 e->e_attrs = save_attrs;
446 wt_modify( Operation *op, SlapReply *rs )
448 struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
451 int manageDSAit = get_manageDSAit( op );
452 char textbuf[SLAP_TEXT_BUFLEN];
453 size_t textlen = sizeof textbuf;
456 LDAPControl **preread_ctrl = NULL;
457 LDAPControl **postread_ctrl = NULL;
458 LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
463 Debug( LDAP_DEBUG_ARGS, LDAP_XSTRING(wt_modify) ": %s\n",
464 op->o_req_dn.bv_val, 0, 0 );
467 if( op->o_txnSpec && txn_preop( op, rs ))
471 ctrls[num_ctrls] = NULL;
473 wc = wt_ctx_get(op, wi);
475 Debug( LDAP_DEBUG_ANY,
477 ": wt_ctx_get failed\n",
479 rs->sr_err = LDAP_OTHER;
480 rs->sr_text = "internal error";
481 send_ldap_result( op, rs );
485 /* Don't touch the opattrs, if this is a contextCSN update
486 * initiated from updatedn */
487 if ( !be_isupdate(op) || !op->orm_modlist || op->orm_modlist->sml_next ||
488 op->orm_modlist->sml_desc != slap_schema.si_ad_contextCSN ) {
490 slap_mods_opattrs( op, &op->orm_modlist, 1 );
494 rc = wt_dn2entry(op->o_bd, wc, &op->o_req_ndn, &e);
499 Debug( LDAP_DEBUG_ARGS,
500 "<== " LDAP_XSTRING(wt_delete)
501 ": no such object %s\n",
502 op->o_req_dn.bv_val, 0, 0);
503 /* TODO: lookup referrals */
504 rs->sr_err = LDAP_NO_SUCH_OBJECT;
507 Debug( LDAP_DEBUG_ANY,
508 LDAP_XSTRING(wt_modify)
509 ": wt_dn2entry failed (%d)\n",
511 rs->sr_err = LDAP_OTHER;
512 rs->sr_text = "internal error";
516 if ( !manageDSAit && is_entry_referral( e ) ) {
517 /* entry is a referral, don't allow modify */
518 rs->sr_ref = get_entry_referrals( op, e );
520 Debug( LDAP_DEBUG_TRACE,
521 LDAP_XSTRING(wt_modify) ": entry is referral\n",
524 rs->sr_err = LDAP_REFERRAL;
525 rs->sr_matched = e->e_name.bv_val;
526 rs->sr_flags = REP_REF_MUSTBEFREED;
527 send_ldap_result( op, rs );
528 rs->sr_matched = NULL;
532 if ( get_assert( op ) &&
533 ( test_filter( op, e, get_assertion( op )) != LDAP_COMPARE_TRUE ))
535 rs->sr_err = LDAP_ASSERTION_FAILED;
539 if( op->o_preread ) {
540 if( preread_ctrl == NULL ) {
541 preread_ctrl = &ctrls[num_ctrls++];
542 ctrls[num_ctrls] = NULL;
544 if ( slap_read_controls( op, rs, e,
545 &slap_pre_read_bv, preread_ctrl ) )
547 Debug( LDAP_DEBUG_TRACE,
548 "<=- " LDAP_XSTRING(wt_modify) ": pre-read "
549 "failed!\n", 0, 0, 0 );
550 if ( op->o_preread & SLAP_CONTROL_CRITICAL ) {
551 /* FIXME: is it correct to abort
552 * operation if control fails? */
558 /* begin transaction */
559 rc = wc->session->begin_transaction(wc->session, NULL);
561 Debug( LDAP_DEBUG_TRACE,
562 LDAP_XSTRING(wt_add) ": begin_transaction failed: %s (%d)\n",
563 wiredtiger_strerror(rc), rc, 0 );
564 rs->sr_err = LDAP_OTHER;
565 rs->sr_text = "begin_transaction failed";
568 Debug( LDAP_DEBUG_TRACE, LDAP_XSTRING(wt_modify) ": session id: %p\n",
571 /* Modify the entry */
573 rs->sr_err = wt_modify_internal( op, wc, op->orm_modlist,
574 &dummy, &rs->sr_text, textbuf, textlen );
575 if( rs->sr_err != LDAP_SUCCESS ) {
576 Debug( LDAP_DEBUG_TRACE,
577 LDAP_XSTRING(wt_modify) ": modify failed (%d)\n",
579 /* Only free attrs if they were dup'd. */
580 if ( dummy.e_attrs == e->e_attrs ) dummy.e_attrs = NULL;
584 /* change the entry itself */
585 rs->sr_err = wt_id2entry_update( op, wc->session, &dummy );
586 if ( rs->sr_err != 0 ) {
587 Debug( LDAP_DEBUG_TRACE,
588 LDAP_XSTRING(wt_modify) ": id2entry update failed " "(%d)\n",
590 if ( rs->sr_err == LDAP_ADMINLIMIT_EXCEEDED ) {
591 rs->sr_text = "entry too big";
593 rs->sr_err = LDAP_OTHER;
594 rs->sr_text = "entry update failed";
600 wc->session->rollback_transaction(wc->session, NULL);
601 rs->sr_err = LDAP_X_NO_OPERATION;
605 /* Only free attrs if they were dup'd. */
606 if ( dummy.e_attrs == e->e_attrs ) dummy.e_attrs = NULL;
608 rc = wc->session->commit_transaction(wc->session, NULL);
610 Debug( LDAP_DEBUG_TRACE,
611 "<== " LDAP_XSTRING(wt_modify)
612 ": commit failed: %s (%d)\n",
613 wiredtiger_strerror(rc), rc, 0 );
614 rs->sr_err = LDAP_OTHER;
615 rs->sr_text = "commit failed";
619 Debug( LDAP_DEBUG_TRACE,
620 LDAP_XSTRING(wt_modify) ": updated%s id=%08lx dn=\"%s\"\n",
621 op->o_noop ? " (no-op)" : "",
622 dummy.e_id, op->o_req_dn.bv_val );
624 if( op->o_postread ) {
625 if( postread_ctrl == NULL ) {
626 postread_ctrl = &ctrls[num_ctrls++];
627 ctrls[num_ctrls] = NULL;
629 if( slap_read_controls( op, rs, &dummy,
630 &slap_post_read_bv, postread_ctrl ) )
632 Debug( LDAP_DEBUG_TRACE,
633 "<=- " LDAP_XSTRING(wt_modify)
634 ": post-read failed!\n", 0, 0, 0 );
635 if ( op->o_postread & SLAP_CONTROL_CRITICAL ) {
636 /* FIXME: is it correct to abort
637 * operation if control fails? */
642 if( num_ctrls ) rs->sr_ctrls = ctrls;
644 rs->sr_err = LDAP_SUCCESS;
648 if( dummy.e_attrs ) {
649 attrs_free( dummy.e_attrs );
651 send_ldap_result( op, rs );
654 slap_graduate_commit_csn( op );
657 wt_entry_return( e );
660 if( preread_ctrl != NULL && (*preread_ctrl) != NULL ) {
661 slap_sl_free( (*preread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
662 slap_sl_free( *preread_ctrl, op->o_tmpmemctx );
664 if( postread_ctrl != NULL && (*postread_ctrl) != NULL ) {
665 slap_sl_free( (*postread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
666 slap_sl_free( *postread_ctrl, op->o_tmpmemctx );
676 * indent-tabs-mode: t