1 /* $NetBSD: tools.c,v 1.2 2021/08/14 16:15:02 christos Exp $ */ 2 3 /* OpenLDAP WiredTiger backend */ 4 /* $OpenLDAP$ */ 5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>. 6 * 7 * Copyright 2002-2021 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 /* ACKNOWLEDGEMENTS: 19 * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp> 20 * based on back-bdb for inclusion in OpenLDAP Software. 21 * WiredTiger is a product of MongoDB Inc. 22 */ 23 24 #include <sys/cdefs.h> 25 __RCSID("$NetBSD: tools.c,v 1.2 2021/08/14 16:15:02 christos Exp $"); 26 27 #include "portable.h" 28 29 #include <stdio.h> 30 #include <ac/string.h> 31 #include "back-wt.h" 32 #include "slap-config.h" 33 34 typedef struct dn_id { 35 ID id; 36 struct berval dn; 37 } dn_id; 38 39 #define HOLE_SIZE 4096 40 static dn_id hbuf[HOLE_SIZE], *holes = hbuf; 41 static unsigned nhmax = HOLE_SIZE; 42 static unsigned nholes; 43 44 static int index_nattrs; 45 46 static struct berval *tool_base; 47 static int tool_scope; 48 static Filter *tool_filter; 49 static Entry *tool_next_entry; 50 51 static wt_ctx *wc; 52 static WT_CURSOR *reader; 53 static WT_ITEM item; 54 55 int 56 wt_tool_entry_open( BackendDB *be, int mode ) 57 { 58 struct wt_info *wi = (struct wt_info *) be->be_private; 59 WT_CONNECTION *conn = wi->wi_conn; 60 int rc; 61 62 wc = wt_ctx_init(wi); 63 if( !wc ){ 64 Debug( LDAP_DEBUG_ANY, 65 LDAP_XSTRING(wt_tool_entry_open) 66 ": wt_ctx_get failed\n" ); 67 return -1; 68 } 69 70 rc = wc->session->open_cursor(wc->session, WT_TABLE_ID2ENTRY"(entry)" 71 ,NULL, NULL, &reader); 72 if ( rc ) { 73 Debug( LDAP_DEBUG_ANY, 74 LDAP_XSTRING(wt_tool_entry_open) 75 ": cursor open failed: %s (%d)\n", 76 wiredtiger_strerror(rc), rc ); 77 return -1; 78 } 79 80 return 0; 81 } 82 83 int 84 wt_tool_entry_close( BackendDB *be ) 85 { 86 int rc; 87 88 if( reader ) { 89 reader->close(reader); 90 reader = NULL; 91 } 92 93 wt_ctx_free(NULL, wc); 94 95 if( nholes ) { 96 unsigned i; 97 fprintf( stderr, "Error, entries missing!\n"); 98 for (i=0; i<nholes; i++) { 99 fprintf(stderr, " entry %ld: %s\n", 100 holes[i].id, holes[i].dn.bv_val); 101 } 102 return -1; 103 } 104 105 return 0; 106 } 107 108 ID 109 wt_tool_entry_first_x( BackendDB *be, 110 struct berval *base, 111 int scope, 112 Filter *f ) 113 { 114 tool_base = base; 115 tool_scope = scope; 116 tool_filter = f; 117 118 return wt_tool_entry_next( be ); 119 } 120 121 ID 122 wt_tool_entry_next( BackendDB *be ) 123 { 124 int rc; 125 ID id; 126 127 rc = reader->next(reader); 128 switch( rc ){ 129 case 0: 130 break; 131 case WT_NOTFOUND: 132 return NOID; 133 default: 134 Debug( LDAP_DEBUG_ANY, 135 LDAP_XSTRING(wt_tool_entry_next) 136 ": next failed: %s (%d)\n", 137 wiredtiger_strerror(rc), rc ); 138 return NOID; 139 } 140 141 rc = reader->get_key(reader, &id); 142 if( rc ){ 143 Debug( LDAP_DEBUG_ANY, 144 LDAP_XSTRING(wt_tool_entry_next) 145 ": get_key failed: %s (%d)\n", 146 wiredtiger_strerror(rc), rc ); 147 } 148 149 rc = reader->get_value(reader, &item); 150 if( rc ){ 151 Debug( LDAP_DEBUG_ANY, 152 LDAP_XSTRING(wt_tool_entry_next) 153 ": get_value failed: %s (%d)\n", 154 wiredtiger_strerror(rc), rc ); 155 } 156 return id; 157 } 158 159 static ber_len_t 160 entry_getlen(unsigned char **buf) 161 { 162 ber_len_t len; 163 int i; 164 165 len = *(*buf)++; 166 if (len <= 0x7f) 167 return len; 168 i = len & 0x7f; 169 len = 0; 170 for (;i > 0; i--) { 171 len <<= 8; 172 len |= *(*buf)++; 173 } 174 return len; 175 } 176 177 int wt_entry_header(WT_ITEM *item, EntryHeader *eh){ 178 unsigned char *ptr = (unsigned char *)item->data; 179 180 /* Some overlays can create empty entries 181 * so don't check for zeros here. 182 */ 183 eh->nattrs = entry_getlen(&ptr); 184 eh->nvals = entry_getlen(&ptr); 185 eh->data = (char *)ptr; 186 return LDAP_SUCCESS; 187 } 188 189 Entry * 190 wt_tool_entry_get( BackendDB *be, ID id ) 191 { 192 Entry *e = NULL; 193 static EntryHeader eh; 194 int rc, eoff; 195 196 assert( be != NULL ); 197 assert( slapMode & SLAP_TOOL_MODE ); 198 199 rc = wt_entry_header( &item, &eh ); 200 assert( rc == 0 ); 201 eoff = eh.data - (char *)item.data; 202 203 eh.bv.bv_len = eh.nvals * sizeof( struct berval ) + item.size; 204 eh.bv.bv_val = ch_realloc( eh.bv.bv_val, eh.bv.bv_len ); 205 memset(eh.bv.bv_val, 0xff, eh.bv.bv_len); 206 eh.data = eh.bv.bv_val + eh.nvals * sizeof( struct berval ); 207 memcpy(eh.data, item.data, item.size); 208 eh.data += eoff; 209 210 rc = entry_decode( &eh, &e ); 211 assert( rc == 0 ); 212 213 if( rc == LDAP_SUCCESS ) { 214 e->e_id = id; 215 } 216 217 return e; 218 } 219 220 static int wt_tool_next_id( 221 Operation *op, 222 Entry *e, 223 struct berval *text, 224 int hole ) 225 { 226 struct wt_info *wi = (struct wt_info *) op->o_bd->be_private; 227 struct berval dn = e->e_name; 228 struct berval ndn = e->e_nname; 229 struct berval pdn, npdn; 230 int rc; 231 ID id = 0; 232 ID pid = 0; 233 234 if(ndn.bv_len == 0){ 235 e->e_id = 0; 236 return 0; 237 } 238 239 rc = wt_dn2id(op, wc->session, &ndn, &id); 240 if(rc == 0){ 241 e->e_id = id; 242 }else if( rc == WT_NOTFOUND ){ 243 if ( !be_issuffix( op->o_bd, &ndn ) ) { 244 ID eid = e->e_id; 245 dnParent( &dn, &pdn ); 246 dnParent( &ndn, &npdn ); 247 e->e_name = pdn; 248 e->e_nname = npdn; 249 rc = wt_tool_next_id( op, e, text, 1 ); 250 e->e_name = dn; 251 e->e_nname = ndn; 252 if ( rc ) { 253 return rc; 254 } 255 /* If parent didn't exist, it was created just now 256 * and its ID is now in e->e_id. Make sure the current 257 * entry gets added under the new parent ID. 258 */ 259 if ( eid != e->e_id ) { 260 pid = e->e_id; 261 } 262 }else{ 263 pid = id; 264 } 265 wt_next_id( op->o_bd, &e->e_id ); 266 rc = wt_dn2id_add(op, wc->session, pid, e); 267 if( rc ){ 268 snprintf( text->bv_val, text->bv_len, 269 "wt_dn2id_add failed: %s (%d)", 270 wiredtiger_strerror(rc), rc ); 271 Debug( LDAP_DEBUG_ANY, 272 "=> wt_tool_next_id: %s\n", text->bv_val ); 273 } 274 275 }else if ( !hole ) { 276 unsigned i, j; 277 e->e_id = id; 278 279 for ( i=0; i<nholes; i++) { 280 if ( holes[i].id == e->e_id ) { 281 free(holes[i].dn.bv_val); 282 for (j=i;j<nholes;j++) holes[j] = holes[j+1]; 283 holes[j].id = 0; 284 nholes--; 285 break; 286 } else if ( holes[i].id > e->e_id ) { 287 break; 288 } 289 } 290 } 291 return rc; 292 } 293 294 static int 295 wt_tool_index_add( 296 Operation *op, 297 wt_ctx *wc, 298 Entry *e ) 299 { 300 return wt_index_entry_add( op, wc, e ); 301 } 302 303 ID 304 wt_tool_entry_put( BackendDB *be, Entry *e, struct berval *text ) 305 { 306 struct wt_info *wi = (struct wt_info *) be->be_private; 307 int rc; 308 309 Operation op = {0}; 310 Opheader ohdr = {0}; 311 312 assert( slapMode & SLAP_TOOL_MODE ); 313 assert( text != NULL ); 314 assert( text->bv_val != NULL ); 315 assert( text->bv_val[0] == '\0' ); /* overconservative? */ 316 317 Debug( LDAP_DEBUG_TRACE, 318 "=> " LDAP_XSTRING(wt_tool_entry_put) 319 ": ( \"%s\" )\n", e->e_dn ); 320 321 rc = wc->session->begin_transaction(wc->session, NULL); 322 if( rc ){ 323 Debug( LDAP_DEBUG_ANY, 324 LDAP_XSTRING(wt_dn2id_add) 325 ": begin_transaction failed: %s (%d)\n", 326 wiredtiger_strerror(rc), rc ); 327 return NOID; 328 } 329 330 op.o_hdr = &ohdr; 331 op.o_bd = be; 332 op.o_tmpmemctx = NULL; 333 op.o_tmpmfuncs = &ch_mfuncs; 334 335 rc = wt_tool_next_id( &op, e, text, 0 ); 336 if( rc != 0 ) { 337 snprintf( text->bv_val, text->bv_len, 338 "wt_tool_next_id failed: %s (%d)", 339 wiredtiger_strerror(rc), rc ); 340 Debug( LDAP_DEBUG_ANY, 341 "=> " LDAP_XSTRING(wt_tool_entry_put) ": %s\n", 342 text->bv_val ); 343 goto done; 344 } 345 346 rc = wt_id2entry_add( &op, wc->session, e ); 347 if( rc != 0 ) { 348 snprintf( text->bv_val, text->bv_len, 349 "id2entry_add failed: %s (%d)", 350 wiredtiger_strerror(rc), rc ); 351 Debug( LDAP_DEBUG_ANY, 352 "=> " LDAP_XSTRING(wt_tool_entry_put) ": %s\n", 353 text->bv_val ); 354 goto done; 355 } 356 357 rc = wt_tool_index_add( &op, wc, e ); 358 if( rc != 0 ) { 359 snprintf( text->bv_val, text->bv_len, 360 "index_entry_add failed: %s (%d)", 361 rc == LDAP_OTHER ? "Internal error" : 362 wiredtiger_strerror(rc), rc ); 363 Debug( LDAP_DEBUG_ANY, 364 "=> " LDAP_XSTRING(wt_tool_entry_put) ": %s\n", 365 text->bv_val ); 366 goto done; 367 } 368 369 done: 370 if ( rc == 0 ){ 371 rc = wc->session->commit_transaction(wc->session, NULL); 372 if( rc != 0 ) { 373 snprintf( text->bv_val, text->bv_len, 374 "txn_commit failed: %s (%d)", 375 wiredtiger_strerror(rc), rc ); 376 Debug( LDAP_DEBUG_ANY, 377 "=> " LDAP_XSTRING(wt_tool_entry_put) ": %s\n", 378 text->bv_val ); 379 e->e_id = NOID; 380 } 381 }else{ 382 rc = wc->session->rollback_transaction(wc->session, NULL); 383 snprintf( text->bv_val, text->bv_len, 384 "txn_aborted! %s (%d)", 385 rc == LDAP_OTHER ? "Internal error" : 386 wiredtiger_strerror(rc), rc ); 387 Debug( LDAP_DEBUG_ANY, 388 "=> " LDAP_XSTRING(wt_tool_entry_put) ": %s\n", 389 text->bv_val ); 390 e->e_id = NOID; 391 } 392 393 return e->e_id; 394 } 395 396 int wt_tool_entry_reindex( 397 BackendDB *be, 398 ID id, 399 AttributeDescription **adv ) 400 { 401 struct wt_info *wi = (struct wt_info *) be->be_private; 402 int rc; 403 Entry *e; 404 Operation op = {0}; 405 Opheader ohdr = {0}; 406 407 Debug( LDAP_DEBUG_ARGS, 408 "=> " LDAP_XSTRING(wt_tool_entry_reindex) "( %ld )\n", 409 (long) id ); 410 assert( tool_base == NULL ); 411 assert( tool_filter == NULL ); 412 413 /* No indexes configured, nothing to do. Could return an 414 * error here to shortcut things. 415 */ 416 if (!wi->wi_attrs) { 417 return 0; 418 } 419 420 /* Check for explicit list of attrs to index */ 421 if ( adv ) { 422 int i, j, n; 423 424 if ( wi->wi_attrs[0]->ai_desc != adv[0] ) { 425 /* count */ 426 for ( n = 0; adv[n]; n++ ) ; 427 428 /* insertion sort */ 429 for ( i = 0; i < n; i++ ) { 430 AttributeDescription *ad = adv[i]; 431 for ( j = i-1; j>=0; j--) { 432 if ( SLAP_PTRCMP( adv[j], ad ) <= 0 ) break; 433 adv[j+1] = adv[j]; 434 } 435 adv[j+1] = ad; 436 } 437 } 438 439 for ( i = 0; adv[i]; i++ ) { 440 if ( wi->wi_attrs[i]->ai_desc != adv[i] ) { 441 for ( j = i+1; j < wi->wi_nattrs; j++ ) { 442 if ( wi->wi_attrs[j]->ai_desc == adv[i] ) { 443 AttrInfo *ai = wi->wi_attrs[i]; 444 wi->wi_attrs[i] = wi->wi_attrs[j]; 445 wi->wi_attrs[j] = ai; 446 break; 447 } 448 } 449 if ( j == wi->wi_nattrs ) { 450 Debug( LDAP_DEBUG_ANY, 451 LDAP_XSTRING(wt_tool_entry_reindex) 452 ": no index configured for %s\n", 453 adv[i]->ad_cname.bv_val ); 454 return -1; 455 } 456 } 457 } 458 wi->wi_nattrs = i; 459 } 460 461 e = wt_tool_entry_get( be, id ); 462 463 if( e == NULL ) { 464 Debug( LDAP_DEBUG_ANY, 465 LDAP_XSTRING(wt_tool_entry_reindex) 466 ": could not locate id=%ld\n", 467 (long) id ); 468 return -1; 469 } 470 471 op.o_hdr = &ohdr; 472 op.o_bd = be; 473 op.o_tmpmemctx = NULL; 474 op.o_tmpmfuncs = &ch_mfuncs; 475 476 rc = wc->session->begin_transaction(wc->session, NULL); 477 if( rc ){ 478 Debug( LDAP_DEBUG_ANY, 479 LDAP_XSTRING(wt_dn2id_add) 480 ": begin_transaction failed: %s (%d)\n", 481 wiredtiger_strerror(rc), rc ); 482 goto done; 483 } 484 Debug( LDAP_DEBUG_TRACE, 485 "=> " LDAP_XSTRING(wt_tool_entry_reindex) "( %ld, \"%s\" )\n", 486 (long) id, e->e_dn ); 487 488 rc = wt_tool_index_add( &op, wc, e ); 489 490 done: 491 if ( rc == 0 ){ 492 rc = wc->session->commit_transaction(wc->session, NULL); 493 if( rc ) { 494 Debug( LDAP_DEBUG_ANY, 495 "=> " LDAP_XSTRING(wt_tool_entry_reindex) 496 "commit_transaction failed: %s (%d)\n", 497 wiredtiger_strerror(rc), rc ); 498 } 499 }else{ 500 rc = wc->session->rollback_transaction(wc->session, NULL); 501 Debug( LDAP_DEBUG_ANY, 502 "=> " LDAP_XSTRING(wt_tool_entry_reindex) 503 ": rollback transaction %s (%d)\n", 504 wiredtiger_strerror(rc), rc ); 505 } 506 507 wt_entry_release( &op, e, 0 ); 508 509 return rc; 510 } 511 512 /* 513 * Local variables: 514 * indent-tabs-mode: t 515 * tab-width: 4 516 * c-basic-offset: 4 517 * End: 518 */ 519