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
31typedef struct dn_id {
32	ID id;
33	struct berval dn;
34} dn_id;
35
36#define	HOLE_SIZE	4096
37static dn_id hbuf[HOLE_SIZE], *holes = hbuf;
38static unsigned nhmax = HOLE_SIZE;
39static unsigned nholes;
40static Avlnode *myParents;
41
42static Ndb *myNdb;
43static NdbTransaction *myScanTxn;
44static NdbIndexScanOperation *myScanOp;
45
46static NdbRecAttr *myScanID, *myScanOC;
47static NdbRecAttr *myScanDN[NDB_MAX_RDNS];
48static char myDNbuf[2048];
49static char myIdbuf[2*sizeof(ID)];
50static char myOcbuf[NDB_OC_BUFLEN];
51static NdbRdns myRdns;
52
53static NdbTransaction *myPutTxn;
54static int myPutCnt;
55
56static struct berval *myOcList;
57static struct berval myDn;
58
59extern "C"
60int 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
69extern "C"
70int 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
102extern "C"
103ID 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
149extern "C"
150ID 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
178extern "C"
179ID 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
231extern "C"
232Entry* 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
277static struct berval glueval[] = {
278	BER_BVC("glue"),
279	BER_BVNULL
280};
281
282static 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
289static 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
390extern "C"
391ID 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
482done:
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
513extern "C"
514int 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
528extern "C"
529ID 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
541done:
542	return e->e_id;
543}
544
545