xref: /netbsd-src/external/bsd/openldap/dist/servers/slapd/back-ndb/tools.cpp (revision e670fd5c413e99c2f6a37901bb21c537fcd322d2)
1 /* tools.cpp - tools for slap tools */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2008-2021 The OpenLDAP Foundation.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted only as authorized by the OpenLDAP
10  * Public License.
11  *
12  * A copy of this license is available in the file LICENSE in the
13  * top-level directory of the distribution or, alternatively, at
14  * <http://www.OpenLDAP.org/license.html>.
15  */
16 /* ACKNOWLEDGEMENTS:
17  * This work was initially developed by Howard Chu for inclusion
18  * in OpenLDAP Software. This work was sponsored by MySQL.
19  */
20 
21 #include "portable.h"
22 
23 #include <stdio.h>
24 #include <ac/string.h>
25 #include <ac/errno.h>
26 
27 #include "lutil.h"
28 
29 #include "back-ndb.h"
30 
31 typedef struct dn_id {
32 	ID id;
33 	struct berval dn;
34 } dn_id;
35 
36 #define	HOLE_SIZE	4096
37 static dn_id hbuf[HOLE_SIZE], *holes = hbuf;
38 static unsigned nhmax = HOLE_SIZE;
39 static unsigned nholes;
40 static Avlnode *myParents;
41 
42 static Ndb *myNdb;
43 static NdbTransaction *myScanTxn;
44 static NdbIndexScanOperation *myScanOp;
45 
46 static NdbRecAttr *myScanID, *myScanOC;
47 static NdbRecAttr *myScanDN[NDB_MAX_RDNS];
48 static char myDNbuf[2048];
49 static char myIdbuf[2*sizeof(ID)];
50 static char myOcbuf[NDB_OC_BUFLEN];
51 static NdbRdns myRdns;
52 
53 static NdbTransaction *myPutTxn;
54 static int myPutCnt;
55 
56 static struct berval *myOcList;
57 static struct berval myDn;
58 
59 extern "C"
ndb_tool_entry_open(BackendDB * be,int mode)60 int ndb_tool_entry_open(
61 	BackendDB *be, int mode )
62 {
63 	struct ndb_info *ni = (struct ndb_info *) be->be_private;
64 
65 	myNdb = new Ndb( ni->ni_cluster[0], ni->ni_dbname );
66 	return myNdb->init(1024);
67 }
68 
69 extern "C"
ndb_tool_entry_close(BackendDB * be)70 int ndb_tool_entry_close(
71 	BackendDB *be )
72 {
73 	if ( myPutTxn ) {
74 		int rc = myPutTxn->execute(NdbTransaction::Commit);
75 		if( rc != 0 ) {
76 			char text[1024];
77 			snprintf( text, sizeof(text),
78 					"txn_commit failed: %s (%d)",
79 					myPutTxn->getNdbError().message, myPutTxn->getNdbError().code );
80 			Debug( LDAP_DEBUG_ANY,
81 				"=> " LDAP_XSTRING(ndb_tool_entry_put) ": %s\n",
82 				text, 0, 0 );
83 		}
84 		myPutTxn->close();
85 		myPutTxn = NULL;
86 	}
87 	myPutCnt = 0;
88 
89 	if( nholes ) {
90 		unsigned i;
91 		fprintf( stderr, "Error, entries missing!\n");
92 		for (i=0; i<nholes; i++) {
93 			fprintf(stderr, "  entry %ld: %s\n",
94 				holes[i].id, holes[i].dn.bv_val);
95 		}
96 		return -1;
97 	}
98 
99 	return 0;
100 }
101 
102 extern "C"
ndb_tool_entry_next(BackendDB * be)103 ID ndb_tool_entry_next(
104 	BackendDB *be )
105 {
106 	struct ndb_info *ni = (struct ndb_info *) be->be_private;
107 	char *ptr;
108 	ID id;
109 	int i;
110 
111 	assert( be != NULL );
112 	assert( slapMode & SLAP_TOOL_MODE );
113 
114 	if ( myScanOp->nextResult() ) {
115 		myScanOp->close();
116 		myScanOp = NULL;
117 		myScanTxn->close();
118 		myScanTxn = NULL;
119 		return NOID;
120 	}
121 	id = myScanID->u_64_value();
122 
123 	if ( myOcList ) {
124 		ber_bvarray_free( myOcList );
125 	}
126 	myOcList = ndb_ref2oclist( myOcbuf, NULL );
127 	for ( i=0; i<NDB_MAX_RDNS; i++ ) {
128 		if ( myScanDN[i]->isNULL() || !myRdns.nr_buf[i][0] )
129 			break;
130 	}
131 	myRdns.nr_num = i;
132 	ptr = myDNbuf;
133 	for ( --i; i>=0; i-- ) {
134 		char *buf;
135 		int len;
136 		buf = myRdns.nr_buf[i];
137 		len = *buf++;
138 		ptr = lutil_strncopy( ptr, buf, len );
139 		if ( i )
140 			*ptr++ = ',';
141 	}
142 	*ptr = '\0';
143 	myDn.bv_val = myDNbuf;
144 	myDn.bv_len = ptr - myDNbuf;
145 
146 	return id;
147 }
148 
149 extern "C"
ndb_tool_entry_first(BackendDB * be)150 ID ndb_tool_entry_first(
151 	BackendDB *be )
152 {
153 	struct ndb_info *ni = (struct ndb_info *) be->be_private;
154 	int i;
155 
156 	myScanTxn = myNdb->startTransaction();
157 	if ( !myScanTxn )
158 		return NOID;
159 
160 	myScanOp = myScanTxn->getNdbIndexScanOperation( "PRIMARY", DN2ID_TABLE );
161 	if ( !myScanOp )
162 		return NOID;
163 
164 	if ( myScanOp->readTuples( NdbOperation::LM_CommittedRead, NdbScanOperation::SF_KeyInfo ))
165 		return NOID;
166 
167 	myScanID = myScanOp->getValue( EID_COLUMN, myIdbuf );
168 	myScanOC = myScanOp->getValue( OCS_COLUMN, myOcbuf );
169 	for ( i=0; i<NDB_MAX_RDNS; i++ ) {
170 		myScanDN[i] = myScanOp->getValue( i+RDN_COLUMN, myRdns.nr_buf[i] );
171 	}
172 	if ( myScanTxn->execute( NdbTransaction::NoCommit, NdbOperation::AbortOnError, 1 ))
173 		return NOID;
174 
175 	return ndb_tool_entry_next( be );
176 }
177 
178 extern "C"
ndb_tool_dn2id_get(Backend * be,struct berval * dn)179 ID ndb_tool_dn2id_get(
180 	Backend *be,
181 	struct berval *dn
182 )
183 {
184 	struct ndb_info *ni = (struct ndb_info *) be->be_private;
185 	NdbArgs NA;
186 	NdbRdns rdns;
187 	Entry e;
188 	char text[1024];
189 	Operation op = {0};
190 	Opheader ohdr = {0};
191 	int rc;
192 
193 	if ( BER_BVISEMPTY(dn) )
194 		return 0;
195 
196 	NA.ndb = myNdb;
197 	NA.txn = myNdb->startTransaction();
198 	if ( !NA.txn ) {
199 		snprintf( text, sizeof(text),
200 			"startTransaction failed: %s (%d)",
201 			myNdb->getNdbError().message, myNdb->getNdbError().code );
202 		Debug( LDAP_DEBUG_ANY,
203 			"=> " LDAP_XSTRING(ndb_tool_dn2id_get) ": %s\n",
204 			 text, 0, 0 );
205 		return NOID;
206 	}
207 	if ( myOcList ) {
208 		ber_bvarray_free( myOcList );
209 		myOcList = NULL;
210 	}
211 	op.o_hdr = &ohdr;
212 	op.o_bd = be;
213 	op.o_tmpmemctx = NULL;
214 	op.o_tmpmfuncs = &ch_mfuncs;
215 
216 	NA.e = &e;
217 	e.e_name = *dn;
218 	NA.rdns = &rdns;
219 	NA.ocs = NULL;
220 	rc = ndb_entry_get_info( &op, &NA, 0, NULL );
221 	myOcList = NA.ocs;
222 	NA.txn->close();
223 	if ( rc )
224 		return NOID;
225 
226 	myDn = *dn;
227 
228 	return e.e_id;
229 }
230 
231 extern "C"
ndb_tool_entry_get(BackendDB * be,ID id)232 Entry* ndb_tool_entry_get( BackendDB *be, ID id )
233 {
234 	NdbArgs NA;
235 	int rc;
236 	char text[1024];
237 	Operation op = {0};
238 	Opheader ohdr = {0};
239 
240 	assert( be != NULL );
241 	assert( slapMode & SLAP_TOOL_MODE );
242 
243 	NA.txn = myNdb->startTransaction();
244 	if ( !NA.txn ) {
245 		snprintf( text, sizeof(text),
246 			"start_transaction failed: %s (%d)",
247 			myNdb->getNdbError().message, myNdb->getNdbError().code );
248 		Debug( LDAP_DEBUG_ANY,
249 			"=> " LDAP_XSTRING(ndb_tool_entry_get) ": %s\n",
250 			 text, 0, 0 );
251 		return NULL;
252 	}
253 
254 	NA.e = entry_alloc();
255 	NA.e->e_id = id;
256 	ber_dupbv( &NA.e->e_name, &myDn );
257 	dnNormalize( 0, NULL, NULL, &NA.e->e_name, &NA.e->e_nname, NULL );
258 
259 	op.o_hdr = &ohdr;
260 	op.o_bd = be;
261 	op.o_tmpmemctx = NULL;
262 	op.o_tmpmfuncs = &ch_mfuncs;
263 
264 	NA.ndb = myNdb;
265 	NA.ocs = myOcList;
266 	rc = ndb_entry_get_data( &op, &NA, 0 );
267 
268 	if ( rc ) {
269 		entry_free( NA.e );
270 		NA.e = NULL;
271 	}
272 	NA.txn->close();
273 
274 	return NA.e;
275 }
276 
277 static struct berval glueval[] = {
278 	BER_BVC("glue"),
279 	BER_BVNULL
280 };
281 
ndb_dnid_cmp(const void * v1,const void * v2)282 static int ndb_dnid_cmp( const void *v1, const void *v2 )
283 {
284 	struct dn_id *dn1 = (struct dn_id *)v1,
285 		*dn2 = (struct dn_id *)v2;
286 	return ber_bvcmp( &dn1->dn, &dn2->dn );
287 }
288 
ndb_tool_next_id(Operation * op,NdbArgs * NA,struct berval * text,int hole)289 static int ndb_tool_next_id(
290 	Operation *op,
291 	NdbArgs *NA,
292 	struct berval *text,
293 	int hole )
294 {
295 	struct berval ndn = NA->e->e_nname;
296 	int rc;
297 
298 	if (ndn.bv_len == 0) {
299 		NA->e->e_id = 0;
300 		return 0;
301 	}
302 
303 	rc = ndb_entry_get_info( op, NA, 0, NULL );
304 	if ( rc ) {
305 		Attribute *a, tmp = {0};
306 		if ( !be_issuffix( op->o_bd, &ndn ) ) {
307 			struct dn_id *dptr;
308 			struct berval npdn;
309 			dnParent( &ndn, &npdn );
310 			NA->e->e_nname = npdn;
311 			NA->rdns->nr_num--;
312 			rc = ndb_tool_next_id( op, NA, text, 1 );
313 			NA->e->e_nname = ndn;
314 			NA->rdns->nr_num++;
315 			if ( rc ) {
316 				return rc;
317 			}
318 			/* If parent didn't exist, it was created just now
319 			 * and its ID is now in e->e_id.
320 			 */
321 			dptr = (struct dn_id *)ch_malloc( sizeof( struct dn_id ) + npdn.bv_len + 1);
322 			dptr->id = NA->e->e_id;
323 			dptr->dn.bv_val = (char *)(dptr+1);
324 			strcpy(dptr->dn.bv_val, npdn.bv_val );
325 			dptr->dn.bv_len = npdn.bv_len;
326 			if ( avl_insert( &myParents, dptr, ndb_dnid_cmp, avl_dup_error )) {
327 				ch_free( dptr );
328 			}
329 		}
330 		rc = ndb_next_id( op->o_bd, myNdb, &NA->e->e_id );
331 		if ( rc ) {
332 			snprintf( text->bv_val, text->bv_len,
333 				"next_id failed: %s (%d)",
334 				myNdb->getNdbError().message, myNdb->getNdbError().code );
335 			Debug( LDAP_DEBUG_ANY,
336 				"=> ndb_tool_next_id: %s\n", text->bv_val, 0, 0 );
337 			return rc;
338 		}
339 		if ( hole ) {
340 			a = NA->e->e_attrs;
341 			NA->e->e_attrs = &tmp;
342 			tmp.a_desc = slap_schema.si_ad_objectClass;
343 			tmp.a_vals = glueval;
344 			tmp.a_nvals = tmp.a_vals;
345 			tmp.a_numvals = 1;
346 		}
347 		rc = ndb_entry_put_info( op->o_bd, NA, 0 );
348 		if ( hole ) {
349 			NA->e->e_attrs = a;
350 		}
351 		if ( rc ) {
352 			snprintf( text->bv_val, text->bv_len,
353 				"ndb_entry_put_info failed: %s (%d)",
354 				myNdb->getNdbError().message, myNdb->getNdbError().code );
355 		Debug( LDAP_DEBUG_ANY,
356 			"=> ndb_tool_next_id: %s\n", text->bv_val, 0, 0 );
357 		} else if ( hole ) {
358 			if ( nholes == nhmax - 1 ) {
359 				if ( holes == hbuf ) {
360 					holes = (dn_id *)ch_malloc( nhmax * sizeof(dn_id) * 2 );
361 					AC_MEMCPY( holes, hbuf, sizeof(hbuf) );
362 				} else {
363 					holes = (dn_id *)ch_realloc( holes, nhmax * sizeof(dn_id) * 2 );
364 				}
365 				nhmax *= 2;
366 			}
367 			ber_dupbv( &holes[nholes].dn, &ndn );
368 			holes[nholes++].id = NA->e->e_id;
369 		}
370 	} else if ( !hole ) {
371 		unsigned i;
372 
373 		for ( i=0; i<nholes; i++) {
374 			if ( holes[i].id == NA->e->e_id ) {
375 				int j;
376 				free(holes[i].dn.bv_val);
377 				for (j=i;j<nholes;j++) holes[j] = holes[j+1];
378 				holes[j].id = 0;
379 				nholes--;
380 				rc = ndb_entry_put_info( op->o_bd, NA, 1 );
381 				break;
382 			} else if ( holes[i].id > NA->e->e_id ) {
383 				break;
384 			}
385 		}
386 	}
387 	return rc;
388 }
389 
390 extern "C"
ndb_tool_entry_put(BackendDB * be,Entry * e,struct berval * text)391 ID ndb_tool_entry_put(
392 	BackendDB *be,
393 	Entry *e,
394 	struct berval *text )
395 {
396 	struct ndb_info *ni = (struct ndb_info *) be->be_private;
397 	struct dn_id dtmp, *dptr;
398 	NdbArgs NA;
399 	NdbRdns rdns;
400 	int rc, slow = 0;
401 	Operation op = {0};
402 	Opheader ohdr = {0};
403 
404 	assert( be != NULL );
405 	assert( slapMode & SLAP_TOOL_MODE );
406 
407 	assert( text != NULL );
408 	assert( text->bv_val != NULL );
409 	assert( text->bv_val[0] == '\0' );	/* overconservative? */
410 
411 	Debug( LDAP_DEBUG_TRACE, "=> " LDAP_XSTRING(ndb_tool_entry_put)
412 		"( %ld, \"%s\" )\n", (long) e->e_id, e->e_dn, 0 );
413 
414 	if ( !be_issuffix( be, &e->e_nname )) {
415 		dnParent( &e->e_nname, &dtmp.dn );
416 		dptr = (struct dn_id *)avl_find( myParents, &dtmp, ndb_dnid_cmp );
417 		if ( !dptr )
418 			slow = 1;
419 	}
420 
421 	rdns.nr_num = 0;
422 
423 	op.o_hdr = &ohdr;
424 	op.o_bd = be;
425 	op.o_tmpmemctx = NULL;
426 	op.o_tmpmfuncs = &ch_mfuncs;
427 
428 	if ( !slow ) {
429 		rc = ndb_next_id( be, myNdb, &e->e_id );
430 		if ( rc ) {
431 			snprintf( text->bv_val, text->bv_len,
432 				"next_id failed: %s (%d)",
433 				myNdb->getNdbError().message, myNdb->getNdbError().code );
434 			Debug( LDAP_DEBUG_ANY,
435 				"=> ndb_tool_next_id: %s\n", text->bv_val, 0, 0 );
436 			return rc;
437 		}
438 	}
439 
440 	if ( !myPutTxn )
441 		myPutTxn = myNdb->startTransaction();
442 	if ( !myPutTxn ) {
443 		snprintf( text->bv_val, text->bv_len,
444 			"start_transaction failed: %s (%d)",
445 			myNdb->getNdbError().message, myNdb->getNdbError().code );
446 		Debug( LDAP_DEBUG_ANY,
447 			"=> " LDAP_XSTRING(ndb_tool_entry_put) ": %s\n",
448 			 text->bv_val, 0, 0 );
449 		return NOID;
450 	}
451 
452 	/* add dn2id indices */
453 	ndb_dn2rdns( &e->e_name, &rdns );
454 	NA.rdns = &rdns;
455 	NA.e = e;
456 	NA.ndb = myNdb;
457 	NA.txn = myPutTxn;
458 	if ( slow ) {
459 		rc = ndb_tool_next_id( &op, &NA, text, 0 );
460 		if( rc != 0 ) {
461 			goto done;
462 		}
463 	} else {
464 		rc = ndb_entry_put_info( be, &NA, 0 );
465 		if ( rc != 0 ) {
466 			goto done;
467 		}
468 	}
469 
470 	/* id2entry index */
471 	rc = ndb_entry_put_data( be, &NA );
472 	if( rc != 0 ) {
473 		snprintf( text->bv_val, text->bv_len,
474 				"ndb_entry_put_data failed: %s (%d)",
475 				myNdb->getNdbError().message, myNdb->getNdbError().code );
476 		Debug( LDAP_DEBUG_ANY,
477 			"=> " LDAP_XSTRING(ndb_tool_entry_put) ": %s\n",
478 			text->bv_val, 0, 0 );
479 		goto done;
480 	}
481 
482 done:
483 	if( rc == 0 ) {
484 		myPutCnt++;
485 		if ( !( myPutCnt & 0x0f )) {
486 			rc = myPutTxn->execute(NdbTransaction::Commit);
487 			if( rc != 0 ) {
488 				snprintf( text->bv_val, text->bv_len,
489 					"txn_commit failed: %s (%d)",
490 					myPutTxn->getNdbError().message, myPutTxn->getNdbError().code );
491 				Debug( LDAP_DEBUG_ANY,
492 					"=> " LDAP_XSTRING(ndb_tool_entry_put) ": %s\n",
493 					text->bv_val, 0, 0 );
494 				e->e_id = NOID;
495 			}
496 			myPutTxn->close();
497 			myPutTxn = NULL;
498 		}
499 	} else {
500 		snprintf( text->bv_val, text->bv_len,
501 			"txn_aborted! %s (%d)",
502 			myPutTxn->getNdbError().message, myPutTxn->getNdbError().code );
503 		Debug( LDAP_DEBUG_ANY,
504 			"=> " LDAP_XSTRING(ndb_tool_entry_put) ": %s\n",
505 			text->bv_val, 0, 0 );
506 		e->e_id = NOID;
507 		myPutTxn->close();
508 	}
509 
510 	return e->e_id;
511 }
512 
513 extern "C"
ndb_tool_entry_reindex(BackendDB * be,ID id,AttributeDescription ** adv)514 int ndb_tool_entry_reindex(
515 	BackendDB *be,
516 	ID id,
517 	AttributeDescription **adv )
518 {
519 	struct ndb_info *ni = (struct ndb_info *) be->be_private;
520 
521 	Debug( LDAP_DEBUG_ARGS,
522 		"=> " LDAP_XSTRING(ndb_tool_entry_reindex) "( %ld )\n",
523 		(long) id, 0, 0 );
524 
525 	return 0;
526 }
527 
528 extern "C"
ndb_tool_entry_modify(BackendDB * be,Entry * e,struct berval * text)529 ID ndb_tool_entry_modify(
530 	BackendDB *be,
531 	Entry *e,
532 	struct berval *text )
533 {
534 	struct ndb_info *ni = (struct ndb_info *) be->be_private;
535 	int rc;
536 
537 	Debug( LDAP_DEBUG_TRACE,
538 		"=> " LDAP_XSTRING(ndb_tool_entry_modify) "( %ld, \"%s\" )\n",
539 		(long) e->e_id, e->e_dn, 0 );
540 
541 done:
542 	return e->e_id;
543 }
544 
545