Back to index

openldap  2.4.31
add.c
Go to the documentation of this file.
00001 /* add.c - ldap mdb 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-mdb.h"
00023 
00024 int
00025 mdb_add(Operation *op, SlapReply *rs )
00026 {
00027        struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
00028        struct berval pdn;
00029        Entry         *p = NULL, *oe = op->ora_e;
00030        char textbuf[SLAP_TEXT_BUFLEN];
00031        size_t textlen = sizeof textbuf;
00032        AttributeDescription *children = slap_schema.si_ad_children;
00033        AttributeDescription *entry = slap_schema.si_ad_entry;
00034        MDB_txn              *txn = NULL;
00035        MDB_cursor    *mc = NULL;
00036        MDB_cursor    *mcd;
00037        ID eid, pid = 0;
00038        mdb_op_info opinfo = {{{ 0 }}}, *moi = &opinfo;
00039        int subentry;
00040 
00041        int           success;
00042 
00043        LDAPControl **postread_ctrl = NULL;
00044        LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
00045        int num_ctrls = 0;
00046 
00047 #ifdef LDAP_X_TXN
00048        int settle = 0;
00049 #endif
00050 
00051        Debug(LDAP_DEBUG_ARGS, "==> " LDAP_XSTRING(mdb_add) ": %s\n",
00052               op->ora_e->e_name.bv_val, 0, 0);
00053 
00054 #ifdef LDAP_X_TXN
00055        if( op->o_txnSpec ) {
00056               /* acquire connection lock */
00057               ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
00058               if( op->o_conn->c_txn == CONN_TXN_INACTIVE ) {
00059                      rs->sr_text = "invalid transaction identifier";
00060                      rs->sr_err = LDAP_X_TXN_ID_INVALID;
00061                      goto txnReturn;
00062               } else if( op->o_conn->c_txn == CONN_TXN_SETTLE ) {
00063                      settle=1;
00064                      goto txnReturn;
00065               }
00066 
00067               if( op->o_conn->c_txn_backend == NULL ) {
00068                      op->o_conn->c_txn_backend = op->o_bd;
00069 
00070               } else if( op->o_conn->c_txn_backend != op->o_bd ) {
00071                      rs->sr_text = "transaction cannot span multiple database contexts";
00072                      rs->sr_err = LDAP_AFFECTS_MULTIPLE_DSAS;
00073                      goto txnReturn;
00074               }
00075 
00076               /* insert operation into transaction */
00077 
00078               rs->sr_text = "transaction specified";
00079               rs->sr_err = LDAP_X_TXN_SPECIFY_OKAY;
00080 
00081 txnReturn:
00082               /* release connection lock */
00083               ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
00084 
00085               if( !settle ) {
00086                      send_ldap_result( op, rs );
00087                      return rs->sr_err;
00088               }
00089        }
00090 #endif
00091 
00092        ctrls[num_ctrls] = 0;
00093 
00094        /* check entry's schema */
00095        rs->sr_err = entry_schema_check( op, op->ora_e, NULL,
00096               get_relax(op), 1, NULL, &rs->sr_text, textbuf, textlen );
00097        if ( rs->sr_err != LDAP_SUCCESS ) {
00098               Debug( LDAP_DEBUG_TRACE,
00099                      LDAP_XSTRING(mdb_add) ": entry failed schema check: "
00100                      "%s (%d)\n", rs->sr_text, rs->sr_err, 0 );
00101               goto return_results;
00102        }
00103 
00104        /* add opattrs to shadow as well, only missing attrs will actually
00105         * be added; helps compatibility with older OL versions */
00106        rs->sr_err = slap_add_opattrs( op, &rs->sr_text, textbuf, textlen, 1 );
00107        if ( rs->sr_err != LDAP_SUCCESS ) {
00108               Debug( LDAP_DEBUG_TRACE,
00109                      LDAP_XSTRING(mdb_add) ": entry failed op attrs add: "
00110                      "%s (%d)\n", rs->sr_text, rs->sr_err, 0 );
00111               goto return_results;
00112        }
00113 
00114        if ( get_assert( op ) &&
00115               ( test_filter( op, op->ora_e, get_assertion( op )) != LDAP_COMPARE_TRUE ))
00116        {
00117               rs->sr_err = LDAP_ASSERTION_FAILED;
00118               goto return_results;
00119        }
00120 
00121        subentry = is_entry_subentry( op->ora_e );
00122 
00123        /* begin transaction */
00124        rs->sr_err = mdb_opinfo_get( op, mdb, 0, &moi );
00125        rs->sr_text = NULL;
00126        if( rs->sr_err != 0 ) {
00127               Debug( LDAP_DEBUG_TRACE,
00128                      LDAP_XSTRING(mdb_add) ": txn_begin failed: %s (%d)\n",
00129                      mdb_strerror(rs->sr_err), rs->sr_err, 0 );
00130               rs->sr_err = LDAP_OTHER;
00131               rs->sr_text = "internal error";
00132               goto return_results;
00133        }
00134 
00135        txn = moi->moi_txn;
00136 
00137        /*
00138         * Get the parent dn and see if the corresponding entry exists.
00139         */
00140        if ( be_issuffix( op->o_bd, &op->ora_e->e_nname ) ) {
00141               pdn = slap_empty_bv;
00142        } else {
00143               dnParent( &op->ora_e->e_nname, &pdn );
00144        }
00145 
00146        rs->sr_err = mdb_cursor_open( txn, mdb->mi_dn2id, &mcd );
00147        if( rs->sr_err != 0 ) {
00148               Debug( LDAP_DEBUG_TRACE,
00149                      LDAP_XSTRING(mdb_add) ": mdb_cursor_open failed (%d)\n",
00150                      rs->sr_err, 0, 0 );
00151               rs->sr_err = LDAP_OTHER;
00152               rs->sr_text = "internal error";
00153               goto return_results;
00154        }
00155 
00156        /* get entry or parent */
00157        rs->sr_err = mdb_dn2entry( op, txn, mcd, &op->ora_e->e_nname, &p, 1 );
00158        switch( rs->sr_err ) {
00159        case 0:
00160               rs->sr_err = LDAP_ALREADY_EXISTS;
00161               mdb_entry_return( op, p );
00162               p = NULL;
00163               goto return_results;
00164        case MDB_NOTFOUND:
00165               break;
00166        case LDAP_BUSY:
00167               rs->sr_text = "ldap server busy";
00168               goto return_results;
00169        default:
00170               rs->sr_err = LDAP_OTHER;
00171               rs->sr_text = "internal error";
00172               goto return_results;
00173        }
00174 
00175        if ( !p )
00176               p = (Entry *)&slap_entry_root;
00177 
00178        if ( !bvmatch( &pdn, &p->e_nname ) ) {
00179               rs->sr_matched = ber_strdup_x( p->e_name.bv_val,
00180                      op->o_tmpmemctx );
00181               if ( p != (Entry *)&slap_entry_root && is_entry_referral( p )) {
00182                      BerVarray ref = get_entry_referrals( op, p );
00183                      rs->sr_ref = referral_rewrite( ref, &p->e_name,
00184                             &op->o_req_dn, LDAP_SCOPE_DEFAULT );
00185                      ber_bvarray_free( ref );
00186               } else {
00187                      rs->sr_ref = NULL;
00188               }
00189               if ( p != (Entry *)&slap_entry_root )
00190                      mdb_entry_return( op, p );
00191               p = NULL;
00192               Debug( LDAP_DEBUG_TRACE,
00193                      LDAP_XSTRING(mdb_add) ": parent "
00194                      "does not exist\n", 0, 0, 0 );
00195 
00196               rs->sr_err = LDAP_REFERRAL;
00197               rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
00198               goto return_results;
00199        }
00200 
00201        rs->sr_err = access_allowed( op, p,
00202               children, NULL, ACL_WADD, NULL );
00203 
00204        if ( ! rs->sr_err ) {
00205               if ( p != (Entry *)&slap_entry_root )
00206                      mdb_entry_return( op, p );
00207               p = NULL;
00208 
00209               Debug( LDAP_DEBUG_TRACE,
00210                      LDAP_XSTRING(mdb_add) ": no write access to parent\n",
00211                      0, 0, 0 );
00212               rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
00213               rs->sr_text = "no write access to parent";
00214               goto return_results;;
00215        }
00216 
00217        if ( p != (Entry *)&slap_entry_root ) {
00218               if ( is_entry_subentry( p ) ) {
00219                      mdb_entry_return( op, p );
00220                      p = NULL;
00221                      /* parent is a subentry, don't allow add */
00222                      Debug( LDAP_DEBUG_TRACE,
00223                             LDAP_XSTRING(mdb_add) ": parent is subentry\n",
00224                             0, 0, 0 );
00225                      rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
00226                      rs->sr_text = "parent is a subentry";
00227                      goto return_results;;
00228               }
00229 
00230               if ( is_entry_alias( p ) ) {
00231                      mdb_entry_return( op, p );
00232                      p = NULL;
00233                      /* parent is an alias, don't allow add */
00234                      Debug( LDAP_DEBUG_TRACE,
00235                             LDAP_XSTRING(mdb_add) ": parent is alias\n",
00236                             0, 0, 0 );
00237                      rs->sr_err = LDAP_ALIAS_PROBLEM;
00238                      rs->sr_text = "parent is an alias";
00239                      goto return_results;;
00240               }
00241 
00242               if ( is_entry_referral( p ) ) {
00243                      BerVarray ref = get_entry_referrals( op, p );
00244                      /* parent is a referral, don't allow add */
00245                      rs->sr_matched = ber_strdup_x( p->e_name.bv_val,
00246                             op->o_tmpmemctx );
00247                      rs->sr_ref = referral_rewrite( ref, &p->e_name,
00248                             &op->o_req_dn, LDAP_SCOPE_DEFAULT );
00249                      ber_bvarray_free( ref );
00250                      mdb_entry_return( op, p );
00251                      p = NULL;
00252                      Debug( LDAP_DEBUG_TRACE,
00253                             LDAP_XSTRING(mdb_add) ": parent is referral\n",
00254                             0, 0, 0 );
00255 
00256                      rs->sr_err = LDAP_REFERRAL;
00257                      rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
00258                      goto return_results;
00259               }
00260 
00261        }
00262 
00263        if ( subentry ) {
00264               /* FIXME: */
00265               /* parent must be an administrative point of the required kind */
00266        }
00267 
00268        /* free parent */
00269        if ( p != (Entry *)&slap_entry_root ) {
00270               pid = p->e_id;
00271               if ( p->e_nname.bv_len ) {
00272                      struct berval ppdn;
00273 
00274                      /* ITS#5326: use parent's DN if differs from provided one */
00275                      dnParent( &op->ora_e->e_name, &ppdn );
00276                      if ( !dn_match( &p->e_name, &ppdn ) ) {
00277                             struct berval rdn;
00278                             struct berval newdn;
00279 
00280                             dnRdn( &op->ora_e->e_name, &rdn );
00281 
00282                             build_new_dn( &newdn, &p->e_name, &rdn, NULL ); 
00283                             if ( op->ora_e->e_name.bv_val != op->o_req_dn.bv_val )
00284                                    ber_memfree( op->ora_e->e_name.bv_val );
00285                             op->ora_e->e_name = newdn;
00286 
00287                             /* FIXME: should check whether
00288                              * dnNormalize(newdn) == e->e_nname ... */
00289                      }
00290               }
00291 
00292               mdb_entry_return( op, p );
00293        }
00294        p = NULL;
00295 
00296        rs->sr_err = access_allowed( op, op->ora_e,
00297               entry, NULL, ACL_WADD, NULL );
00298 
00299        if ( ! rs->sr_err ) {
00300               Debug( LDAP_DEBUG_TRACE,
00301                      LDAP_XSTRING(mdb_add) ": no write access to entry\n",
00302                      0, 0, 0 );
00303               rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
00304               rs->sr_text = "no write access to entry";
00305               goto return_results;;
00306        }
00307 
00308        /* 
00309         * Check ACL for attribute write access
00310         */
00311        if (!acl_check_modlist(op, oe, op->ora_modlist)) {
00312               Debug( LDAP_DEBUG_TRACE,
00313                      LDAP_XSTRING(mdb_add) ": no write access to attribute\n",
00314                      0, 0, 0 );
00315               rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
00316               rs->sr_text = "no write access to attribute";
00317               goto return_results;;
00318        }
00319 
00320        rs->sr_err = mdb_cursor_open( txn, mdb->mi_id2entry, &mc );
00321        if( rs->sr_err != 0 ) {
00322               Debug( LDAP_DEBUG_TRACE,
00323                      LDAP_XSTRING(mdb_add) ": mdb_cursor_open failed (%d)\n",
00324                      rs->sr_err, 0, 0 );
00325               rs->sr_err = LDAP_OTHER;
00326               rs->sr_text = "internal error";
00327               goto return_results;
00328        }
00329 
00330        rs->sr_err = mdb_next_id( op->o_bd, mc, &eid );
00331        if( rs->sr_err != 0 ) {
00332               Debug( LDAP_DEBUG_TRACE,
00333                      LDAP_XSTRING(mdb_add) ": next_id failed (%d)\n",
00334                      rs->sr_err, 0, 0 );
00335               rs->sr_err = LDAP_OTHER;
00336               rs->sr_text = "internal error";
00337               goto return_results;
00338        }
00339        op->ora_e->e_id = eid;
00340 
00341        /* dn2id index */
00342        rs->sr_err = mdb_dn2id_add( op, mcd, mcd, pid, op->ora_e );
00343        mdb_cursor_close( mcd );
00344        if ( rs->sr_err != 0 ) {
00345               Debug( LDAP_DEBUG_TRACE,
00346                      LDAP_XSTRING(mdb_add) ": dn2id_add failed: %s (%d)\n",
00347                      mdb_strerror(rs->sr_err), rs->sr_err, 0 );
00348 
00349               switch( rs->sr_err ) {
00350               case MDB_KEYEXIST:
00351                      rs->sr_err = LDAP_ALREADY_EXISTS;
00352                      break;
00353               default:
00354                      rs->sr_err = LDAP_OTHER;
00355               }
00356               goto return_results;
00357        }
00358 
00359        /* attribute indexes */
00360        rs->sr_err = mdb_index_entry_add( op, txn, op->ora_e );
00361        if ( rs->sr_err != LDAP_SUCCESS ) {
00362               Debug( LDAP_DEBUG_TRACE,
00363                      LDAP_XSTRING(mdb_add) ": index_entry_add failed\n",
00364                      0, 0, 0 );
00365               rs->sr_err = LDAP_OTHER;
00366               rs->sr_text = "index generation failed";
00367               goto return_results;
00368        }
00369 
00370        /* id2entry index */
00371        rs->sr_err = mdb_id2entry_add( op, txn, mc, op->ora_e );
00372        if ( rs->sr_err != 0 ) {
00373               Debug( LDAP_DEBUG_TRACE,
00374                      LDAP_XSTRING(mdb_add) ": id2entry_add failed\n",
00375                      0, 0, 0 );
00376               rs->sr_err = LDAP_OTHER;
00377               rs->sr_text = "entry store failed";
00378               goto return_results;
00379        }
00380 
00381        /* post-read */
00382        if( op->o_postread ) {
00383               if( postread_ctrl == NULL ) {
00384                      postread_ctrl = &ctrls[num_ctrls++];
00385                      ctrls[num_ctrls] = NULL;
00386               }
00387               if ( slap_read_controls( op, rs, op->ora_e,
00388                      &slap_post_read_bv, postread_ctrl ) )
00389               {
00390                      Debug( LDAP_DEBUG_TRACE,
00391                             "<=- " LDAP_XSTRING(mdb_add) ": post-read "
00392                             "failed!\n", 0, 0, 0 );
00393                      if ( op->o_postread & SLAP_CONTROL_CRITICAL ) {
00394                             /* FIXME: is it correct to abort
00395                              * operation if control fails? */
00396                             goto return_results;
00397                      }
00398               }
00399        }
00400 
00401        if ( moi == &opinfo ) {
00402               LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.moi_oe, OpExtra, oe_next );
00403               opinfo.moi_oe.oe_key = NULL;
00404               if ( op->o_noop ) {
00405                      mdb_txn_abort( txn );
00406                      rs->sr_err = LDAP_X_NO_OPERATION;
00407                      txn = NULL;
00408                      goto return_results;
00409               }
00410 
00411               rs->sr_err = mdb_txn_commit( txn );
00412               txn = NULL;
00413               if ( rs->sr_err != 0 ) {
00414                      rs->sr_text = "txn_commit failed";
00415                      Debug( LDAP_DEBUG_TRACE,
00416                             LDAP_XSTRING(mdb_add) ": %s : %s (%d)\n",
00417                             rs->sr_text, mdb_strerror(rs->sr_err), rs->sr_err );
00418                      rs->sr_err = LDAP_OTHER;
00419                      goto return_results;
00420               }
00421        }
00422 
00423        Debug(LDAP_DEBUG_TRACE,
00424               LDAP_XSTRING(mdb_add) ": added%s id=%08lx dn=\"%s\"\n",
00425               op->o_noop ? " (no-op)" : "",
00426               op->ora_e->e_id, op->ora_e->e_dn );
00427 
00428        rs->sr_text = NULL;
00429        if( num_ctrls ) rs->sr_ctrls = ctrls;
00430 
00431 return_results:
00432        success = rs->sr_err;
00433        send_ldap_result( op, rs );
00434 
00435        if( moi == &opinfo ) {
00436               if( txn != NULL ) {
00437                      mdb_txn_abort( txn );
00438               }
00439               if ( opinfo.moi_oe.oe_key ) {
00440                      LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.moi_oe, OpExtra, oe_next );
00441               }
00442        }
00443 
00444        if( success == LDAP_SUCCESS ) {
00445 #if 0
00446               if ( mdb->bi_txn_cp_kbyte ) {
00447                      TXN_CHECKPOINT( mdb->bi_dbenv,
00448                             mdb->bi_txn_cp_kbyte, mdb->bi_txn_cp_min, 0 );
00449               }
00450 #endif
00451        }
00452 
00453        slap_graduate_commit_csn( op );
00454 
00455        if( postread_ctrl != NULL && (*postread_ctrl) != NULL ) {
00456               slap_sl_free( (*postread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
00457               slap_sl_free( *postread_ctrl, op->o_tmpmemctx );
00458        }
00459        return rs->sr_err;
00460 }