Home | History | Annotate | Line # | Download | only in back-mdb
add.c revision 1.1.1.7
      1 /*	$NetBSD: add.c,v 1.1.1.7 2025/09/05 21:09:50 christos Exp $	*/
      2 
      3 /* add.c - ldap mdb back-end add routine */
      4 /* $OpenLDAP$ */
      5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
      6  *
      7  * Copyright 2000-2024 The OpenLDAP Foundation.
      8  * All rights reserved.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted only as authorized by the OpenLDAP
     12  * Public License.
     13  *
     14  * A copy of this license is available in the file LICENSE in the
     15  * top-level directory of the distribution or, alternatively, at
     16  * <http://www.OpenLDAP.org/license.html>.
     17  */
     18 
     19 #include <sys/cdefs.h>
     20 __RCSID("$NetBSD: add.c,v 1.1.1.7 2025/09/05 21:09:50 christos Exp $");
     21 
     22 #include "portable.h"
     23 
     24 #include <stdio.h>
     25 #include <ac/string.h>
     26 
     27 #include "back-mdb.h"
     28 
     29 int
     30 mdb_add(Operation *op, SlapReply *rs )
     31 {
     32 	struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
     33 	struct berval	pdn;
     34 	Entry		*p = NULL, *oe = op->ora_e;
     35 	char textbuf[SLAP_TEXT_BUFLEN];
     36 	size_t textlen = sizeof textbuf;
     37 	AttributeDescription *children = slap_schema.si_ad_children;
     38 	AttributeDescription *entry = slap_schema.si_ad_entry;
     39 	MDB_txn		*txn = NULL;
     40 	MDB_cursor	*mc = NULL;
     41 	MDB_cursor	*mcd;
     42 	ID eid, pid = 0;
     43 	mdb_op_info opinfo = {{{ 0 }}}, *moi = &opinfo;
     44 	int subentry;
     45 	int numads = mdb->mi_numads;
     46 
     47 	int		success;
     48 
     49 	LDAPControl **postread_ctrl = NULL;
     50 	LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
     51 	int num_ctrls = 0;
     52 
     53 	Debug(LDAP_DEBUG_ARGS, "==> " LDAP_XSTRING(mdb_add) ": %s\n",
     54 		op->ora_e->e_name.bv_val );
     55 
     56 	ctrls[num_ctrls] = 0;
     57 
     58 	/* check entry's schema */
     59 	rs->sr_err = entry_schema_check( op, op->ora_e, NULL,
     60 		get_relax(op), 1, NULL, &rs->sr_text, textbuf, textlen );
     61 	if ( rs->sr_err != LDAP_SUCCESS ) {
     62 		Debug( LDAP_DEBUG_TRACE,
     63 			LDAP_XSTRING(mdb_add) ": entry failed schema check: "
     64 			"%s (%d)\n", rs->sr_text, rs->sr_err );
     65 		goto return_results;
     66 	}
     67 
     68 	/* begin transaction */
     69 	rs->sr_err = mdb_opinfo_get( op, mdb, 0, &moi );
     70 	rs->sr_text = NULL;
     71 	if( rs->sr_err != 0 ) {
     72 		Debug( LDAP_DEBUG_TRACE,
     73 			LDAP_XSTRING(mdb_add) ": txn_begin failed: %s (%d)\n",
     74 			mdb_strerror(rs->sr_err), rs->sr_err );
     75 		rs->sr_err = LDAP_OTHER;
     76 		rs->sr_text = "internal error";
     77 		goto return_results;
     78 	}
     79 	txn = moi->moi_txn;
     80 
     81 	/* add opattrs to shadow as well, only missing attrs will actually
     82 	 * be added; helps compatibility with older OL versions */
     83 	rs->sr_err = slap_add_opattrs( op, &rs->sr_text, textbuf, textlen, 1 );
     84 	if ( rs->sr_err != LDAP_SUCCESS ) {
     85 		Debug( LDAP_DEBUG_TRACE,
     86 			LDAP_XSTRING(mdb_add) ": entry failed op attrs add: "
     87 			"%s (%d)\n", rs->sr_text, rs->sr_err );
     88 		goto return_results;
     89 	}
     90 
     91 	if ( get_assert( op ) &&
     92 		( test_filter( op, op->ora_e, get_assertion( op )) != LDAP_COMPARE_TRUE ))
     93 	{
     94 		rs->sr_err = LDAP_ASSERTION_FAILED;
     95 		goto return_results;
     96 	}
     97 
     98 	subentry = is_entry_subentry( op->ora_e );
     99 
    100 	/*
    101 	 * Get the parent dn and see if the corresponding entry exists.
    102 	 */
    103 	if ( be_issuffix( op->o_bd, &op->ora_e->e_nname ) ) {
    104 		pdn = slap_empty_bv;
    105 	} else {
    106 		dnParent( &op->ora_e->e_nname, &pdn );
    107 	}
    108 
    109 	rs->sr_err = mdb_cursor_open( txn, mdb->mi_dn2id, &mcd );
    110 	if( rs->sr_err != 0 ) {
    111 		Debug( LDAP_DEBUG_TRACE,
    112 			LDAP_XSTRING(mdb_add) ": mdb_cursor_open failed (%d)\n",
    113 			rs->sr_err );
    114 		rs->sr_err = LDAP_OTHER;
    115 		rs->sr_text = "internal error";
    116 		goto return_results;
    117 	}
    118 
    119 	/* get entry or parent */
    120 	rs->sr_err = mdb_dn2entry( op, txn, mcd, &op->ora_e->e_nname, &p, NULL, 1 );
    121 	switch( rs->sr_err ) {
    122 	case 0:
    123 		rs->sr_err = LDAP_ALREADY_EXISTS;
    124 		mdb_entry_return( op, p );
    125 		p = NULL;
    126 		goto return_results;
    127 	case MDB_NOTFOUND:
    128 		break;
    129 	case LDAP_BUSY:
    130 		rs->sr_text = "ldap server busy";
    131 		goto return_results;
    132 	default:
    133 		rs->sr_err = LDAP_OTHER;
    134 		rs->sr_text = "internal error";
    135 		goto return_results;
    136 	}
    137 
    138 	if ( !p )
    139 		p = (Entry *)&slap_entry_root;
    140 
    141 	if ( !bvmatch( &pdn, &p->e_nname ) ) {
    142 		rs->sr_matched = ber_strdup_x( p->e_name.bv_val,
    143 			op->o_tmpmemctx );
    144 		if ( p != (Entry *)&slap_entry_root && is_entry_referral( p )) {
    145 			BerVarray ref = get_entry_referrals( op, p );
    146 			rs->sr_ref = referral_rewrite( ref, &p->e_name,
    147 				&op->o_req_dn, LDAP_SCOPE_DEFAULT );
    148 			ber_bvarray_free( ref );
    149 		} else {
    150 			rs->sr_ref = NULL;
    151 		}
    152 		if ( p != (Entry *)&slap_entry_root )
    153 			mdb_entry_return( op, p );
    154 		p = NULL;
    155 		Debug( LDAP_DEBUG_TRACE,
    156 			LDAP_XSTRING(mdb_add) ": parent "
    157 			"does not exist\n" );
    158 
    159 		rs->sr_err = LDAP_REFERRAL;
    160 		rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
    161 		goto return_results;
    162 	}
    163 
    164 	rs->sr_err = access_allowed( op, p,
    165 		children, NULL, ACL_WADD, NULL );
    166 
    167 	if ( ! rs->sr_err ) {
    168 		if ( p != (Entry *)&slap_entry_root )
    169 			mdb_entry_return( op, p );
    170 		p = NULL;
    171 
    172 		Debug( LDAP_DEBUG_TRACE,
    173 			LDAP_XSTRING(mdb_add) ": no write access to parent\n" );
    174 		rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
    175 		rs->sr_text = "no write access to parent";
    176 		goto return_results;;
    177 	}
    178 
    179 	if ( p != (Entry *)&slap_entry_root ) {
    180 		if ( is_entry_subentry( p ) ) {
    181 			mdb_entry_return( op, p );
    182 			p = NULL;
    183 			/* parent is a subentry, don't allow add */
    184 			Debug( LDAP_DEBUG_TRACE,
    185 				LDAP_XSTRING(mdb_add) ": parent is subentry\n" );
    186 			rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
    187 			rs->sr_text = "parent is a subentry";
    188 			goto return_results;;
    189 		}
    190 
    191 		if ( is_entry_alias( p ) ) {
    192 			mdb_entry_return( op, p );
    193 			p = NULL;
    194 			/* parent is an alias, don't allow add */
    195 			Debug( LDAP_DEBUG_TRACE,
    196 				LDAP_XSTRING(mdb_add) ": parent is alias\n" );
    197 			rs->sr_err = LDAP_ALIAS_PROBLEM;
    198 			rs->sr_text = "parent is an alias";
    199 			goto return_results;;
    200 		}
    201 
    202 		if ( is_entry_referral( p ) ) {
    203 			BerVarray ref = get_entry_referrals( op, p );
    204 			/* parent is a referral, don't allow add */
    205 			rs->sr_matched = ber_strdup_x( p->e_name.bv_val,
    206 				op->o_tmpmemctx );
    207 			rs->sr_ref = referral_rewrite( ref, &p->e_name,
    208 				&op->o_req_dn, LDAP_SCOPE_DEFAULT );
    209 			ber_bvarray_free( ref );
    210 			mdb_entry_return( op, p );
    211 			p = NULL;
    212 			Debug( LDAP_DEBUG_TRACE,
    213 				LDAP_XSTRING(mdb_add) ": parent is referral\n" );
    214 
    215 			rs->sr_err = LDAP_REFERRAL;
    216 			rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
    217 			goto return_results;
    218 		}
    219 
    220 	}
    221 
    222 	if ( subentry ) {
    223 		/* FIXME: */
    224 		/* parent must be an administrative point of the required kind */
    225 	}
    226 
    227 	/* free parent */
    228 	if ( p != (Entry *)&slap_entry_root ) {
    229 		pid = p->e_id;
    230 		if ( p->e_nname.bv_len ) {
    231 			struct berval ppdn;
    232 
    233 			/* ITS#5326: use parent's DN if differs from provided one */
    234 			dnParent( &op->ora_e->e_name, &ppdn );
    235 			if ( !dn_match( &p->e_name, &ppdn ) ) {
    236 				struct berval rdn;
    237 				struct berval newdn;
    238 
    239 				dnRdn( &op->ora_e->e_name, &rdn );
    240 
    241 				build_new_dn( &newdn, &p->e_name, &rdn, NULL );
    242 				if ( op->ora_e->e_name.bv_val != op->o_req_dn.bv_val )
    243 					ber_memfree( op->ora_e->e_name.bv_val );
    244 				op->ora_e->e_name = newdn;
    245 
    246 				/* FIXME: should check whether
    247 				 * dnNormalize(newdn) == e->e_nname ... */
    248 			}
    249 		}
    250 
    251 		mdb_entry_return( op, p );
    252 	}
    253 	p = NULL;
    254 
    255 	rs->sr_err = access_allowed( op, op->ora_e,
    256 		entry, NULL, ACL_WADD, NULL );
    257 
    258 	if ( ! rs->sr_err ) {
    259 		Debug( LDAP_DEBUG_TRACE,
    260 			LDAP_XSTRING(mdb_add) ": no write access to entry\n" );
    261 		rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
    262 		rs->sr_text = "no write access to entry";
    263 		goto return_results;;
    264 	}
    265 
    266 	/*
    267 	 * Check ACL for attribute write access
    268 	 */
    269 	if (!acl_check_modlist(op, oe, op->ora_modlist)) {
    270 		Debug( LDAP_DEBUG_TRACE,
    271 			LDAP_XSTRING(mdb_add) ": no write access to attribute\n" );
    272 		rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
    273 		rs->sr_text = "no write access to attribute";
    274 		goto return_results;;
    275 	}
    276 
    277 	rs->sr_err = mdb_cursor_open( txn, mdb->mi_id2entry, &mc );
    278 	if( rs->sr_err != 0 ) {
    279 		Debug( LDAP_DEBUG_TRACE,
    280 			LDAP_XSTRING(mdb_add) ": mdb_cursor_open failed (%d)\n",
    281 			rs->sr_err );
    282 		rs->sr_err = LDAP_OTHER;
    283 		rs->sr_text = "internal error";
    284 		goto return_results;
    285 	}
    286 
    287 	rs->sr_err = mdb_next_id( op->o_bd, mc, &eid );
    288 	if( rs->sr_err != 0 ) {
    289 		Debug( LDAP_DEBUG_TRACE,
    290 			LDAP_XSTRING(mdb_add) ": next_id failed (%d)\n",
    291 			rs->sr_err );
    292 		rs->sr_err = LDAP_OTHER;
    293 		rs->sr_text = "internal error";
    294 		goto return_results;
    295 	}
    296 	op->ora_e->e_id = eid;
    297 
    298 	/* dn2id index */
    299 	rs->sr_err = mdb_dn2id_add( op, mcd, mcd, pid, 1, 1, op->ora_e );
    300 	mdb_cursor_close( mcd );
    301 	if ( rs->sr_err != 0 ) {
    302 		Debug( LDAP_DEBUG_TRACE,
    303 			LDAP_XSTRING(mdb_add) ": dn2id_add failed: %s (%d)\n",
    304 			mdb_strerror(rs->sr_err), rs->sr_err );
    305 
    306 		switch( rs->sr_err ) {
    307 		case MDB_KEYEXIST:
    308 			rs->sr_err = LDAP_ALREADY_EXISTS;
    309 			break;
    310 		default:
    311 			rs->sr_err = LDAP_OTHER;
    312 		}
    313 		goto return_results;
    314 	}
    315 
    316 	/* attribute indexes */
    317 	rs->sr_err = mdb_index_entry_add( op, txn, op->ora_e );
    318 	if ( rs->sr_err != LDAP_SUCCESS ) {
    319 		Debug( LDAP_DEBUG_TRACE,
    320 			LDAP_XSTRING(mdb_add) ": index_entry_add failed\n" );
    321 		rs->sr_err = LDAP_OTHER;
    322 		rs->sr_text = "index generation failed";
    323 		goto return_results;
    324 	}
    325 
    326 	/* id2entry index */
    327 	rs->sr_err = mdb_id2entry_add( op, txn, mc, op->ora_e );
    328 	if ( rs->sr_err != 0 ) {
    329 		Debug( LDAP_DEBUG_TRACE,
    330 			LDAP_XSTRING(mdb_add) ": id2entry_add failed\n" );
    331 		if ( rs->sr_err == LDAP_ADMINLIMIT_EXCEEDED ) {
    332 			rs->sr_text = "entry is too big";
    333 		} else {
    334 			rs->sr_err = LDAP_OTHER;
    335 			rs->sr_text = "entry store failed";
    336 		}
    337 		goto return_results;
    338 	}
    339 
    340 	/* post-read */
    341 	if( op->o_postread ) {
    342 		if( postread_ctrl == NULL ) {
    343 			postread_ctrl = &ctrls[num_ctrls++];
    344 			ctrls[num_ctrls] = NULL;
    345 		}
    346 		if ( slap_read_controls( op, rs, op->ora_e,
    347 			&slap_post_read_bv, postread_ctrl ) )
    348 		{
    349 			Debug( LDAP_DEBUG_TRACE,
    350 				"<=- " LDAP_XSTRING(mdb_add) ": post-read "
    351 				"failed!\n" );
    352 			if ( op->o_postread & SLAP_CONTROL_CRITICAL ) {
    353 				/* FIXME: is it correct to abort
    354 				 * operation if control fails? */
    355 				goto return_results;
    356 			}
    357 		}
    358 	}
    359 
    360 	if ( moi == &opinfo ) {
    361 		LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.moi_oe, OpExtra, oe_next );
    362 		opinfo.moi_oe.oe_key = NULL;
    363 		if ( op->o_noop ) {
    364 			mdb->mi_numads = numads;
    365 			mdb_txn_abort( txn );
    366 			rs->sr_err = LDAP_X_NO_OPERATION;
    367 			txn = NULL;
    368 			goto return_results;
    369 		}
    370 
    371 		rs->sr_err = mdb_txn_commit( txn );
    372 		txn = NULL;
    373 		if ( rs->sr_err != 0 ) {
    374 			mdb->mi_numads = numads;
    375 			rs->sr_text = "txn_commit failed";
    376 			Debug( LDAP_DEBUG_ANY,
    377 				LDAP_XSTRING(mdb_add) ": %s : %s (%d)\n",
    378 				rs->sr_text, mdb_strerror(rs->sr_err), rs->sr_err );
    379 			rs->sr_err = LDAP_OTHER;
    380 			goto return_results;
    381 		}
    382 	}
    383 
    384 	Debug(LDAP_DEBUG_TRACE,
    385 		LDAP_XSTRING(mdb_add) ": added%s id=%08lx dn=\"%s\"\n",
    386 		op->o_noop ? " (no-op)" : "",
    387 		op->ora_e->e_id, op->ora_e->e_dn );
    388 
    389 	rs->sr_text = NULL;
    390 	if( num_ctrls ) rs->sr_ctrls = ctrls;
    391 
    392 return_results:
    393 	success = rs->sr_err;
    394 	send_ldap_result( op, rs );
    395 
    396 	if( moi == &opinfo ) {
    397 		if( txn != NULL ) {
    398 			mdb->mi_numads = numads;
    399 			mdb_txn_abort( txn );
    400 		}
    401 		if ( opinfo.moi_oe.oe_key ) {
    402 			LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.moi_oe, OpExtra, oe_next );
    403 		}
    404 	} else {
    405 		moi->moi_ref--;
    406 	}
    407 
    408 	if( success == LDAP_SUCCESS ) {
    409 #if 0
    410 		if ( mdb->bi_txn_cp_kbyte ) {
    411 			TXN_CHECKPOINT( mdb->bi_dbenv,
    412 				mdb->bi_txn_cp_kbyte, mdb->bi_txn_cp_min, 0 );
    413 		}
    414 #endif
    415 	}
    416 
    417 	slap_graduate_commit_csn( op );
    418 
    419 	if( postread_ctrl != NULL && (*postread_ctrl) != NULL ) {
    420 		slap_sl_free( (*postread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
    421 		slap_sl_free( *postread_ctrl, op->o_tmpmemctx );
    422 	}
    423 	return rs->sr_err;
    424 }
    425