~ttx/openldap/lucid-gssapi-495418

« back to all changes in this revision

Viewing changes to servers/slapd/back-bdb/add.c

  • Committer: Bazaar Package Importer
  • Author(s): Mathias Gug
  • Date: 2008-07-10 14:45:49 UTC
  • Revision ID: james.westby@ubuntu.com-20080710144549-wck73med0e72gfyo
Tags: upstream-2.4.10
ImportĀ upstreamĀ versionĀ 2.4.10

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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/>.
 
4
 *
 
5
 * Copyright 2000-2008 The OpenLDAP Foundation.
 
6
 * All rights reserved.
 
7
 *
 
8
 * Redistribution and use in source and binary forms, with or without
 
9
 * modification, are permitted only as authorized by the OpenLDAP
 
10
 * Public License.
 
11
 *
 
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>.
 
15
 */
 
16
 
 
17
#include "portable.h"
 
18
 
 
19
#include <stdio.h>
 
20
#include <ac/string.h>
 
21
 
 
22
#include "back-bdb.h"
 
23
 
 
24
int
 
25
bdb_add(Operation *op, SlapReply *rs )
 
26
{
 
27
        struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private;
 
28
        struct berval   pdn;
 
29
        Entry           *p = NULL, *oe = op->ora_e;
 
30
        EntryInfo       *ei;
 
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;
 
36
        ID eid = NOID;
 
37
        struct bdb_op_info opinfo = {0};
 
38
        int subentry;
 
39
        BDB_LOCKER      locker = 0, rlocker = 0;
 
40
        DB_LOCK         lock;
 
41
 
 
42
        int             num_retries = 0;
 
43
        int             success;
 
44
 
 
45
        LDAPControl **postread_ctrl = NULL;
 
46
        LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
 
47
        int num_ctrls = 0;
 
48
 
 
49
#ifdef LDAP_X_TXN
 
50
        int settle = 0;
 
51
#endif
 
52
 
 
53
        Debug(LDAP_DEBUG_ARGS, "==> " LDAP_XSTRING(bdb_add) ": %s\n",
 
54
                op->oq_add.rs_e->e_name.bv_val, 0, 0);
 
55
 
 
56
#ifdef LDAP_X_TXN
 
57
        if( op->o_txnSpec ) {
 
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;
 
63
                        goto txnReturn;
 
64
                } else if( op->o_conn->c_txn == CONN_TXN_SETTLE ) {
 
65
                        settle=1;
 
66
                        goto txnReturn;
 
67
                }
 
68
 
 
69
                if( op->o_conn->c_txn_backend == NULL ) {
 
70
                        op->o_conn->c_txn_backend = op->o_bd;
 
71
 
 
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;
 
75
                        goto txnReturn;
 
76
                }
 
77
 
 
78
                /* insert operation into transaction */
 
79
 
 
80
                rs->sr_text = "transaction specified";
 
81
                rs->sr_err = LDAP_X_TXN_SPECIFY_OKAY;
 
82
 
 
83
txnReturn:
 
84
                /* release connection lock */
 
85
                ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
 
86
 
 
87
                if( !settle ) {
 
88
                        send_ldap_result( op, rs );
 
89
                        return rs->sr_err;
 
90
                }
 
91
        }
 
92
#endif
 
93
 
 
94
        ctrls[num_ctrls] = 0;
 
95
 
 
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 );
 
103
                goto return_results;
 
104
        }
 
105
 
 
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 );
 
113
                goto return_results;
 
114
        }
 
115
 
 
116
        subentry = is_entry_subentry( op->oq_add.rs_e );
 
117
 
 
118
        /* Get our thread locker ID */
 
119
        rs->sr_err = LOCK_ID( bdb->bi_dbenv, &rlocker );
 
120
 
 
121
        if( 0 ) {
 
122
retry:  /* transaction retry */
 
123
                if( p ) {
 
124
                        /* free parent and reader lock */
 
125
                        if ( p != (Entry *)&slap_entry_root ) {
 
126
                                bdb_unlocked_cache_return_entry_r( bdb, p );
 
127
                        }
 
128
                        p = NULL;
 
129
                }
 
130
                rs->sr_err = TXN_ABORT( ltid );
 
131
                ltid = NULL;
 
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";
 
138
                        goto return_results;
 
139
                }
 
140
                if ( op->o_abandon ) {
 
141
                        rs->sr_err = SLAPD_ABANDON;
 
142
                        goto return_results;
 
143
                }
 
144
                bdb_trans_backoff( ++num_retries );
 
145
        }
 
146
 
 
147
        /* begin transaction */
 
148
        rs->sr_err = TXN_BEGIN( bdb->bi_dbenv, NULL, &ltid, 
 
149
                bdb->bi_db_opflags );
 
150
        rs->sr_text = NULL;
 
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";
 
157
                goto return_results;
 
158
        }
 
159
 
 
160
        locker = TXN_ID ( ltid );
 
161
 
 
162
        opinfo.boi_oe.oe_key = bdb;
 
163
        opinfo.boi_txn = ltid;
 
164
        opinfo.boi_err = 0;
 
165
        opinfo.boi_acl_cache = op->o_do_not_cache;
 
166
        LDAP_SLIST_INSERT_HEAD( &op->o_extra, &opinfo.boi_oe, oe_next );
 
167
 
 
168
        /*
 
169
         * Get the parent dn and see if the corresponding entry exists.
 
170
         */
 
171
        if ( be_issuffix( op->o_bd, &op->oq_add.rs_e->e_nname ) ) {
 
172
                pdn = slap_empty_bv;
 
173
        } else {
 
174
                dnParent( &op->oq_add.rs_e->e_nname, &pdn );
 
175
        }
 
176
 
 
177
        /* get entry or parent */
 
178
        rs->sr_err = bdb_dn2entry( op, ltid, &op->ora_e->e_nname, &ei,
 
179
                1, locker, &lock );
 
180
        switch( rs->sr_err ) {
 
181
        case 0:
 
182
                rs->sr_err = LDAP_ALREADY_EXISTS;
 
183
                goto return_results;
 
184
        case DB_NOTFOUND:
 
185
                break;
 
186
        case DB_LOCK_DEADLOCK:
 
187
        case DB_LOCK_NOTGRANTED:
 
188
                goto retry;
 
189
        case LDAP_BUSY:
 
190
                rs->sr_text = "ldap server busy";
 
191
                goto return_results;
 
192
        default:
 
193
                rs->sr_err = LDAP_OTHER;
 
194
                rs->sr_text = "internal error";
 
195
                goto return_results;
 
196
        }
 
197
 
 
198
        p = ei->bei_e;
 
199
        if ( !p )
 
200
                p = (Entry *)&slap_entry_root;
 
201
 
 
202
        if ( !bvmatch( &pdn, &p->e_nname ) ) {
 
203
                rs->sr_matched = ber_strdup_x( p->e_name.bv_val,
 
204
                        op->o_tmpmemctx );
 
205
                rs->sr_ref = is_entry_referral( p )
 
206
                        ? get_entry_referrals( op, p )
 
207
                        : NULL;
 
208
                bdb_unlocked_cache_return_entry_r( bdb, p );
 
209
                p = NULL;
 
210
                Debug( LDAP_DEBUG_TRACE,
 
211
                        LDAP_XSTRING(bdb_add) ": parent "
 
212
                        "does not exist\n", 0, 0, 0 );
 
213
 
 
214
                rs->sr_err = LDAP_REFERRAL;
 
215
                rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
 
216
                goto return_results;
 
217
        }
 
218
 
 
219
        rs->sr_err = access_allowed( op, p,
 
220
                children, NULL, ACL_WADD, NULL );
 
221
 
 
222
        if ( ! rs->sr_err ) {
 
223
                switch( opinfo.boi_err ) {
 
224
                case DB_LOCK_DEADLOCK:
 
225
                case DB_LOCK_NOTGRANTED:
 
226
                        goto retry;
 
227
                }
 
228
 
 
229
                Debug( LDAP_DEBUG_TRACE,
 
230
                        LDAP_XSTRING(bdb_add) ": no write access to parent\n",
 
231
                        0, 0, 0 );
 
232
                rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
 
233
                rs->sr_text = "no write access to parent";
 
234
                goto return_results;;
 
235
        }
 
236
 
 
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",
 
242
                                0, 0, 0 );
 
243
                        rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
 
244
                        rs->sr_text = "parent is a subentry";
 
245
                        goto return_results;;
 
246
                }
 
247
 
 
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",
 
252
                                0, 0, 0 );
 
253
                        rs->sr_err = LDAP_ALIAS_PROBLEM;
 
254
                        rs->sr_text = "parent is an alias";
 
255
                        goto return_results;;
 
256
                }
 
257
 
 
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,
 
261
                                op->o_tmpmemctx );
 
262
                        rs->sr_ref = get_entry_referrals( op, p );
 
263
                        bdb_unlocked_cache_return_entry_r( bdb, p );
 
264
                        p = NULL;
 
265
                        Debug( LDAP_DEBUG_TRACE,
 
266
                                LDAP_XSTRING(bdb_add) ": parent is referral\n",
 
267
                                0, 0, 0 );
 
268
 
 
269
                        rs->sr_err = LDAP_REFERRAL;
 
270
                        rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
 
271
                        goto return_results;
 
272
                }
 
273
 
 
274
        }
 
275
 
 
276
        if ( subentry ) {
 
277
                /* FIXME: */
 
278
                /* parent must be an administrative point of the required kind */
 
279
        }
 
280
 
 
281
        /* free parent and reader lock */
 
282
        if ( p != (Entry *)&slap_entry_root ) {
 
283
                bdb_unlocked_cache_return_entry_r( bdb, p );
 
284
        }
 
285
        p = NULL;
 
286
 
 
287
        rs->sr_err = access_allowed( op, op->oq_add.rs_e,
 
288
                entry, NULL, ACL_WADD, NULL );
 
289
 
 
290
        if ( ! rs->sr_err ) {
 
291
                switch( opinfo.boi_err ) {
 
292
                case DB_LOCK_DEADLOCK:
 
293
                case DB_LOCK_NOTGRANTED:
 
294
                        goto retry;
 
295
                }
 
296
 
 
297
                Debug( LDAP_DEBUG_TRACE,
 
298
                        LDAP_XSTRING(bdb_add) ": no write access to entry\n",
 
299
                        0, 0, 0 );
 
300
                rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
 
301
                rs->sr_text = "no write access to entry";
 
302
                goto return_results;;
 
303
        }
 
304
 
 
305
        if ( eid == NOID ) {
 
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",
 
310
                                rs->sr_err, 0, 0 );
 
311
                        rs->sr_err = LDAP_OTHER;
 
312
                        rs->sr_text = "internal error";
 
313
                        goto return_results;
 
314
                }
 
315
                op->oq_add.rs_e->e_id = eid;
 
316
        }
 
317
 
 
318
        /* nested transaction */
 
319
        rs->sr_err = TXN_BEGIN( bdb->bi_dbenv, ltid, &lt2, 
 
320
                bdb->bi_db_opflags );
 
321
        rs->sr_text = NULL;
 
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";
 
328
                goto return_results;
 
329
        }
 
330
 
 
331
        /* dn2id index */
 
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 );
 
337
 
 
338
                switch( rs->sr_err ) {
 
339
                case DB_LOCK_DEADLOCK:
 
340
                case DB_LOCK_NOTGRANTED:
 
341
                        goto retry;
 
342
                case DB_KEYEXIST:
 
343
                        rs->sr_err = LDAP_ALREADY_EXISTS;
 
344
                        break;
 
345
                default:
 
346
                        rs->sr_err = LDAP_OTHER;
 
347
                }
 
348
                goto return_results;
 
349
        }
 
350
 
 
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",
 
356
                        0, 0, 0 );
 
357
                switch( rs->sr_err ) {
 
358
                case DB_LOCK_DEADLOCK:
 
359
                case DB_LOCK_NOTGRANTED:
 
360
                        goto retry;
 
361
                default:
 
362
                        rs->sr_err = LDAP_OTHER;
 
363
                }
 
364
                rs->sr_text = "index generation failed";
 
365
                goto return_results;
 
366
        }
 
367
 
 
368
        /* id2entry index */
 
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",
 
373
                        0, 0, 0 );
 
374
                switch( rs->sr_err ) {
 
375
                case DB_LOCK_DEADLOCK:
 
376
                case DB_LOCK_NOTGRANTED:
 
377
                        goto retry;
 
378
                default:
 
379
                        rs->sr_err = LDAP_OTHER;
 
380
                }
 
381
                rs->sr_text = "entry store failed";
 
382
                goto return_results;
 
383
        }
 
384
 
 
385
        if ( TXN_COMMIT( lt2, 0 ) != 0 ) {
 
386
                rs->sr_err = LDAP_OTHER;
 
387
                rs->sr_text = "txn_commit(2) failed";
 
388
                goto return_results;
 
389
        }
 
390
 
 
391
        /* post-read */
 
392
        if( op->o_postread ) {
 
393
                if( postread_ctrl == NULL ) {
 
394
                        postread_ctrl = &ctrls[num_ctrls++];
 
395
                        ctrls[num_ctrls] = NULL;
 
396
                }
 
397
                if ( slap_read_controls( op, rs, op->oq_add.rs_e,
 
398
                        &slap_post_read_bv, postread_ctrl ) )
 
399
                {
 
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? */
 
406
                                goto return_results;
 
407
                        }
 
408
                }
 
409
        }
 
410
 
 
411
        if ( op->o_noop ) {
 
412
                if (( rs->sr_err=TXN_ABORT( ltid )) != 0 ) {
 
413
                        rs->sr_text = "txn_abort (no-op) failed";
 
414
                } else {
 
415
                        rs->sr_err = LDAP_X_NO_OPERATION;
 
416
                        ltid = NULL;
 
417
                        goto return_results;
 
418
                }
 
419
 
 
420
        } else {
 
421
                struct berval nrdn;
 
422
 
 
423
                /* pick the RDN if not suffix; otherwise pick the entire DN */
 
424
                if (pdn.bv_len) {
 
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;
 
427
                } else {
 
428
                        nrdn = op->ora_e->e_nname;
 
429
                }
 
430
 
 
431
                /* Use the thread locker here, outside the txn */
 
432
                bdb_cache_add( bdb, ei, op->ora_e, &nrdn, rlocker, &lock );
 
433
 
 
434
                if(( rs->sr_err=TXN_COMMIT( ltid, 0 )) != 0 ) {
 
435
                        rs->sr_text = "txn_commit failed";
 
436
                } else {
 
437
                        rs->sr_err = LDAP_SUCCESS;
 
438
                }
 
439
        }
 
440
 
 
441
        ltid = NULL;
 
442
        LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.boi_oe, OpExtra, oe_next );
 
443
        opinfo.boi_oe.oe_key = NULL;
 
444
 
 
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;
 
450
                goto return_results;
 
451
        }
 
452
 
 
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 );
 
457
 
 
458
        rs->sr_text = NULL;
 
459
        if( num_ctrls ) rs->sr_ctrls = ctrls;
 
460
 
 
461
return_results:
 
462
        success = rs->sr_err;
 
463
        send_ldap_result( op, rs );
 
464
        slap_graduate_commit_csn( op );
 
465
 
 
466
        if( ltid != NULL ) {
 
467
                TXN_ABORT( ltid );
 
468
        }
 
469
        if ( opinfo.boi_oe.oe_key ) {
 
470
                LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.boi_oe, OpExtra, oe_next );
 
471
        }
 
472
 
 
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.
 
478
                 */
 
479
                bdb_cache_return_entry_r( bdb, oe, &lock );
 
480
                if ( op->ora_e == oe )
 
481
                        op->ora_e = NULL;
 
482
 
 
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 );
 
486
                }
 
487
        }
 
488
 
 
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 );
 
492
        }
 
493
 
 
494
        return rs->sr_err;
 
495
}