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