Back to index

openldap  2.4.31
add.c
Go to the documentation of this file.
00001 /* add.c - ldap BerkeleyDB back-end add routine */
00002 /* $OpenLDAP$ */
00003 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
00004  *
00005  * Copyright 2000-2012 The OpenLDAP Foundation.
00006  * All rights reserved.
00007  *
00008  * Redistribution and use in source and binary forms, with or without
00009  * modification, are permitted only as authorized by the OpenLDAP
00010  * Public License.
00011  *
00012  * A copy of this license is available in the file LICENSE in the
00013  * top-level directory of the distribution or, alternatively, at
00014  * <http://www.OpenLDAP.org/license.html>.
00015  */
00016 
00017 #include "portable.h"
00018 
00019 #include <stdio.h>
00020 #include <ac/string.h>
00021 
00022 #include "back-bdb.h"
00023 
00024 int
00025 bdb_add(Operation *op, SlapReply *rs )
00026 {
00027        struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private;
00028        struct berval pdn;
00029        Entry         *p = NULL, *oe = op->ora_e;
00030        EntryInfo     *ei;
00031        char textbuf[SLAP_TEXT_BUFLEN];
00032        size_t textlen = sizeof textbuf;
00033        AttributeDescription *children = slap_schema.si_ad_children;
00034        AttributeDescription *entry = slap_schema.si_ad_entry;
00035        DB_TXN        *ltid = NULL, *lt2;
00036        ID eid = NOID;
00037        struct bdb_op_info opinfo = {{{ 0 }}};
00038        int subentry;
00039        DB_LOCK              lock;
00040 
00041        int           num_retries = 0;
00042        int           success;
00043 
00044        LDAPControl **postread_ctrl = NULL;
00045        LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
00046        int num_ctrls = 0;
00047 
00048 #ifdef LDAP_X_TXN
00049        int settle = 0;
00050 #endif
00051 
00052        Debug(LDAP_DEBUG_ARGS, "==> " LDAP_XSTRING(bdb_add) ": %s\n",
00053               op->ora_e->e_name.bv_val, 0, 0);
00054 
00055 #ifdef LDAP_X_TXN
00056        if( op->o_txnSpec ) {
00057               /* acquire connection lock */
00058               ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
00059               if( op->o_conn->c_txn == CONN_TXN_INACTIVE ) {
00060                      rs->sr_text = "invalid transaction identifier";
00061                      rs->sr_err = LDAP_X_TXN_ID_INVALID;
00062                      goto txnReturn;
00063               } else if( op->o_conn->c_txn == CONN_TXN_SETTLE ) {
00064                      settle=1;
00065                      goto txnReturn;
00066               }
00067 
00068               if( op->o_conn->c_txn_backend == NULL ) {
00069                      op->o_conn->c_txn_backend = op->o_bd;
00070 
00071               } else if( op->o_conn->c_txn_backend != op->o_bd ) {
00072                      rs->sr_text = "transaction cannot span multiple database contexts";
00073                      rs->sr_err = LDAP_AFFECTS_MULTIPLE_DSAS;
00074                      goto txnReturn;
00075               }
00076 
00077               /* insert operation into transaction */
00078 
00079               rs->sr_text = "transaction specified";
00080               rs->sr_err = LDAP_X_TXN_SPECIFY_OKAY;
00081 
00082 txnReturn:
00083               /* release connection lock */
00084               ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
00085 
00086               if( !settle ) {
00087                      send_ldap_result( op, rs );
00088                      return rs->sr_err;
00089               }
00090        }
00091 #endif
00092 
00093        ctrls[num_ctrls] = 0;
00094 
00095        /* check entry's schema */
00096        rs->sr_err = entry_schema_check( op, op->ora_e, NULL,
00097               get_relax(op), 1, NULL, &rs->sr_text, textbuf, textlen );
00098        if ( rs->sr_err != LDAP_SUCCESS ) {
00099               Debug( LDAP_DEBUG_TRACE,
00100                      LDAP_XSTRING(bdb_add) ": entry failed schema check: "
00101                      "%s (%d)\n", rs->sr_text, rs->sr_err, 0 );
00102               goto return_results;
00103        }
00104 
00105        /* add opattrs to shadow as well, only missing attrs will actually
00106         * be added; helps compatibility with older OL versions */
00107        rs->sr_err = slap_add_opattrs( op, &rs->sr_text, textbuf, textlen, 1 );
00108        if ( rs->sr_err != LDAP_SUCCESS ) {
00109               Debug( LDAP_DEBUG_TRACE,
00110                      LDAP_XSTRING(bdb_add) ": entry failed op attrs add: "
00111                      "%s (%d)\n", rs->sr_text, rs->sr_err, 0 );
00112               goto return_results;
00113        }
00114 
00115        if ( get_assert( op ) &&
00116               ( test_filter( op, op->ora_e, get_assertion( op )) != LDAP_COMPARE_TRUE ))
00117        {
00118               rs->sr_err = LDAP_ASSERTION_FAILED;
00119               goto return_results;
00120        }
00121 
00122        subentry = is_entry_subentry( op->ora_e );
00123 
00124        if( 0 ) {
00125 retry: /* transaction retry */
00126               if( p ) {
00127                      /* free parent and reader lock */
00128                      if ( p != (Entry *)&slap_entry_root ) {
00129                             bdb_unlocked_cache_return_entry_r( bdb, p );
00130                      }
00131                      p = NULL;
00132               }
00133               rs->sr_err = TXN_ABORT( ltid );
00134               ltid = NULL;
00135               LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.boi_oe, OpExtra, oe_next );
00136               opinfo.boi_oe.oe_key = NULL;
00137               op->o_do_not_cache = opinfo.boi_acl_cache;
00138               if( rs->sr_err != 0 ) {
00139                      rs->sr_err = LDAP_OTHER;
00140                      rs->sr_text = "internal error";
00141                      goto return_results;
00142               }
00143               if ( op->o_abandon ) {
00144                      rs->sr_err = SLAPD_ABANDON;
00145                      goto return_results;
00146               }
00147               bdb_trans_backoff( ++num_retries );
00148        }
00149 
00150        /* begin transaction */
00151        rs->sr_err = TXN_BEGIN( bdb->bi_dbenv, NULL, &ltid, 
00152               bdb->bi_db_opflags );
00153        rs->sr_text = NULL;
00154        if( rs->sr_err != 0 ) {
00155               Debug( LDAP_DEBUG_TRACE,
00156                      LDAP_XSTRING(bdb_add) ": txn_begin failed: %s (%d)\n",
00157                      db_strerror(rs->sr_err), rs->sr_err, 0 );
00158               rs->sr_err = LDAP_OTHER;
00159               rs->sr_text = "internal error";
00160               goto return_results;
00161        }
00162 
00163        opinfo.boi_oe.oe_key = bdb;
00164        opinfo.boi_txn = ltid;
00165        opinfo.boi_err = 0;
00166        opinfo.boi_acl_cache = op->o_do_not_cache;
00167        LDAP_SLIST_INSERT_HEAD( &op->o_extra, &opinfo.boi_oe, oe_next );
00168 
00169        /*
00170         * Get the parent dn and see if the corresponding entry exists.
00171         */
00172        if ( be_issuffix( op->o_bd, &op->ora_e->e_nname ) ) {
00173               pdn = slap_empty_bv;
00174        } else {
00175               dnParent( &op->ora_e->e_nname, &pdn );
00176        }
00177 
00178        /* get entry or parent */
00179        rs->sr_err = bdb_dn2entry( op, ltid, &op->ora_e->e_nname, &ei,
00180               1, &lock );
00181        switch( rs->sr_err ) {
00182        case 0:
00183               rs->sr_err = LDAP_ALREADY_EXISTS;
00184               goto return_results;
00185        case DB_NOTFOUND:
00186               break;
00187        case DB_LOCK_DEADLOCK:
00188        case DB_LOCK_NOTGRANTED:
00189               goto retry;
00190        case LDAP_BUSY:
00191               rs->sr_text = "ldap server busy";
00192               goto return_results;
00193        default:
00194               rs->sr_err = LDAP_OTHER;
00195               rs->sr_text = "internal error";
00196               goto return_results;
00197        }
00198 
00199        p = ei->bei_e;
00200        if ( !p )
00201               p = (Entry *)&slap_entry_root;
00202 
00203        if ( !bvmatch( &pdn, &p->e_nname ) ) {
00204               rs->sr_matched = ber_strdup_x( p->e_name.bv_val,
00205                      op->o_tmpmemctx );
00206               rs->sr_ref = is_entry_referral( p )
00207                      ? get_entry_referrals( op, p )
00208                      : NULL;
00209               if ( p != (Entry *)&slap_entry_root )
00210                      bdb_unlocked_cache_return_entry_r( bdb, p );
00211               p = NULL;
00212               Debug( LDAP_DEBUG_TRACE,
00213                      LDAP_XSTRING(bdb_add) ": parent "
00214                      "does not exist\n", 0, 0, 0 );
00215 
00216               rs->sr_err = LDAP_REFERRAL;
00217               rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
00218               goto return_results;
00219        }
00220 
00221        rs->sr_err = access_allowed( op, p,
00222               children, NULL, ACL_WADD, NULL );
00223 
00224        if ( ! rs->sr_err ) {
00225               switch( opinfo.boi_err ) {
00226               case DB_LOCK_DEADLOCK:
00227               case DB_LOCK_NOTGRANTED:
00228                      goto retry;
00229               }
00230 
00231               if ( p != (Entry *)&slap_entry_root )
00232                      bdb_unlocked_cache_return_entry_r( bdb, p );
00233               p = NULL;
00234 
00235               Debug( LDAP_DEBUG_TRACE,
00236                      LDAP_XSTRING(bdb_add) ": no write access to parent\n",
00237                      0, 0, 0 );
00238               rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
00239               rs->sr_text = "no write access to parent";
00240               goto return_results;;
00241        }
00242 
00243        if ( p != (Entry *)&slap_entry_root ) {
00244               if ( is_entry_subentry( p ) ) {
00245                      bdb_unlocked_cache_return_entry_r( bdb, p );
00246                      p = NULL;
00247                      /* parent is a subentry, don't allow add */
00248                      Debug( LDAP_DEBUG_TRACE,
00249                             LDAP_XSTRING(bdb_add) ": parent is subentry\n",
00250                             0, 0, 0 );
00251                      rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
00252                      rs->sr_text = "parent is a subentry";
00253                      goto return_results;;
00254               }
00255 
00256               if ( is_entry_alias( p ) ) {
00257                      bdb_unlocked_cache_return_entry_r( bdb, p );
00258                      p = NULL;
00259                      /* parent is an alias, don't allow add */
00260                      Debug( LDAP_DEBUG_TRACE,
00261                             LDAP_XSTRING(bdb_add) ": parent is alias\n",
00262                             0, 0, 0 );
00263                      rs->sr_err = LDAP_ALIAS_PROBLEM;
00264                      rs->sr_text = "parent is an alias";
00265                      goto return_results;;
00266               }
00267 
00268               if ( is_entry_referral( p ) ) {
00269                      /* parent is a referral, don't allow add */
00270                      rs->sr_matched = ber_strdup_x( p->e_name.bv_val,
00271                             op->o_tmpmemctx );
00272                      rs->sr_ref = get_entry_referrals( op, p );
00273                      bdb_unlocked_cache_return_entry_r( bdb, p );
00274                      p = NULL;
00275                      Debug( LDAP_DEBUG_TRACE,
00276                             LDAP_XSTRING(bdb_add) ": parent is referral\n",
00277                             0, 0, 0 );
00278 
00279                      rs->sr_err = LDAP_REFERRAL;
00280                      rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
00281                      goto return_results;
00282               }
00283 
00284        }
00285 
00286        if ( subentry ) {
00287               /* FIXME: */
00288               /* parent must be an administrative point of the required kind */
00289        }
00290 
00291        /* free parent and reader lock */
00292        if ( p != (Entry *)&slap_entry_root ) {
00293               if ( p->e_nname.bv_len ) {
00294                      struct berval ppdn;
00295 
00296                      /* ITS#5326: use parent's DN if differs from provided one */
00297                      dnParent( &op->ora_e->e_name, &ppdn );
00298                      if ( !dn_match( &p->e_name, &ppdn ) ) {
00299                             struct berval rdn;
00300                             struct berval newdn;
00301 
00302                             dnRdn( &op->ora_e->e_name, &rdn );
00303 
00304                             build_new_dn( &newdn, &p->e_name, &rdn, NULL ); 
00305                             if ( op->ora_e->e_name.bv_val != op->o_req_dn.bv_val )
00306                                    ber_memfree( op->ora_e->e_name.bv_val );
00307                             op->ora_e->e_name = newdn;
00308 
00309                             /* FIXME: should check whether
00310                              * dnNormalize(newdn) == e->e_nname ... */
00311                      }
00312               }
00313 
00314               bdb_unlocked_cache_return_entry_r( bdb, p );
00315        }
00316        p = NULL;
00317 
00318        rs->sr_err = access_allowed( op, op->ora_e,
00319               entry, NULL, ACL_WADD, NULL );
00320 
00321        if ( ! rs->sr_err ) {
00322               switch( opinfo.boi_err ) {
00323               case DB_LOCK_DEADLOCK:
00324               case DB_LOCK_NOTGRANTED:
00325                      goto retry;
00326               }
00327 
00328               Debug( LDAP_DEBUG_TRACE,
00329                      LDAP_XSTRING(bdb_add) ": no write access to entry\n",
00330                      0, 0, 0 );
00331               rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
00332               rs->sr_text = "no write access to entry";
00333               goto return_results;;
00334        }
00335 
00336        /* 
00337         * Check ACL for attribute write access
00338         */
00339        if (!acl_check_modlist(op, oe, op->ora_modlist)) {
00340               switch( opinfo.boi_err ) {
00341               case DB_LOCK_DEADLOCK:
00342               case DB_LOCK_NOTGRANTED:
00343                      goto retry;
00344               }
00345 
00346               Debug( LDAP_DEBUG_TRACE,
00347                      LDAP_XSTRING(bdb_add) ": no write access to attribute\n",
00348                      0, 0, 0 );
00349               rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
00350               rs->sr_text = "no write access to attribute";
00351               goto return_results;;
00352        }
00353 
00354        if ( eid == NOID ) {
00355               rs->sr_err = bdb_next_id( op->o_bd, &eid );
00356               if( rs->sr_err != 0 ) {
00357                      Debug( LDAP_DEBUG_TRACE,
00358                             LDAP_XSTRING(bdb_add) ": next_id failed (%d)\n",
00359                             rs->sr_err, 0, 0 );
00360                      rs->sr_err = LDAP_OTHER;
00361                      rs->sr_text = "internal error";
00362                      goto return_results;
00363               }
00364               op->ora_e->e_id = eid;
00365        }
00366 
00367        /* nested transaction */
00368        rs->sr_err = TXN_BEGIN( bdb->bi_dbenv, ltid, &lt2, 
00369               bdb->bi_db_opflags );
00370        rs->sr_text = NULL;
00371        if( rs->sr_err != 0 ) {
00372               Debug( LDAP_DEBUG_TRACE,
00373                      LDAP_XSTRING(bdb_add) ": txn_begin(2) failed: "
00374                      "%s (%d)\n", db_strerror(rs->sr_err), rs->sr_err, 0 );
00375               rs->sr_err = LDAP_OTHER;
00376               rs->sr_text = "internal error";
00377               goto return_results;
00378        }
00379 
00380        /* dn2id index */
00381        rs->sr_err = bdb_dn2id_add( op, lt2, ei, op->ora_e );
00382        if ( rs->sr_err != 0 ) {
00383               Debug( LDAP_DEBUG_TRACE,
00384                      LDAP_XSTRING(bdb_add) ": dn2id_add failed: %s (%d)\n",
00385                      db_strerror(rs->sr_err), rs->sr_err, 0 );
00386 
00387               switch( rs->sr_err ) {
00388               case DB_LOCK_DEADLOCK:
00389               case DB_LOCK_NOTGRANTED:
00390                      goto retry;
00391               case DB_KEYEXIST:
00392                      rs->sr_err = LDAP_ALREADY_EXISTS;
00393                      break;
00394               default:
00395                      rs->sr_err = LDAP_OTHER;
00396               }
00397               goto return_results;
00398        }
00399 
00400        /* attribute indexes */
00401        rs->sr_err = bdb_index_entry_add( op, lt2, op->ora_e );
00402        if ( rs->sr_err != LDAP_SUCCESS ) {
00403               Debug( LDAP_DEBUG_TRACE,
00404                      LDAP_XSTRING(bdb_add) ": index_entry_add failed\n",
00405                      0, 0, 0 );
00406               switch( rs->sr_err ) {
00407               case DB_LOCK_DEADLOCK:
00408               case DB_LOCK_NOTGRANTED:
00409                      goto retry;
00410               default:
00411                      rs->sr_err = LDAP_OTHER;
00412               }
00413               rs->sr_text = "index generation failed";
00414               goto return_results;
00415        }
00416 
00417        /* id2entry index */
00418        rs->sr_err = bdb_id2entry_add( op->o_bd, lt2, op->ora_e );
00419        if ( rs->sr_err != 0 ) {
00420               Debug( LDAP_DEBUG_TRACE,
00421                      LDAP_XSTRING(bdb_add) ": id2entry_add failed\n",
00422                      0, 0, 0 );
00423               switch( rs->sr_err ) {
00424               case DB_LOCK_DEADLOCK:
00425               case DB_LOCK_NOTGRANTED:
00426                      goto retry;
00427               default:
00428                      rs->sr_err = LDAP_OTHER;
00429               }
00430               rs->sr_text = "entry store failed";
00431               goto return_results;
00432        }
00433 
00434        if ( TXN_COMMIT( lt2, 0 ) != 0 ) {
00435               rs->sr_err = LDAP_OTHER;
00436               rs->sr_text = "txn_commit(2) failed";
00437               goto return_results;
00438        }
00439 
00440        /* post-read */
00441        if( op->o_postread ) {
00442               if( postread_ctrl == NULL ) {
00443                      postread_ctrl = &ctrls[num_ctrls++];
00444                      ctrls[num_ctrls] = NULL;
00445               }
00446               if ( slap_read_controls( op, rs, op->ora_e,
00447                      &slap_post_read_bv, postread_ctrl ) )
00448               {
00449                      Debug( LDAP_DEBUG_TRACE,
00450                             "<=- " LDAP_XSTRING(bdb_add) ": post-read "
00451                             "failed!\n", 0, 0, 0 );
00452                      if ( op->o_postread & SLAP_CONTROL_CRITICAL ) {
00453                             /* FIXME: is it correct to abort
00454                              * operation if control fails? */
00455                             goto return_results;
00456                      }
00457               }
00458        }
00459 
00460        if ( op->o_noop ) {
00461               if (( rs->sr_err=TXN_ABORT( ltid )) != 0 ) {
00462                      rs->sr_text = "txn_abort (no-op) failed";
00463               } else {
00464                      rs->sr_err = LDAP_X_NO_OPERATION;
00465                      ltid = NULL;
00466                      goto return_results;
00467               }
00468 
00469        } else {
00470               struct berval nrdn;
00471 
00472               /* pick the RDN if not suffix; otherwise pick the entire DN */
00473               if (pdn.bv_len) {
00474                      nrdn.bv_val = op->ora_e->e_nname.bv_val;
00475                      nrdn.bv_len = pdn.bv_val - op->ora_e->e_nname.bv_val - 1;
00476               } else {
00477                      nrdn = op->ora_e->e_nname;
00478               }
00479 
00480               bdb_cache_add( bdb, ei, op->ora_e, &nrdn, ltid, &lock );
00481 
00482               if(( rs->sr_err=TXN_COMMIT( ltid, 0 )) != 0 ) {
00483                      rs->sr_text = "txn_commit failed";
00484               } else {
00485                      rs->sr_err = LDAP_SUCCESS;
00486               }
00487        }
00488 
00489        ltid = NULL;
00490        LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.boi_oe, OpExtra, oe_next );
00491        opinfo.boi_oe.oe_key = NULL;
00492 
00493        if ( rs->sr_err != LDAP_SUCCESS ) {
00494               Debug( LDAP_DEBUG_TRACE,
00495                      LDAP_XSTRING(bdb_add) ": %s : %s (%d)\n",
00496                      rs->sr_text, db_strerror(rs->sr_err), rs->sr_err );
00497               rs->sr_err = LDAP_OTHER;
00498               goto return_results;
00499        }
00500 
00501        Debug(LDAP_DEBUG_TRACE,
00502               LDAP_XSTRING(bdb_add) ": added%s id=%08lx dn=\"%s\"\n",
00503               op->o_noop ? " (no-op)" : "",
00504               op->ora_e->e_id, op->ora_e->e_dn );
00505 
00506        rs->sr_text = NULL;
00507        if( num_ctrls ) rs->sr_ctrls = ctrls;
00508 
00509 return_results:
00510        success = rs->sr_err;
00511        send_ldap_result( op, rs );
00512 
00513        if( ltid != NULL ) {
00514               TXN_ABORT( ltid );
00515        }
00516        if ( opinfo.boi_oe.oe_key ) {
00517               LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.boi_oe, OpExtra, oe_next );
00518        }
00519 
00520        if( success == LDAP_SUCCESS ) {
00521               /* We own the entry now, and it can be purged at will
00522                * Check to make sure it's the same entry we entered with.
00523                * Possibly a callback may have mucked with it, although
00524                * in general callbacks should treat the entry as read-only.
00525                */
00526               bdb_cache_deref( oe->e_private );
00527               if ( op->ora_e == oe )
00528                      op->ora_e = NULL;
00529 
00530               if ( bdb->bi_txn_cp_kbyte ) {
00531                      TXN_CHECKPOINT( bdb->bi_dbenv,
00532                             bdb->bi_txn_cp_kbyte, bdb->bi_txn_cp_min, 0 );
00533               }
00534        }
00535 
00536        slap_graduate_commit_csn( op );
00537 
00538        if( postread_ctrl != NULL && (*postread_ctrl) != NULL ) {
00539               slap_sl_free( (*postread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
00540               slap_sl_free( *postread_ctrl, op->o_tmpmemctx );
00541        }
00542        return rs->sr_err;
00543 }