1 /* add.c - ldap BerkeleyDB back-end add routine */
2 /* $OpenLDAP: pkg/ldap/servers/slapd/back-bdb/add.c,v 1.152.2.10 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 bdb_add(Operation
*op
, SlapReply
*rs
)
27 struct bdb_info
*bdb
= (struct bdb_info
*) op
->o_bd
->be_private
;
29 Entry
*p
= NULL
, *oe
= op
->ora_e
;
31 char textbuf
[SLAP_TEXT_BUFLEN
];
32 size_t textlen
= sizeof textbuf
;
33 AttributeDescription
*children
= slap_schema
.si_ad_children
;
34 AttributeDescription
*entry
= slap_schema
.si_ad_entry
;
35 DB_TXN
*ltid
= NULL
, *lt2
;
37 struct bdb_op_info opinfo
= {0};
39 BDB_LOCKER locker
= 0, rlocker
= 0;
45 LDAPControl
**postread_ctrl
= NULL
;
46 LDAPControl
*ctrls
[SLAP_MAX_RESPONSE_CONTROLS
];
53 Debug(LDAP_DEBUG_ARGS
, "==> " LDAP_XSTRING(bdb_add
) ": %s\n",
54 op
->oq_add
.rs_e
->e_name
.bv_val
, 0, 0);
58 /* acquire connection lock */
59 ldap_pvt_thread_mutex_lock( &op
->o_conn
->c_mutex
);
60 if( op
->o_conn
->c_txn
== CONN_TXN_INACTIVE
) {
61 rs
->sr_text
= "invalid transaction identifier";
62 rs
->sr_err
= LDAP_X_TXN_ID_INVALID
;
64 } else if( op
->o_conn
->c_txn
== CONN_TXN_SETTLE
) {
69 if( op
->o_conn
->c_txn_backend
== NULL
) {
70 op
->o_conn
->c_txn_backend
= op
->o_bd
;
72 } else if( op
->o_conn
->c_txn_backend
!= op
->o_bd
) {
73 rs
->sr_text
= "transaction cannot span multiple database contexts";
74 rs
->sr_err
= LDAP_AFFECTS_MULTIPLE_DSAS
;
78 /* insert operation into transaction */
80 rs
->sr_text
= "transaction specified";
81 rs
->sr_err
= LDAP_X_TXN_SPECIFY_OKAY
;
84 /* release connection lock */
85 ldap_pvt_thread_mutex_unlock( &op
->o_conn
->c_mutex
);
88 send_ldap_result( op
, rs
);
96 /* check entry's schema */
97 rs
->sr_err
= entry_schema_check( op
, op
->oq_add
.rs_e
, NULL
,
98 get_relax(op
), 1, &rs
->sr_text
, textbuf
, textlen
);
99 if ( rs
->sr_err
!= LDAP_SUCCESS
) {
100 Debug( LDAP_DEBUG_TRACE
,
101 LDAP_XSTRING(bdb_add
) ": entry failed schema check: "
102 "%s (%d)\n", rs
->sr_text
, rs
->sr_err
, 0 );
106 /* add opattrs to shadow as well, only missing attrs will actually
107 * be added; helps compatibility with older OL versions */
108 rs
->sr_err
= slap_add_opattrs( op
, &rs
->sr_text
, textbuf
, textlen
, 1 );
109 if ( rs
->sr_err
!= LDAP_SUCCESS
) {
110 Debug( LDAP_DEBUG_TRACE
,
111 LDAP_XSTRING(bdb_add
) ": entry failed op attrs add: "
112 "%s (%d)\n", rs
->sr_text
, rs
->sr_err
, 0 );
116 subentry
= is_entry_subentry( op
->oq_add
.rs_e
);
118 /* Get our thread locker ID */
119 rs
->sr_err
= LOCK_ID( bdb
->bi_dbenv
, &rlocker
);
122 retry
: /* transaction retry */
124 /* free parent and reader lock */
125 if ( p
!= (Entry
*)&slap_entry_root
) {
126 bdb_unlocked_cache_return_entry_r( bdb
, p
);
130 rs
->sr_err
= TXN_ABORT( ltid
);
132 LDAP_SLIST_REMOVE( &op
->o_extra
, &opinfo
.boi_oe
, OpExtra
, oe_next
);
133 opinfo
.boi_oe
.oe_key
= NULL
;
134 op
->o_do_not_cache
= opinfo
.boi_acl_cache
;
135 if( rs
->sr_err
!= 0 ) {
136 rs
->sr_err
= LDAP_OTHER
;
137 rs
->sr_text
= "internal error";
140 if ( op
->o_abandon
) {
141 rs
->sr_err
= SLAPD_ABANDON
;
144 bdb_trans_backoff( ++num_retries
);
147 /* begin transaction */
148 rs
->sr_err
= TXN_BEGIN( bdb
->bi_dbenv
, NULL
, <id
,
149 bdb
->bi_db_opflags
);
151 if( rs
->sr_err
!= 0 ) {
152 Debug( LDAP_DEBUG_TRACE
,
153 LDAP_XSTRING(bdb_add
) ": txn_begin failed: %s (%d)\n",
154 db_strerror(rs
->sr_err
), rs
->sr_err
, 0 );
155 rs
->sr_err
= LDAP_OTHER
;
156 rs
->sr_text
= "internal error";
160 locker
= TXN_ID ( ltid
);
162 opinfo
.boi_oe
.oe_key
= bdb
;
163 opinfo
.boi_txn
= ltid
;
165 opinfo
.boi_acl_cache
= op
->o_do_not_cache
;
166 LDAP_SLIST_INSERT_HEAD( &op
->o_extra
, &opinfo
.boi_oe
, oe_next
);
169 * Get the parent dn and see if the corresponding entry exists.
171 if ( be_issuffix( op
->o_bd
, &op
->oq_add
.rs_e
->e_nname
) ) {
174 dnParent( &op
->oq_add
.rs_e
->e_nname
, &pdn
);
177 /* get entry or parent */
178 rs
->sr_err
= bdb_dn2entry( op
, ltid
, &op
->ora_e
->e_nname
, &ei
,
180 switch( rs
->sr_err
) {
182 rs
->sr_err
= LDAP_ALREADY_EXISTS
;
186 case DB_LOCK_DEADLOCK
:
187 case DB_LOCK_NOTGRANTED
:
190 rs
->sr_text
= "ldap server busy";
193 rs
->sr_err
= LDAP_OTHER
;
194 rs
->sr_text
= "internal error";
200 p
= (Entry
*)&slap_entry_root
;
202 if ( !bvmatch( &pdn
, &p
->e_nname
) ) {
203 rs
->sr_matched
= ber_strdup_x( p
->e_name
.bv_val
,
205 rs
->sr_ref
= is_entry_referral( p
)
206 ? get_entry_referrals( op
, p
)
208 bdb_unlocked_cache_return_entry_r( bdb
, p
);
210 Debug( LDAP_DEBUG_TRACE
,
211 LDAP_XSTRING(bdb_add
) ": parent "
212 "does not exist\n", 0, 0, 0 );
214 rs
->sr_err
= LDAP_REFERRAL
;
215 rs
->sr_flags
= REP_MATCHED_MUSTBEFREED
| REP_REF_MUSTBEFREED
;
219 rs
->sr_err
= access_allowed( op
, p
,
220 children
, NULL
, ACL_WADD
, NULL
);
222 if ( ! rs
->sr_err
) {
223 switch( opinfo
.boi_err
) {
224 case DB_LOCK_DEADLOCK
:
225 case DB_LOCK_NOTGRANTED
:
229 Debug( LDAP_DEBUG_TRACE
,
230 LDAP_XSTRING(bdb_add
) ": no write access to parent\n",
232 rs
->sr_err
= LDAP_INSUFFICIENT_ACCESS
;
233 rs
->sr_text
= "no write access to parent";
234 goto return_results
;;
237 if ( p
!= (Entry
*)&slap_entry_root
) {
238 if ( is_entry_subentry( p
) ) {
239 /* parent is a subentry, don't allow add */
240 Debug( LDAP_DEBUG_TRACE
,
241 LDAP_XSTRING(bdb_add
) ": parent is subentry\n",
243 rs
->sr_err
= LDAP_OBJECT_CLASS_VIOLATION
;
244 rs
->sr_text
= "parent is a subentry";
245 goto return_results
;;
248 if ( is_entry_alias( p
) ) {
249 /* parent is an alias, don't allow add */
250 Debug( LDAP_DEBUG_TRACE
,
251 LDAP_XSTRING(bdb_add
) ": parent is alias\n",
253 rs
->sr_err
= LDAP_ALIAS_PROBLEM
;
254 rs
->sr_text
= "parent is an alias";
255 goto return_results
;;
258 if ( is_entry_referral( p
) ) {
259 /* parent is a referral, don't allow add */
260 rs
->sr_matched
= ber_strdup_x( p
->e_name
.bv_val
,
262 rs
->sr_ref
= get_entry_referrals( op
, p
);
263 bdb_unlocked_cache_return_entry_r( bdb
, p
);
265 Debug( LDAP_DEBUG_TRACE
,
266 LDAP_XSTRING(bdb_add
) ": parent is referral\n",
269 rs
->sr_err
= LDAP_REFERRAL
;
270 rs
->sr_flags
= REP_MATCHED_MUSTBEFREED
| REP_REF_MUSTBEFREED
;
278 /* parent must be an administrative point of the required kind */
281 /* free parent and reader lock */
282 if ( p
!= (Entry
*)&slap_entry_root
) {
283 bdb_unlocked_cache_return_entry_r( bdb
, p
);
287 rs
->sr_err
= access_allowed( op
, op
->oq_add
.rs_e
,
288 entry
, NULL
, ACL_WADD
, NULL
);
290 if ( ! rs
->sr_err
) {
291 switch( opinfo
.boi_err
) {
292 case DB_LOCK_DEADLOCK
:
293 case DB_LOCK_NOTGRANTED
:
297 Debug( LDAP_DEBUG_TRACE
,
298 LDAP_XSTRING(bdb_add
) ": no write access to entry\n",
300 rs
->sr_err
= LDAP_INSUFFICIENT_ACCESS
;
301 rs
->sr_text
= "no write access to entry";
302 goto return_results
;;
306 rs
->sr_err
= bdb_next_id( op
->o_bd
, &eid
);
307 if( rs
->sr_err
!= 0 ) {
308 Debug( LDAP_DEBUG_TRACE
,
309 LDAP_XSTRING(bdb_add
) ": next_id failed (%d)\n",
311 rs
->sr_err
= LDAP_OTHER
;
312 rs
->sr_text
= "internal error";
315 op
->oq_add
.rs_e
->e_id
= eid
;
318 /* nested transaction */
319 rs
->sr_err
= TXN_BEGIN( bdb
->bi_dbenv
, ltid
, <2
,
320 bdb
->bi_db_opflags
);
322 if( rs
->sr_err
!= 0 ) {
323 Debug( LDAP_DEBUG_TRACE
,
324 LDAP_XSTRING(bdb_add
) ": txn_begin(2) failed: "
325 "%s (%d)\n", db_strerror(rs
->sr_err
), rs
->sr_err
, 0 );
326 rs
->sr_err
= LDAP_OTHER
;
327 rs
->sr_text
= "internal error";
332 rs
->sr_err
= bdb_dn2id_add( op
, lt2
, ei
, op
->oq_add
.rs_e
);
333 if ( rs
->sr_err
!= 0 ) {
334 Debug( LDAP_DEBUG_TRACE
,
335 LDAP_XSTRING(bdb_add
) ": dn2id_add failed: %s (%d)\n",
336 db_strerror(rs
->sr_err
), rs
->sr_err
, 0 );
338 switch( rs
->sr_err
) {
339 case DB_LOCK_DEADLOCK
:
340 case DB_LOCK_NOTGRANTED
:
343 rs
->sr_err
= LDAP_ALREADY_EXISTS
;
346 rs
->sr_err
= LDAP_OTHER
;
351 /* attribute indexes */
352 rs
->sr_err
= bdb_index_entry_add( op
, lt2
, op
->oq_add
.rs_e
);
353 if ( rs
->sr_err
!= LDAP_SUCCESS
) {
354 Debug( LDAP_DEBUG_TRACE
,
355 LDAP_XSTRING(bdb_add
) ": index_entry_add failed\n",
357 switch( rs
->sr_err
) {
358 case DB_LOCK_DEADLOCK
:
359 case DB_LOCK_NOTGRANTED
:
362 rs
->sr_err
= LDAP_OTHER
;
364 rs
->sr_text
= "index generation failed";
369 rs
->sr_err
= bdb_id2entry_add( op
->o_bd
, lt2
, op
->oq_add
.rs_e
);
370 if ( rs
->sr_err
!= 0 ) {
371 Debug( LDAP_DEBUG_TRACE
,
372 LDAP_XSTRING(bdb_add
) ": id2entry_add failed\n",
374 switch( rs
->sr_err
) {
375 case DB_LOCK_DEADLOCK
:
376 case DB_LOCK_NOTGRANTED
:
379 rs
->sr_err
= LDAP_OTHER
;
381 rs
->sr_text
= "entry store failed";
385 if ( TXN_COMMIT( lt2
, 0 ) != 0 ) {
386 rs
->sr_err
= LDAP_OTHER
;
387 rs
->sr_text
= "txn_commit(2) failed";
392 if( op
->o_postread
) {
393 if( postread_ctrl
== NULL
) {
394 postread_ctrl
= &ctrls
[num_ctrls
++];
395 ctrls
[num_ctrls
] = NULL
;
397 if ( slap_read_controls( op
, rs
, op
->oq_add
.rs_e
,
398 &slap_post_read_bv
, postread_ctrl
) )
400 Debug( LDAP_DEBUG_TRACE
,
401 "<=- " LDAP_XSTRING(bdb_add
) ": post-read "
402 "failed!\n", 0, 0, 0 );
403 if ( op
->o_postread
& SLAP_CONTROL_CRITICAL
) {
404 /* FIXME: is it correct to abort
405 * operation if control fails? */
412 if (( rs
->sr_err
=TXN_ABORT( ltid
)) != 0 ) {
413 rs
->sr_text
= "txn_abort (no-op) failed";
415 rs
->sr_err
= LDAP_X_NO_OPERATION
;
423 /* pick the RDN if not suffix; otherwise pick the entire DN */
425 nrdn
.bv_val
= op
->ora_e
->e_nname
.bv_val
;
426 nrdn
.bv_len
= pdn
.bv_val
- op
->ora_e
->e_nname
.bv_val
- 1;
428 nrdn
= op
->ora_e
->e_nname
;
431 /* Use the thread locker here, outside the txn */
432 bdb_cache_add( bdb
, ei
, op
->ora_e
, &nrdn
, rlocker
, &lock
);
434 if(( rs
->sr_err
=TXN_COMMIT( ltid
, 0 )) != 0 ) {
435 rs
->sr_text
= "txn_commit failed";
437 rs
->sr_err
= LDAP_SUCCESS
;
442 LDAP_SLIST_REMOVE( &op
->o_extra
, &opinfo
.boi_oe
, OpExtra
, oe_next
);
443 opinfo
.boi_oe
.oe_key
= NULL
;
445 if ( rs
->sr_err
!= LDAP_SUCCESS
) {
446 Debug( LDAP_DEBUG_TRACE
,
447 LDAP_XSTRING(bdb_add
) ": %s : %s (%d)\n",
448 rs
->sr_text
, db_strerror(rs
->sr_err
), rs
->sr_err
);
449 rs
->sr_err
= LDAP_OTHER
;
453 Debug(LDAP_DEBUG_TRACE
,
454 LDAP_XSTRING(bdb_add
) ": added%s id=%08lx dn=\"%s\"\n",
455 op
->o_noop
? " (no-op)" : "",
456 op
->oq_add
.rs_e
->e_id
, op
->oq_add
.rs_e
->e_dn
);
459 if( num_ctrls
) rs
->sr_ctrls
= ctrls
;
462 success
= rs
->sr_err
;
463 send_ldap_result( op
, rs
);
464 slap_graduate_commit_csn( op
);
469 if ( opinfo
.boi_oe
.oe_key
) {
470 LDAP_SLIST_REMOVE( &op
->o_extra
, &opinfo
.boi_oe
, OpExtra
, oe_next
);
473 if( success
== LDAP_SUCCESS
) {
474 /* We own the entry now, and it can be purged at will
475 * Check to make sure it's the same entry we entered with.
476 * Possibly a callback may have mucked with it, although
477 * in general callbacks should treat the entry as read-only.
479 bdb_cache_return_entry_r( bdb
, oe
, &lock
);
480 if ( op
->ora_e
== oe
)
483 if ( bdb
->bi_txn_cp_kbyte
) {
484 TXN_CHECKPOINT( bdb
->bi_dbenv
,
485 bdb
->bi_txn_cp_kbyte
, bdb
->bi_txn_cp_min
, 0 );
489 if( postread_ctrl
!= NULL
&& (*postread_ctrl
) != NULL
) {
490 slap_sl_free( (*postread_ctrl
)->ldctl_value
.bv_val
, op
->o_tmpmemctx
);
491 slap_sl_free( *postread_ctrl
, op
->o_tmpmemctx
);