1 /* modify.c - bdb backend modify routine */
2 /* $OpenLDAP: pkg/ldap/servers/slapd/back-bdb/modify.c,v 1.156.2.11 2008/05/01 21:39:35 quanah Exp $ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 2000-2008 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>.
20 #include <ac/string.h>
25 static struct berval scbva
[] = {
30 int bdb_modify_internal(
33 Modifications
*modlist
,
42 Attribute
*save_attrs
;
44 int glue_attr_delete
= 0;
48 Debug( LDAP_DEBUG_TRACE
, "bdb_modify_internal: 0x%08lx: %s\n",
51 if ( !acl_check_modlist( op
, e
, modlist
)) {
52 return LDAP_INSUFFICIENT_ACCESS
;
55 /* save_attrs will be disposed of by bdb_cache_modify */
56 save_attrs
= e
->e_attrs
;
57 e
->e_attrs
= attrs_dup( e
->e_attrs
);
59 for ( ml
= modlist
; ml
!= NULL
; ml
= ml
->sml_next
) {
62 switch( mod
->sm_op
) {
64 case LDAP_MOD_REPLACE
:
65 if ( mod
->sm_desc
== slap_schema
.si_ad_structuralObjectClass
) {
66 value_match( &match
, slap_schema
.si_ad_structuralObjectClass
,
67 slap_schema
.si_ad_structuralObjectClass
->
68 ad_type
->sat_equality
,
69 SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX
,
70 &mod
->sm_values
[0], &scbva
[0], text
);
71 if ( !match
) glue_attr_delete
= 1;
74 if ( glue_attr_delete
)
78 if ( glue_attr_delete
) {
79 Attribute
**app
= &e
->e_attrs
;
80 while ( *app
!= NULL
) {
81 if ( !is_at_operational( (*app
)->a_desc
->ad_type
)) {
82 Attribute
*save
= *app
;
83 *app
= (*app
)->a_next
;
87 app
= &(*app
)->a_next
;
91 for ( ml
= modlist
; ml
!= NULL
; ml
= ml
->sml_next
) {
96 switch ( mod
->sm_op
) {
98 Debug(LDAP_DEBUG_ARGS
,
99 "bdb_modify_internal: add %s\n",
100 mod
->sm_desc
->ad_cname
.bv_val
, 0, 0);
101 err
= modify_add_values( e
, mod
, get_permissiveModify(op
),
102 text
, textbuf
, textlen
);
103 if( err
!= LDAP_SUCCESS
) {
104 Debug(LDAP_DEBUG_ARGS
, "bdb_modify_internal: %d %s\n",
109 case LDAP_MOD_DELETE
:
110 if ( glue_attr_delete
) {
115 Debug(LDAP_DEBUG_ARGS
,
116 "bdb_modify_internal: delete %s\n",
117 mod
->sm_desc
->ad_cname
.bv_val
, 0, 0);
118 err
= modify_delete_values( e
, mod
, get_permissiveModify(op
),
119 text
, textbuf
, textlen
);
120 assert( err
!= LDAP_TYPE_OR_VALUE_EXISTS
);
121 if( err
!= LDAP_SUCCESS
) {
122 Debug(LDAP_DEBUG_ARGS
, "bdb_modify_internal: %d %s\n",
129 case LDAP_MOD_REPLACE
:
130 Debug(LDAP_DEBUG_ARGS
,
131 "bdb_modify_internal: replace %s\n",
132 mod
->sm_desc
->ad_cname
.bv_val
, 0, 0);
133 err
= modify_replace_values( e
, mod
, get_permissiveModify(op
),
134 text
, textbuf
, textlen
);
135 if( err
!= LDAP_SUCCESS
) {
136 Debug(LDAP_DEBUG_ARGS
, "bdb_modify_internal: %d %s\n",
143 case LDAP_MOD_INCREMENT
:
144 Debug(LDAP_DEBUG_ARGS
,
145 "bdb_modify_internal: increment %s\n",
146 mod
->sm_desc
->ad_cname
.bv_val
, 0, 0);
147 err
= modify_increment_values( e
, mod
, get_permissiveModify(op
),
148 text
, textbuf
, textlen
);
149 if( err
!= LDAP_SUCCESS
) {
150 Debug(LDAP_DEBUG_ARGS
,
151 "bdb_modify_internal: %d %s\n",
158 case SLAP_MOD_SOFTADD
:
159 Debug(LDAP_DEBUG_ARGS
,
160 "bdb_modify_internal: softadd %s\n",
161 mod
->sm_desc
->ad_cname
.bv_val
, 0, 0);
162 /* Avoid problems in index_add_mods()
163 * We need to add index if necessary.
165 mod
->sm_op
= LDAP_MOD_ADD
;
167 err
= modify_add_values( e
, mod
, get_permissiveModify(op
),
168 text
, textbuf
, textlen
);
170 mod
->sm_op
= SLAP_MOD_SOFTADD
;
172 if ( err
== LDAP_TYPE_OR_VALUE_EXISTS
) {
176 if( err
!= LDAP_SUCCESS
) {
177 Debug(LDAP_DEBUG_ARGS
, "bdb_modify_internal: %d %s\n",
183 Debug(LDAP_DEBUG_ANY
, "bdb_modify_internal: invalid op %d\n",
185 *text
= "Invalid modify operation";
187 Debug(LDAP_DEBUG_ARGS
, "bdb_modify_internal: %d %s\n",
191 if ( err
!= LDAP_SUCCESS
) {
192 attrs_free( e
->e_attrs
);
193 e
->e_attrs
= save_attrs
;
194 /* unlock entry, delete from cache */
198 /* If objectClass was modified, reset the flags */
199 if ( mod
->sm_desc
== slap_schema
.si_ad_objectClass
) {
203 if ( glue_attr_delete
) e
->e_ocflags
= 0;
205 /* check if modified attribute was indexed
206 * but not in case of NOOP... */
207 ai
= bdb_index_mask( op
->o_bd
, mod
->sm_desc
, &ix_at
);
208 if ( ai
&& !op
->o_noop
) {
212 ap
= attr_find( save_attrs
, mod
->sm_desc
);
213 if ( ap
) ap
->a_flags
|= SLAP_ATTR_IXDEL
;
215 /* Find all other attrs that index to same slot */
216 for ( ap
= e
->e_attrs
; ap
; ap
=ap
->a_next
) {
217 ai
= bdb_index_mask( op
->o_bd
, ap
->a_desc
, &ix2
);
218 if ( ai
&& ix2
.bv_val
== ix_at
.bv_val
)
219 ap
->a_flags
|= SLAP_ATTR_IXADD
;
222 ap
= attr_find( e
->e_attrs
, mod
->sm_desc
);
223 if ( ap
) ap
->a_flags
|= SLAP_ATTR_IXADD
;
228 /* check that the entry still obeys the schema */
229 rc
= entry_schema_check( op
, e
, save_attrs
, get_relax(op
), 0,
230 text
, textbuf
, textlen
);
231 if ( rc
!= LDAP_SUCCESS
|| op
->o_noop
) {
232 attrs_free( e
->e_attrs
);
233 /* clear the indexing flags */
234 for ( ap
= save_attrs
; ap
!= NULL
; ap
= ap
->a_next
) {
235 ap
->a_flags
&= ~(SLAP_ATTR_IXADD
|SLAP_ATTR_IXDEL
);
237 e
->e_attrs
= save_attrs
;
239 if ( rc
!= LDAP_SUCCESS
) {
240 Debug( LDAP_DEBUG_ANY
,
241 "entry failed schema check: %s\n",
245 /* if NOOP then silently revert to saved attrs */
249 /* update the indices of the modified attributes */
251 /* start with deleting the old index entries */
252 for ( ap
= save_attrs
; ap
!= NULL
; ap
= ap
->a_next
) {
253 if ( ap
->a_flags
& SLAP_ATTR_IXDEL
) {
256 ap
->a_flags
&= ~SLAP_ATTR_IXDEL
;
257 a2
= attr_find( e
->e_attrs
, ap
->a_desc
);
259 /* need to detect which values were deleted */
264 rc
= attr_valfind( a2
, SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH
,
265 &ap
->a_nvals
[i
], NULL
, op
->o_tmpmemctx
);
266 /* Move deleted values to end of array */
267 if ( rc
== LDAP_NO_SUCH_ATTRIBUTE
) {
270 tmp
= ap
->a_nvals
[j
];
271 ap
->a_nvals
[j
] = ap
->a_nvals
[i
];
272 ap
->a_nvals
[i
] = tmp
;
274 ap
->a_vals
[j
] = ap
->a_vals
[i
];
281 vals
= &ap
->a_nvals
[j
];
283 /* attribute was completely deleted */
286 if ( !BER_BVISNULL( vals
)) {
287 rc
= bdb_index_values( op
, tid
, ap
->a_desc
,
288 vals
, e
->e_id
, SLAP_INDEX_DELETE_OP
);
289 if ( rc
!= LDAP_SUCCESS
) {
290 attrs_free( e
->e_attrs
);
291 e
->e_attrs
= save_attrs
;
292 Debug( LDAP_DEBUG_ANY
,
293 "Attribute index delete failure",
301 /* add the new index entries */
302 for ( ap
= e
->e_attrs
; ap
!= NULL
; ap
= ap
->a_next
) {
303 if (ap
->a_flags
& SLAP_ATTR_IXADD
) {
304 ap
->a_flags
&= ~SLAP_ATTR_IXADD
;
305 rc
= bdb_index_values( op
, tid
, ap
->a_desc
,
307 e
->e_id
, SLAP_INDEX_ADD_OP
);
308 if ( rc
!= LDAP_SUCCESS
) {
309 attrs_free( e
->e_attrs
);
310 e
->e_attrs
= save_attrs
;
311 Debug( LDAP_DEBUG_ANY
,
312 "Attribute index add failure",
324 bdb_modify( Operation
*op
, SlapReply
*rs
)
326 struct bdb_info
*bdb
= (struct bdb_info
*) op
->o_bd
->be_private
;
328 EntryInfo
*ei
= NULL
;
329 int manageDSAit
= get_manageDSAit( op
);
330 char textbuf
[SLAP_TEXT_BUFLEN
];
331 size_t textlen
= sizeof textbuf
;
332 DB_TXN
*ltid
= NULL
, *lt2
;
333 struct bdb_op_info opinfo
= {0};
337 BDB_LOCKER locker
= 0;
342 LDAPControl
**preread_ctrl
= NULL
;
343 LDAPControl
**postread_ctrl
= NULL
;
344 LDAPControl
*ctrls
[SLAP_MAX_RESPONSE_CONTROLS
];
353 Debug( LDAP_DEBUG_ARGS
, LDAP_XSTRING(bdb_modify
) ": %s\n",
354 op
->o_req_dn
.bv_val
, 0, 0 );
357 if( op
->o_txnSpec
) {
358 /* acquire connection lock */
359 ldap_pvt_thread_mutex_lock( &op
->o_conn
->c_mutex
);
360 if( op
->o_conn
->c_txn
== CONN_TXN_INACTIVE
) {
361 rs
->sr_text
= "invalid transaction identifier";
362 rs
->sr_err
= LDAP_X_TXN_ID_INVALID
;
364 } else if( op
->o_conn
->c_txn
== CONN_TXN_SETTLE
) {
369 if( op
->o_conn
->c_txn_backend
== NULL
) {
370 op
->o_conn
->c_txn_backend
= op
->o_bd
;
372 } else if( op
->o_conn
->c_txn_backend
!= op
->o_bd
) {
373 rs
->sr_text
= "transaction cannot span multiple database contexts";
374 rs
->sr_err
= LDAP_AFFECTS_MULTIPLE_DSAS
;
378 /* insert operation into transaction */
380 rs
->sr_text
= "transaction specified";
381 rs
->sr_err
= LDAP_X_TXN_SPECIFY_OKAY
;
384 /* release connection lock */
385 ldap_pvt_thread_mutex_unlock( &op
->o_conn
->c_mutex
);
388 send_ldap_result( op
, rs
);
394 ctrls
[num_ctrls
] = NULL
;
396 slap_mods_opattrs( op
, &op
->orm_modlist
, 1 );
399 retry
: /* transaction retry */
400 if ( dummy
.e_attrs
) {
401 attrs_free( dummy
.e_attrs
);
402 dummy
.e_attrs
= NULL
;
405 bdb_unlocked_cache_return_entry_w(&bdb
->bi_cache
, e
);
408 Debug(LDAP_DEBUG_TRACE
,
409 LDAP_XSTRING(bdb_modify
) ": retrying...\n", 0, 0, 0);
411 rs
->sr_err
= TXN_ABORT( ltid
);
413 LDAP_SLIST_REMOVE( &op
->o_extra
, &opinfo
.boi_oe
, OpExtra
, oe_next
);
414 opinfo
.boi_oe
.oe_key
= NULL
;
415 op
->o_do_not_cache
= opinfo
.boi_acl_cache
;
416 if( rs
->sr_err
!= 0 ) {
417 rs
->sr_err
= LDAP_OTHER
;
418 rs
->sr_text
= "internal error";
421 if ( op
->o_abandon
) {
422 rs
->sr_err
= SLAPD_ABANDON
;
425 bdb_trans_backoff( ++num_retries
);
428 /* begin transaction */
429 rs
->sr_err
= TXN_BEGIN( bdb
->bi_dbenv
, NULL
, <id
,
430 bdb
->bi_db_opflags
);
432 if( rs
->sr_err
!= 0 ) {
433 Debug( LDAP_DEBUG_TRACE
,
434 LDAP_XSTRING(bdb_modify
) ": txn_begin failed: "
435 "%s (%d)\n", db_strerror(rs
->sr_err
), rs
->sr_err
, 0 );
436 rs
->sr_err
= LDAP_OTHER
;
437 rs
->sr_text
= "internal error";
441 locker
= TXN_ID ( ltid
);
443 opinfo
.boi_oe
.oe_key
= bdb
;
444 opinfo
.boi_txn
= ltid
;
446 opinfo
.boi_acl_cache
= op
->o_do_not_cache
;
447 LDAP_SLIST_INSERT_HEAD( &op
->o_extra
, &opinfo
.boi_oe
, oe_next
);
449 /* get entry or ancestor */
450 rs
->sr_err
= bdb_dn2entry( op
, ltid
, &op
->o_req_ndn
, &ei
, 1,
453 if ( rs
->sr_err
!= 0 ) {
454 Debug( LDAP_DEBUG_TRACE
,
455 LDAP_XSTRING(bdb_modify
) ": dn2entry failed (%d)\n",
457 switch( rs
->sr_err
) {
458 case DB_LOCK_DEADLOCK
:
459 case DB_LOCK_NOTGRANTED
:
462 if ( BER_BVISEMPTY( &op
->o_req_ndn
)) {
463 struct berval gluebv
= BER_BVC("glue");
464 e
= ch_calloc( 1, sizeof(Entry
));
465 e
->e_name
.bv_val
= ch_strdup( "" );
466 ber_dupbv( &e
->e_nname
, &e
->e_name
);
467 attr_merge_one( e
, slap_schema
.si_ad_objectClass
,
469 attr_merge_one( e
, slap_schema
.si_ad_structuralObjectClass
,
477 rs
->sr_text
= "ldap server busy";
480 rs
->sr_err
= LDAP_OTHER
;
481 rs
->sr_text
= "internal error";
490 /* acquire and lock entry */
491 /* FIXME: dn2entry() should return non-glue entry */
492 if (( rs
->sr_err
== DB_NOTFOUND
) ||
493 ( !manageDSAit
&& e
&& is_entry_glue( e
)))
496 rs
->sr_matched
= ch_strdup( e
->e_dn
);
497 rs
->sr_ref
= is_entry_referral( e
)
498 ? get_entry_referrals( op
, e
)
500 bdb_unlocked_cache_return_entry_r (&bdb
->bi_cache
, e
);
504 rs
->sr_ref
= referral_rewrite( default_referral
, NULL
,
505 &op
->o_req_dn
, LDAP_SCOPE_DEFAULT
);
508 rs
->sr_err
= LDAP_REFERRAL
;
509 send_ldap_result( op
, rs
);
511 if ( rs
->sr_ref
!= default_referral
) {
512 ber_bvarray_free( rs
->sr_ref
);
514 free( (char *)rs
->sr_matched
);
516 rs
->sr_matched
= NULL
;
521 if ( !manageDSAit
&& is_entry_referral( e
) ) {
522 /* entry is a referral, don't allow modify */
523 rs
->sr_ref
= get_entry_referrals( op
, e
);
525 Debug( LDAP_DEBUG_TRACE
,
526 LDAP_XSTRING(bdb_modify
) ": entry is referral\n",
529 rs
->sr_err
= LDAP_REFERRAL
;
530 rs
->sr_matched
= e
->e_name
.bv_val
;
531 send_ldap_result( op
, rs
);
533 ber_bvarray_free( rs
->sr_ref
);
535 rs
->sr_matched
= NULL
;
539 if ( get_assert( op
) &&
540 ( test_filter( op
, e
, get_assertion( op
)) != LDAP_COMPARE_TRUE
))
542 rs
->sr_err
= LDAP_ASSERTION_FAILED
;
546 if( op
->o_preread
) {
547 if( preread_ctrl
== NULL
) {
548 preread_ctrl
= &ctrls
[num_ctrls
++];
549 ctrls
[num_ctrls
] = NULL
;
551 if ( slap_read_controls( op
, rs
, e
,
552 &slap_pre_read_bv
, preread_ctrl
) )
554 Debug( LDAP_DEBUG_TRACE
,
555 "<=- " LDAP_XSTRING(bdb_modify
) ": pre-read "
556 "failed!\n", 0, 0, 0 );
557 if ( op
->o_preread
& SLAP_CONTROL_CRITICAL
) {
558 /* FIXME: is it correct to abort
559 * operation if control fails? */
565 /* nested transaction */
566 rs
->sr_err
= TXN_BEGIN( bdb
->bi_dbenv
, ltid
, <2
, bdb
->bi_db_opflags
);
568 if( rs
->sr_err
!= 0 ) {
569 Debug( LDAP_DEBUG_TRACE
,
570 LDAP_XSTRING(bdb_modify
) ": txn_begin(2) failed: " "%s (%d)\n",
571 db_strerror(rs
->sr_err
), rs
->sr_err
, 0 );
572 rs
->sr_err
= LDAP_OTHER
;
573 rs
->sr_text
= "internal error";
576 /* Modify the entry */
578 rs
->sr_err
= bdb_modify_internal( op
, lt2
, op
->orm_modlist
,
579 &dummy
, &rs
->sr_text
, textbuf
, textlen
);
581 if( rs
->sr_err
!= LDAP_SUCCESS
) {
582 Debug( LDAP_DEBUG_TRACE
,
583 LDAP_XSTRING(bdb_modify
) ": modify failed (%d)\n",
585 if ( (rs
->sr_err
== LDAP_INSUFFICIENT_ACCESS
) && opinfo
.boi_err
) {
586 rs
->sr_err
= opinfo
.boi_err
;
588 /* Only free attrs if they were dup'd. */
589 if ( dummy
.e_attrs
== e
->e_attrs
) dummy
.e_attrs
= NULL
;
590 switch( rs
->sr_err
) {
591 case DB_LOCK_DEADLOCK
:
592 case DB_LOCK_NOTGRANTED
:
598 /* change the entry itself */
599 rs
->sr_err
= bdb_id2entry_update( op
->o_bd
, lt2
, &dummy
);
600 if ( rs
->sr_err
!= 0 ) {
601 Debug( LDAP_DEBUG_TRACE
,
602 LDAP_XSTRING(bdb_modify
) ": id2entry update failed " "(%d)\n",
604 switch( rs
->sr_err
) {
605 case DB_LOCK_DEADLOCK
:
606 case DB_LOCK_NOTGRANTED
:
609 rs
->sr_text
= "entry update failed";
613 if ( TXN_COMMIT( lt2
, 0 ) != 0 ) {
614 rs
->sr_err
= LDAP_OTHER
;
615 rs
->sr_text
= "txn_commit(2) failed";
619 if( op
->o_postread
) {
620 if( postread_ctrl
== NULL
) {
621 postread_ctrl
= &ctrls
[num_ctrls
++];
622 ctrls
[num_ctrls
] = NULL
;
624 if( slap_read_controls( op
, rs
, &dummy
,
625 &slap_post_read_bv
, postread_ctrl
) )
627 Debug( LDAP_DEBUG_TRACE
,
628 "<=- " LDAP_XSTRING(bdb_modify
)
629 ": post-read failed!\n", 0, 0, 0 );
630 if ( op
->o_postread
& SLAP_CONTROL_CRITICAL
) {
631 /* FIXME: is it correct to abort
632 * operation if control fails? */
639 if ( ( rs
->sr_err
= TXN_ABORT( ltid
) ) != 0 ) {
640 rs
->sr_text
= "txn_abort (no-op) failed";
642 rs
->sr_err
= LDAP_X_NO_OPERATION
;
644 /* Only free attrs if they were dup'd. */
645 if ( dummy
.e_attrs
== e
->e_attrs
) dummy
.e_attrs
= NULL
;
649 /* may have changed in bdb_modify_internal() */
650 e
->e_ocflags
= dummy
.e_ocflags
;
655 attrs_free( dummy
.e_attrs
);
658 rc
= bdb_cache_modify( bdb
, e
, dummy
.e_attrs
, locker
, &lock
);
660 case DB_LOCK_DEADLOCK
:
661 case DB_LOCK_NOTGRANTED
:
665 dummy
.e_attrs
= NULL
;
667 rs
->sr_err
= TXN_COMMIT( ltid
, 0 );
670 LDAP_SLIST_REMOVE( &op
->o_extra
, &opinfo
.boi_oe
, OpExtra
, oe_next
);
671 opinfo
.boi_oe
.oe_key
= NULL
;
673 if( rs
->sr_err
!= 0 ) {
674 Debug( LDAP_DEBUG_TRACE
,
675 LDAP_XSTRING(bdb_modify
) ": txn_%s failed: %s (%d)\n",
676 op
->o_noop
? "abort (no-op)" : "commit",
677 db_strerror(rs
->sr_err
), rs
->sr_err
);
678 rs
->sr_err
= LDAP_OTHER
;
679 rs
->sr_text
= "commit failed";
684 Debug( LDAP_DEBUG_TRACE
,
685 LDAP_XSTRING(bdb_modify
) ": updated%s id=%08lx dn=\"%s\"\n",
686 op
->o_noop
? " (no-op)" : "",
687 dummy
.e_id
, op
->o_req_dn
.bv_val
);
689 rs
->sr_err
= LDAP_SUCCESS
;
691 if( num_ctrls
) rs
->sr_ctrls
= ctrls
;
694 if( dummy
.e_attrs
) {
695 attrs_free( dummy
.e_attrs
);
697 send_ldap_result( op
, rs
);
699 if( rs
->sr_err
== LDAP_SUCCESS
&& bdb
->bi_txn_cp_kbyte
) {
700 TXN_CHECKPOINT( bdb
->bi_dbenv
,
701 bdb
->bi_txn_cp_kbyte
, bdb
->bi_txn_cp_min
, 0 );
705 slap_graduate_commit_csn( op
);
710 if ( opinfo
.boi_oe
.oe_key
) {
711 LDAP_SLIST_REMOVE( &op
->o_extra
, &opinfo
.boi_oe
, OpExtra
, oe_next
);
715 bdb_unlocked_cache_return_entry_w (&bdb
->bi_cache
, e
);
718 if( preread_ctrl
!= NULL
&& (*preread_ctrl
) != NULL
) {
719 slap_sl_free( (*preread_ctrl
)->ldctl_value
.bv_val
, op
->o_tmpmemctx
);
720 slap_sl_free( *preread_ctrl
, op
->o_tmpmemctx
);
722 if( postread_ctrl
!= NULL
&& (*postread_ctrl
) != NULL
) {
723 slap_sl_free( (*postread_ctrl
)->ldctl_value
.bv_val
, op
->o_tmpmemctx
);
724 slap_sl_free( *postread_ctrl
, op
->o_tmpmemctx
);