1 1.1 christos /* $NetBSD: add.c,v 1.3 2025/09/05 21:16:31 christos Exp $ */ 2 1.1 christos 3 1.1 christos /* OpenLDAP WiredTiger backend */ 4 1.1 christos /* $OpenLDAP$ */ 5 1.1 christos /* This work is part of OpenLDAP Software <http://www.openldap.org/>. 6 1.1 christos * 7 1.3 christos * Copyright 2002-2024 The OpenLDAP Foundation. 8 1.1 christos * All rights reserved. 9 1.1 christos * 10 1.1 christos * Redistribution and use in source and binary forms, with or without 11 1.1 christos * modification, are permitted only as authorized by the OpenLDAP 12 1.1 christos * Public License. 13 1.1 christos * 14 1.1 christos * A copy of this license is available in the file LICENSE in the 15 1.1 christos * top-level directory of the distribution or, alternatively, at 16 1.1 christos * <http://www.OpenLDAP.org/license.html>. 17 1.1 christos */ 18 1.1 christos /* ACKNOWLEDGEMENTS: 19 1.1 christos * This work was developed by HAMANO Tsukasa <hamano (at) osstech.co.jp> 20 1.1 christos * based on back-bdb for inclusion in OpenLDAP Software. 21 1.1 christos * WiredTiger is a product of MongoDB Inc. 22 1.1 christos */ 23 1.1 christos 24 1.1 christos #include <sys/cdefs.h> 25 1.1 christos __RCSID("$NetBSD: add.c,v 1.3 2025/09/05 21:16:31 christos Exp $"); 26 1.1 christos 27 1.1 christos #include "portable.h" 28 1.1 christos 29 1.1 christos #include <stdio.h> 30 1.1 christos #include "back-wt.h" 31 1.1 christos #include "slap-config.h" 32 1.1 christos 33 1.1 christos int 34 1.1 christos wt_add( Operation *op, SlapReply *rs ) 35 1.1 christos { 36 1.1 christos struct wt_info *wi = (struct wt_info *) op->o_bd->be_private; 37 1.1 christos struct berval pdn; 38 1.1 christos char textbuf[SLAP_TEXT_BUFLEN]; 39 1.1 christos size_t textlen = sizeof textbuf; 40 1.1 christos AttributeDescription *children = slap_schema.si_ad_children; 41 1.1 christos AttributeDescription *entry = slap_schema.si_ad_entry; 42 1.3 christos ID eid = NOID; 43 1.1 christos LDAPControl **postread_ctrl = NULL; 44 1.1 christos LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS]; 45 1.1 christos int num_ctrls = 0; 46 1.1 christos wt_ctx *wc; 47 1.1 christos Entry *e = NULL; 48 1.1 christos Entry *p = NULL; 49 1.3 christos ID pid = NOID; 50 1.1 christos int rc; 51 1.1 christos 52 1.3 christos Debug( LDAP_DEBUG_ARGS, "==> wt_add: %s\n", op->ora_e->e_name.bv_val ); 53 1.1 christos 54 1.1 christos ctrls[num_ctrls] = 0; 55 1.1 christos 56 1.1 christos /* check entry's schema */ 57 1.1 christos rs->sr_err = entry_schema_check( 58 1.1 christos op, op->ora_e, NULL, 59 1.1 christos get_relax(op), 1, NULL, &rs->sr_text, textbuf, textlen ); 60 1.1 christos if ( rs->sr_err != LDAP_SUCCESS ) { 61 1.1 christos Debug( LDAP_DEBUG_TRACE, 62 1.3 christos "wt_add: entry failed schema check: %s (%d)\n", 63 1.1 christos rs->sr_text, rs->sr_err ); 64 1.1 christos goto return_results; 65 1.1 christos } 66 1.1 christos 67 1.1 christos /* add opattrs to shadow as well, only missing attrs will actually 68 1.1 christos * be added; helps compatibility with older OL versions */ 69 1.1 christos rs->sr_err = slap_add_opattrs( op, &rs->sr_text, textbuf, textlen, 1 ); 70 1.1 christos if ( rs->sr_err != LDAP_SUCCESS ) { 71 1.1 christos Debug( LDAP_DEBUG_TRACE, 72 1.3 christos "wt_add: entry failed op attrs add: %s (%d)\n", 73 1.1 christos rs->sr_text, rs->sr_err ); 74 1.1 christos goto return_results; 75 1.1 christos } 76 1.1 christos 77 1.1 christos if ( get_assert( op ) && 78 1.1 christos ( test_filter( op, op->ora_e, get_assertion( op )) 79 1.1 christos != LDAP_COMPARE_TRUE )) 80 1.1 christos { 81 1.1 christos rs->sr_err = LDAP_ASSERTION_FAILED; 82 1.1 christos goto return_results; 83 1.1 christos } 84 1.1 christos 85 1.1 christos /* Not used 86 1.1 christos * subentry = is_entry_subentry( op->ora_e ); 87 1.1 christos */ 88 1.1 christos 89 1.1 christos /* 90 1.1 christos * Get the parent dn and see if the corresponding entry exists. 91 1.1 christos */ 92 1.1 christos if ( be_issuffix( op->o_bd, &op->ora_e->e_nname ) ) { 93 1.1 christos pdn = slap_empty_bv; 94 1.1 christos } else { 95 1.1 christos dnParent( &op->ora_e->e_nname, &pdn ); 96 1.1 christos } 97 1.1 christos 98 1.1 christos wc = wt_ctx_get(op, wi); 99 1.1 christos if( !wc ){ 100 1.3 christos Debug( LDAP_DEBUG_ANY, "wt_add: wt_ctx_get failed\n" ); 101 1.1 christos rs->sr_err = LDAP_OTHER; 102 1.1 christos rs->sr_text = "internal error"; 103 1.1 christos send_ldap_result( op, rs ); 104 1.1 christos return rs->sr_err; 105 1.1 christos } 106 1.1 christos 107 1.1 christos rc = wt_dn2entry(op->o_bd, wc, &op->o_req_ndn, &e); 108 1.1 christos switch( rc ) { 109 1.1 christos case 0: 110 1.1 christos rs->sr_err = LDAP_ALREADY_EXISTS; 111 1.1 christos goto return_results; 112 1.1 christos break; 113 1.1 christos case WT_NOTFOUND: 114 1.1 christos break; 115 1.1 christos default: 116 1.1 christos /* TODO: retry handling */ 117 1.1 christos Debug( LDAP_DEBUG_ANY, 118 1.3 christos "wt_add: error at wt_dn2entry() rc=%d\n", rc ); 119 1.1 christos rs->sr_err = LDAP_OTHER; 120 1.1 christos rs->sr_text = "internal error"; 121 1.1 christos goto return_results; 122 1.1 christos } 123 1.1 christos 124 1.1 christos /* get parent entry */ 125 1.1 christos rc = wt_dn2pentry(op->o_bd, wc, &op->o_req_ndn, &p); 126 1.1 christos switch( rc ){ 127 1.1 christos case 0: 128 1.1 christos case WT_NOTFOUND: 129 1.1 christos break; 130 1.1 christos default: 131 1.1 christos Debug( LDAP_DEBUG_ANY, 132 1.3 christos "wt_add: error at wt_dn2pentry() rc=%d\n", rc ); 133 1.1 christos rs->sr_err = LDAP_OTHER; 134 1.1 christos rs->sr_text = "internal error"; 135 1.1 christos goto return_results; 136 1.1 christos } 137 1.1 christos 138 1.1 christos if ( !p ) 139 1.1 christos p = (Entry *)&slap_entry_root; 140 1.1 christos 141 1.1 christos if ( !bvmatch( &pdn, &p->e_nname ) ) { 142 1.1 christos rs->sr_matched = ber_strdup_x( p->e_name.bv_val, 143 1.1 christos op->o_tmpmemctx ); 144 1.1 christos if ( p != (Entry *)&slap_entry_root ) { 145 1.1 christos rs->sr_ref = is_entry_referral( p ) 146 1.1 christos ? get_entry_referrals( op, p ) 147 1.1 christos : NULL; 148 1.1 christos wt_entry_return( p ); 149 1.1 christos } else { 150 1.1 christos rs->sr_ref = NULL; 151 1.1 christos } 152 1.1 christos p = NULL; 153 1.3 christos Debug( LDAP_DEBUG_TRACE, "wt_add: parent does not exist\n" ); 154 1.1 christos rs->sr_err = LDAP_REFERRAL; 155 1.1 christos rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED; 156 1.1 christos goto return_results; 157 1.1 christos } 158 1.1 christos 159 1.1 christos rs->sr_err = access_allowed( op, p, 160 1.1 christos children, NULL, ACL_WADD, NULL ); 161 1.1 christos if ( ! rs->sr_err ) { 162 1.1 christos /* 163 1.1 christos if ( p != (Entry *)&slap_entry_root ) 164 1.1 christos wt_entry_return( op, p ); 165 1.1 christos */ 166 1.1 christos p = NULL; 167 1.1 christos 168 1.3 christos Debug( LDAP_DEBUG_TRACE, "wt_add: no write access to parent\n" ); 169 1.1 christos rs->sr_err = LDAP_INSUFFICIENT_ACCESS; 170 1.1 christos rs->sr_text = "no write access to parent"; 171 1.1 christos goto return_results;; 172 1.1 christos } 173 1.1 christos 174 1.1 christos if ( p != (Entry *)&slap_entry_root ) { 175 1.1 christos if ( is_entry_subentry( p ) ) { 176 1.1 christos wt_entry_return( p ); 177 1.1 christos p = NULL; 178 1.1 christos /* parent is a subentry, don't allow add */ 179 1.3 christos Debug( LDAP_DEBUG_TRACE, "wt_add: parent is subentry\n" ); 180 1.1 christos rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION; 181 1.1 christos rs->sr_text = "parent is a subentry"; 182 1.1 christos goto return_results;; 183 1.1 christos } 184 1.1 christos 185 1.1 christos if ( is_entry_alias( p ) ) { 186 1.1 christos wt_entry_return( p ); 187 1.1 christos p = NULL; 188 1.1 christos /* parent is an alias, don't allow add */ 189 1.3 christos Debug( LDAP_DEBUG_TRACE, "wt_add: parent is alias\n" ); 190 1.1 christos rs->sr_err = LDAP_ALIAS_PROBLEM; 191 1.1 christos rs->sr_text = "parent is an alias"; 192 1.1 christos goto return_results;; 193 1.1 christos } 194 1.1 christos 195 1.1 christos if ( is_entry_referral( p ) ) { 196 1.1 christos BerVarray ref = get_entry_referrals( op, p ); 197 1.1 christos /* parent is a referral, don't allow add */ 198 1.1 christos rs->sr_matched = ber_strdup_x( p->e_name.bv_val, 199 1.1 christos op->o_tmpmemctx ); 200 1.1 christos rs->sr_ref = referral_rewrite( ref, &p->e_name, 201 1.1 christos &op->o_req_dn, LDAP_SCOPE_DEFAULT ); 202 1.1 christos ber_bvarray_free( ref ); 203 1.1 christos wt_entry_return( p ); 204 1.1 christos p = NULL; 205 1.3 christos Debug( LDAP_DEBUG_TRACE, "wt_add: parent is referral\n" ); 206 1.1 christos 207 1.1 christos rs->sr_err = LDAP_REFERRAL; 208 1.1 christos rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED; 209 1.1 christos goto return_results; 210 1.1 christos } 211 1.1 christos } 212 1.1 christos 213 1.1 christos #if 0 214 1.1 christos if ( subentry ) { 215 1.1 christos /* FIXME: */ 216 1.1 christos /* parent must be an administrative point of the required kind */ 217 1.1 christos } 218 1.1 christos #endif 219 1.1 christos 220 1.1 christos /* free parent */ 221 1.1 christos if ( p != (Entry *)&slap_entry_root ) { 222 1.1 christos pid = p->e_id; 223 1.1 christos if ( p->e_nname.bv_len ) { 224 1.1 christos struct berval ppdn; 225 1.1 christos 226 1.1 christos /* ITS#5326: use parent's DN if differs from provided one */ 227 1.1 christos dnParent( &op->ora_e->e_name, &ppdn ); 228 1.1 christos if ( !dn_match( &p->e_name, &ppdn ) ) { 229 1.1 christos struct berval rdn; 230 1.1 christos struct berval newdn; 231 1.1 christos 232 1.1 christos dnRdn( &op->ora_e->e_name, &rdn ); 233 1.1 christos 234 1.1 christos build_new_dn( &newdn, &p->e_name, &rdn, NULL ); 235 1.1 christos if ( op->ora_e->e_name.bv_val != op->o_req_dn.bv_val ) 236 1.1 christos ber_memfree( op->ora_e->e_name.bv_val ); 237 1.1 christos op->ora_e->e_name = newdn; 238 1.1 christos 239 1.1 christos /* FIXME: should check whether 240 1.1 christos * dnNormalize(newdn) == e->e_nname ... */ 241 1.1 christos } 242 1.1 christos } 243 1.1 christos 244 1.1 christos wt_entry_return( p ); 245 1.1 christos } 246 1.1 christos p = NULL; 247 1.1 christos 248 1.1 christos rs->sr_err = access_allowed( op, op->ora_e, 249 1.1 christos entry, NULL, ACL_WADD, NULL ); 250 1.1 christos 251 1.1 christos if ( ! rs->sr_err ) { 252 1.3 christos Debug( LDAP_DEBUG_TRACE, "wt_add: no write access to entry\n" ); 253 1.1 christos rs->sr_err = LDAP_INSUFFICIENT_ACCESS; 254 1.1 christos rs->sr_text = "no write access to entry"; 255 1.1 christos goto return_results; 256 1.1 christos } 257 1.1 christos 258 1.1 christos /* 259 1.1 christos * Check ACL for attribute write access 260 1.1 christos */ 261 1.1 christos if (!acl_check_modlist(op, op->ora_e, op->ora_modlist)) { 262 1.3 christos Debug( LDAP_DEBUG_TRACE, "wt_add: no write access to attribute\n" ); 263 1.1 christos rs->sr_err = LDAP_INSUFFICIENT_ACCESS; 264 1.1 christos rs->sr_text = "no write access to attribute"; 265 1.1 christos goto return_results; 266 1.1 christos } 267 1.1 christos 268 1.3 christos rc = wc->session->begin_transaction(wc->session, "isolation=read-uncommitted"); 269 1.1 christos if( rc ) { 270 1.3 christos Debug( LDAP_DEBUG_TRACE, "wt_add: begin_transaction failed: %s (%d)\n", 271 1.1 christos wiredtiger_strerror(rc), rc ); 272 1.1 christos rs->sr_err = LDAP_OTHER; 273 1.1 christos rs->sr_text = "begin_transaction failed"; 274 1.1 christos goto return_results; 275 1.1 christos } 276 1.3 christos Debug( LDAP_DEBUG_TRACE, "wt_add: session id: %p\n", wc->session ); 277 1.1 christos 278 1.1 christos wt_next_id( op->o_bd, &eid ); 279 1.1 christos op->ora_e->e_id = eid; 280 1.1 christos 281 1.3 christos rc = wt_dn2id_add( op, wc, pid, op->ora_e ); 282 1.1 christos if( rc ){ 283 1.1 christos Debug( LDAP_DEBUG_TRACE, 284 1.3 christos "wt_add: dn2id_add failed: %s (%d)\n", 285 1.1 christos wiredtiger_strerror(rc), rc ); 286 1.1 christos switch( rc ) { 287 1.1 christos case WT_DUPLICATE_KEY: 288 1.1 christos rs->sr_err = LDAP_ALREADY_EXISTS; 289 1.1 christos break; 290 1.1 christos default: 291 1.1 christos rs->sr_err = LDAP_OTHER; 292 1.1 christos } 293 1.1 christos wc->session->rollback_transaction(wc->session, NULL); 294 1.1 christos goto return_results; 295 1.1 christos } 296 1.1 christos 297 1.3 christos rc = wt_id2entry_add( op, wc, op->ora_e ); 298 1.1 christos if ( rc ) { 299 1.1 christos Debug( LDAP_DEBUG_TRACE, 300 1.3 christos "wt_add: id2entry_add failed: %s (%d)\n", 301 1.1 christos wiredtiger_strerror(rc), rc ); 302 1.1 christos if ( rc == LDAP_ADMINLIMIT_EXCEEDED ) { 303 1.1 christos rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED; 304 1.1 christos rs->sr_text = "entry is too big"; 305 1.1 christos } else { 306 1.1 christos rs->sr_err = LDAP_OTHER; 307 1.1 christos rs->sr_text = "entry store failed"; 308 1.1 christos } 309 1.1 christos wc->session->rollback_transaction(wc->session, NULL); 310 1.1 christos goto return_results; 311 1.1 christos } 312 1.1 christos 313 1.1 christos /* add indices */ 314 1.1 christos rc = wt_index_entry_add( op, wc, op->ora_e ); 315 1.1 christos if ( rc ) { 316 1.1 christos Debug(LDAP_DEBUG_TRACE, 317 1.3 christos "<== wt_add: index add failed: %s (%d)\n", 318 1.1 christos wiredtiger_strerror(rc), rc ); 319 1.1 christos rs->sr_err = LDAP_OTHER; 320 1.1 christos rs->sr_text = "index add failed"; 321 1.1 christos wc->session->rollback_transaction(wc->session, NULL); 322 1.1 christos goto return_results; 323 1.1 christos } 324 1.1 christos 325 1.1 christos rc = wc->session->commit_transaction(wc->session, NULL); 326 1.1 christos if( rc ) { 327 1.1 christos Debug( LDAP_DEBUG_TRACE, 328 1.3 christos "<== wt_add: commit_transaction failed: %s (%d)\n", 329 1.1 christos wiredtiger_strerror(rc), rc ); 330 1.1 christos rs->sr_err = LDAP_OTHER; 331 1.1 christos rs->sr_text = "commit_transaction failed"; 332 1.1 christos goto return_results; 333 1.1 christos } 334 1.1 christos 335 1.1 christos rs->sr_err = LDAP_SUCCESS; 336 1.1 christos 337 1.1 christos /* post-read */ 338 1.1 christos if( op->o_postread ) { 339 1.1 christos if( postread_ctrl == NULL ) { 340 1.1 christos postread_ctrl = &ctrls[num_ctrls++]; 341 1.1 christos ctrls[num_ctrls] = NULL; 342 1.1 christos } 343 1.1 christos if ( slap_read_controls( op, rs, op->ora_e, 344 1.1 christos &slap_post_read_bv, postread_ctrl ) ) 345 1.1 christos { 346 1.3 christos Debug( LDAP_DEBUG_TRACE, "<=- wt_add: post-read failed!\n" ); 347 1.1 christos if ( op->o_postread & SLAP_CONTROL_CRITICAL ) { 348 1.1 christos /* FIXME: is it correct to abort 349 1.1 christos * operation if control fails? */ 350 1.1 christos goto return_results; 351 1.1 christos } 352 1.1 christos } 353 1.1 christos } 354 1.1 christos 355 1.1 christos Debug(LDAP_DEBUG_TRACE, 356 1.3 christos "wt_add: added%s id=%08lx dn=\"%s\"\n", 357 1.1 christos op->o_noop ? " (no-op)" : "", 358 1.1 christos op->ora_e->e_id, op->ora_e->e_dn ); 359 1.1 christos 360 1.1 christos return_results: 361 1.1 christos send_ldap_result( op, rs ); 362 1.1 christos 363 1.1 christos slap_graduate_commit_csn( op ); 364 1.1 christos 365 1.1 christos if( postread_ctrl != NULL && (*postread_ctrl) != NULL ) { 366 1.1 christos slap_sl_free( (*postread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx ); 367 1.1 christos slap_sl_free( *postread_ctrl, op->o_tmpmemctx ); 368 1.1 christos } 369 1.1 christos return rs->sr_err; 370 1.1 christos } 371 1.1 christos 372 1.1 christos /* 373 1.1 christos * Local variables: 374 1.1 christos * indent-tabs-mode: t 375 1.1 christos * tab-width: 4 376 1.1 christos * c-basic-offset: 4 377 1.1 christos * End: 378 1.1 christos */ 379