/* OpenLDAP WiredTiger backend */ /* $OpenLDAP$ */ /* This work is part of OpenLDAP Software . * * Copyright 2002-2022 The OpenLDAP Foundation. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted only as authorized by the OpenLDAP * Public License. * * A copy of this license is available in the file LICENSE in the * top-level directory of the distribution or, alternatively, at * . */ /* ACKNOWLEDGEMENTS: * This work was developed by HAMANO Tsukasa * based on back-bdb for inclusion in OpenLDAP Software. * WiredTiger is a product of MongoDB Inc. */ #include "portable.h" #include #include #include "back-wt.h" #include "slap-config.h" typedef struct dn_id { ID id; struct berval dn; } dn_id; #define HOLE_SIZE 4096 static dn_id hbuf[HOLE_SIZE], *holes = hbuf; static unsigned nholes; static struct berval *tool_base; static int tool_scope; static Filter *tool_filter; static wt_ctx *wc; static WT_CURSOR *reader; static WT_ITEM item; int wt_tool_entry_open( BackendDB *be, int mode ) { struct wt_info *wi = (struct wt_info *) be->be_private; int rc; wc = wt_ctx_init(wi); if( !wc ){ Debug( LDAP_DEBUG_ANY, "wt_tool_entry_open: wt_ctx_get failed\n" ); return -1; } rc = wc->session->open_cursor(wc->session, WT_TABLE_ID2ENTRY"(entry)" ,NULL, NULL, &reader); if ( rc ) { Debug( LDAP_DEBUG_ANY, "wt_tool_entry_open: cursor open failed: %s (%d)\n", wiredtiger_strerror(rc), rc ); return -1; } return 0; } int wt_tool_entry_close( BackendDB *be ) { if( reader ) { reader->close(reader); reader = NULL; } wt_ctx_free(NULL, wc); if( nholes ) { unsigned i; fprintf( stderr, "Error, entries missing!\n"); for (i=0; inext(reader); switch( rc ){ case 0: break; case WT_NOTFOUND: return NOID; default: Debug( LDAP_DEBUG_ANY, "wt_tool_entry_next: next failed: %s (%d)\n", wiredtiger_strerror(rc), rc ); return NOID; } rc = reader->get_key(reader, &id); if( rc ){ Debug( LDAP_DEBUG_ANY, "wt_tool_entry_next: get_key failed: %s (%d)\n", wiredtiger_strerror(rc), rc ); } return id; } static ber_len_t entry_getlen(unsigned char **buf) { ber_len_t len; int i; len = *(*buf)++; if (len <= 0x7f) return len; i = len & 0x7f; len = 0; for (;i > 0; i--) { len <<= 8; len |= *(*buf)++; } return len; } int wt_entry_header(WT_ITEM *item, EntryHeader *eh) { unsigned char *ptr = (unsigned char *)item->data; /* Some overlays can create empty entries * so don't check for zeros here. */ eh->nattrs = entry_getlen(&ptr); eh->nvals = entry_getlen(&ptr); eh->data = (char *)ptr; return LDAP_SUCCESS; } Entry * wt_tool_entry_get( BackendDB *be, ID id ) { Entry *e = NULL; static EntryHeader eh; int rc, eoff; assert( be != NULL ); assert( slapMode & SLAP_TOOL_MODE ); reader->set_key(reader, id); rc = reader->search(reader); if ( rc ) { Debug( LDAP_DEBUG_ANY, "wt_tool_entry_get: search failed: %s (%d)\n", wiredtiger_strerror(rc), rc ); goto done; } rc = reader->get_value(reader, &item); if( rc ){ Debug( LDAP_DEBUG_ANY, "wt_tool_entry_get: get_value failed: %s (%d)\n", wiredtiger_strerror(rc), rc ); goto done; } rc = wt_entry_header( &item, &eh ); assert( rc == 0 ); eoff = eh.data - (char *)item.data; eh.bv.bv_len = eh.nvals * sizeof( struct berval ) + item.size; eh.bv.bv_val = ch_realloc( eh.bv.bv_val, eh.bv.bv_len ); memset(eh.bv.bv_val, 0xff, eh.bv.bv_len); eh.data = eh.bv.bv_val + eh.nvals * sizeof( struct berval ); memcpy(eh.data, item.data, item.size); eh.data += eoff; rc = entry_decode( &eh, &e ); assert( rc == 0 ); if( rc == LDAP_SUCCESS ) { e->e_id = id; } done: return e; } static int wt_tool_next_id( Operation *op, Entry *e, struct berval *text, int hole ) { struct berval dn = e->e_name; struct berval ndn = e->e_nname; struct berval pdn, npdn; int rc; ID id = 0; ID pid = 0; if(ndn.bv_len == 0){ e->e_id = 0; return 0; } rc = wt_dn2id(op, wc, &ndn, &id); if(rc == 0){ e->e_id = id; }else if( rc == WT_NOTFOUND ){ if ( !be_issuffix( op->o_bd, &ndn ) ) { ID eid = e->e_id; dnParent( &dn, &pdn ); dnParent( &ndn, &npdn ); e->e_name = pdn; e->e_nname = npdn; rc = wt_tool_next_id( op, e, text, 1 ); e->e_name = dn; e->e_nname = ndn; if ( rc ) { return rc; } /* If parent didn't exist, it was created just now * and its ID is now in e->e_id. Make sure the current * entry gets added under the new parent ID. */ if ( eid != e->e_id ) { pid = e->e_id; } }else{ pid = id; } wt_next_id( op->o_bd, &e->e_id ); rc = wt_dn2id_add(op, wc, pid, e); if( rc ){ snprintf( text->bv_val, text->bv_len, "wt_dn2id_add failed: %s (%d)", wiredtiger_strerror(rc), rc ); Debug( LDAP_DEBUG_ANY, "=> wt_tool_next_id: %s\n", text->bv_val ); } }else if ( !hole ) { unsigned i, j; e->e_id = id; for ( i=0; ie_id ) { free(holes[i].dn.bv_val); for (j=i;j e->e_id ) { break; } } } return rc; } static int wt_tool_index_add( Operation *op, wt_ctx *wc, Entry *e ) { return wt_index_entry_add( op, wc, e ); } ID wt_tool_entry_put( BackendDB *be, Entry *e, struct berval *text ) { int rc; Operation op = {0}; Opheader ohdr = {0}; assert( slapMode & SLAP_TOOL_MODE ); assert( text != NULL ); assert( text->bv_val != NULL ); assert( text->bv_val[0] == '\0' ); /* overconservative? */ Debug( LDAP_DEBUG_TRACE, "=> wt_tool_entry_put: ( \"%s\" )\n", e->e_dn ); rc = wc->session->begin_transaction(wc->session, NULL); if( rc ){ Debug( LDAP_DEBUG_ANY, "wt_dn2id_add: begin_transaction failed: %s (%d)\n", wiredtiger_strerror(rc), rc ); return NOID; } op.o_hdr = &ohdr; op.o_bd = be; op.o_tmpmemctx = NULL; op.o_tmpmfuncs = &ch_mfuncs; rc = wt_tool_next_id( &op, e, text, 0 ); if( rc != 0 ) { snprintf( text->bv_val, text->bv_len, "wt_tool_next_id failed: %s (%d)", wiredtiger_strerror(rc), rc ); Debug( LDAP_DEBUG_ANY, "=> wt_tool_entry_put: %s\n", text->bv_val ); goto done; } rc = wt_id2entry_add( &op, wc, e ); if( rc != 0 ) { snprintf( text->bv_val, text->bv_len, "id2entry_add failed: %s (%d)", wiredtiger_strerror(rc), rc ); Debug( LDAP_DEBUG_ANY, "=> wt_tool_entry_put: %s\n", text->bv_val ); goto done; } rc = wt_tool_index_add( &op, wc, e ); if( rc != 0 ) { snprintf( text->bv_val, text->bv_len, "index_entry_add failed: %s (%d)", rc == LDAP_OTHER ? "Internal error" : wiredtiger_strerror(rc), rc ); Debug( LDAP_DEBUG_ANY, "=> wt_tool_entry_put: %s\n", text->bv_val ); goto done; } done: if ( rc == 0 ){ rc = wc->session->commit_transaction(wc->session, NULL); if( rc != 0 ) { snprintf( text->bv_val, text->bv_len, "txn_commit failed: %s (%d)", wiredtiger_strerror(rc), rc ); Debug( LDAP_DEBUG_ANY, "=> wt_tool_entry_put: %s\n", text->bv_val ); e->e_id = NOID; } }else{ rc = wc->session->rollback_transaction(wc->session, NULL); snprintf( text->bv_val, text->bv_len, "txn_aborted! %s (%d)", rc == LDAP_OTHER ? "Internal error" : wiredtiger_strerror(rc), rc ); Debug( LDAP_DEBUG_ANY, "=> wt_tool_entry_put: %s\n", text->bv_val ); e->e_id = NOID; } return e->e_id; } int wt_tool_entry_reindex( BackendDB *be, ID id, AttributeDescription **adv ) { struct wt_info *wi = (struct wt_info *) be->be_private; int rc; Entry *e; Operation op = {0}; Opheader ohdr = {0}; Debug( LDAP_DEBUG_ARGS, "=> wt_tool_entry_reindex( %ld )\n", (long) id ); assert( tool_base == NULL ); assert( tool_filter == NULL ); /* No indexes configured, nothing to do. Could return an * error here to shortcut things. */ if (!wi->wi_attrs) { return 0; } /* Check for explicit list of attrs to index */ if ( adv ) { int i, j, n; if ( wi->wi_attrs[0]->ai_desc != adv[0] ) { /* count */ for ( n = 0; adv[n]; n++ ) ; /* insertion sort */ for ( i = 0; i < n; i++ ) { AttributeDescription *ad = adv[i]; for ( j = i-1; j>=0; j--) { if ( SLAP_PTRCMP( adv[j], ad ) <= 0 ) break; adv[j+1] = adv[j]; } adv[j+1] = ad; } } for ( i = 0; adv[i]; i++ ) { if ( wi->wi_attrs[i]->ai_desc != adv[i] ) { for ( j = i+1; j < wi->wi_nattrs; j++ ) { if ( wi->wi_attrs[j]->ai_desc == adv[i] ) { AttrInfo *ai = wi->wi_attrs[i]; wi->wi_attrs[i] = wi->wi_attrs[j]; wi->wi_attrs[j] = ai; break; } } if ( j == wi->wi_nattrs ) { Debug( LDAP_DEBUG_ANY, "wt_tool_entry_reindex: no index configured for %s\n", adv[i]->ad_cname.bv_val ); return -1; } } } wi->wi_nattrs = i; } e = wt_tool_entry_get( be, id ); if( e == NULL ) { Debug( LDAP_DEBUG_ANY, "=> wt_tool_entry_reindex" ": could not locate id=%ld\n", (long) id ); return -1; } op.o_hdr = &ohdr; op.o_bd = be; op.o_tmpmemctx = NULL; op.o_tmpmfuncs = &ch_mfuncs; rc = wc->session->begin_transaction(wc->session, NULL); if( rc ){ Debug( LDAP_DEBUG_ANY, "wt_tool_entry_reindex: begin_transaction failed %s (%d)\n", wiredtiger_strerror(rc), rc ); goto done; } Debug( LDAP_DEBUG_TRACE, "=> wt_tool_entry_reindex( %ld, \"%s\" )\n", (long) id, e->e_dn ); rc = wt_tool_index_add( &op, wc, e ); done: if ( rc == 0 ){ rc = wc->session->commit_transaction(wc->session, NULL); if( rc ) { Debug( LDAP_DEBUG_ANY, "=> wt_tool_entry_reindex: commit_transaction failed %s (%d)\n", wiredtiger_strerror(rc), rc ); } }else{ rc = wc->session->rollback_transaction(wc->session, NULL); Debug( LDAP_DEBUG_ANY, "=> wt_tool_entry_reindex: rollback transaction %s (%d)\n", wiredtiger_strerror(rc), rc ); } wt_entry_release( &op, e, 0 ); return rc; } ID wt_tool_dn2id_get( Backend *be, struct berval *dn ) { Operation op = {0}; Opheader ohdr = {0}; ID id; int rc; if ( BER_BVISEMPTY(dn) ) return 0; op.o_hdr = &ohdr; op.o_bd = be; op.o_tmpmemctx = NULL; op.o_tmpmfuncs = &ch_mfuncs; rc = wt_dn2id(&op, wc, dn, &id); switch( rc ){ case 0: break; case WT_NOTFOUND: return NOID; default: Debug( LDAP_DEBUG_ANY, "wt_tool_entry_get: entry get failed: %s (%d)\n", wiredtiger_strerror(rc), rc ); return NOID; } return id; } ID wt_tool_entry_modify( BackendDB *be, Entry *e, struct berval *text ) { int rc; Operation op = {0}; Opheader ohdr = {0}; assert( be != NULL ); assert( slapMode & SLAP_TOOL_MODE ); assert( text != NULL ); assert( text->bv_val != NULL ); assert( text->bv_val[0] == '\0' ); /* overconservative? */ assert ( e->e_id != NOID ); Debug( LDAP_DEBUG_TRACE, "=> wt_tool_entry_modify( %ld, \"%s\" )\n", (long) e->e_id, e->e_dn ); rc = wc->session->begin_transaction(wc->session, NULL); if( rc ){ Debug( LDAP_DEBUG_ANY, "=> wt_tool_entry_modify" ": begin_transaction failed: %s (%d)\n", wiredtiger_strerror(rc), rc ); return NOID; } op.o_hdr = &ohdr; op.o_bd = be; op.o_tmpmemctx = NULL; op.o_tmpmfuncs = &ch_mfuncs; rc = wt_id2entry_update( &op, wc, e ); if( rc != 0 ) { snprintf( text->bv_val, text->bv_len, "id2entry_update failed: %s (%d)", wiredtiger_strerror(rc), rc ); Debug( LDAP_DEBUG_ANY, "=> wt_tool_entry_modify: %s\n", text->bv_val ); goto done; } done: if ( rc == 0 ){ rc = wc->session->commit_transaction(wc->session, NULL); if( rc != 0 ) { snprintf( text->bv_val, text->bv_len, "txn_commit failed: %s (%d)", wiredtiger_strerror(rc), rc ); Debug( LDAP_DEBUG_ANY, "=> wt_tool_entry_modify: %s\n", text->bv_val ); e->e_id = NOID; } }else{ rc = wc->session->rollback_transaction(wc->session, NULL); snprintf( text->bv_val, text->bv_len, "txn_aborted! %s (%d)", rc == LDAP_OTHER ? "Internal error" : wiredtiger_strerror(rc), rc ); Debug( LDAP_DEBUG_ANY, "=> wt_tool_entry_modify: %s\n", text->bv_val ); e->e_id = NOID; } return e->e_id; } int wt_tool_entry_delete( BackendDB *be, struct berval *ndn, struct berval *text ) { struct wt_info *wi = (struct wt_info *) be->be_private; int rc; Operation op = {0}; Opheader ohdr = {0}; Entry *e = NULL; assert( be != NULL ); assert( slapMode & SLAP_TOOL_MODE ); assert( text != NULL ); assert( text->bv_val != NULL ); assert( text->bv_val[0] == '\0' ); /* overconservative? */ assert ( ndn != NULL ); assert ( ndn->bv_val != NULL ); Debug( LDAP_DEBUG_TRACE, "=> wt_tool_entry_delete( %s )\n", ndn->bv_val ); op.o_hdr = &ohdr; op.o_bd = be; op.o_tmpmemctx = NULL; op.o_tmpmfuncs = &ch_mfuncs; /* get entry */ rc = wt_dn2entry(op.o_bd, wc, ndn, &e); switch( rc ) { case 0: break; case WT_NOTFOUND: Debug( LDAP_DEBUG_ARGS, "<== wt_tool_entry_delete: no such object %s\n", ndn->bv_val); goto done; default: Debug( LDAP_DEBUG_ANY, "wt_tool_entry_delete: error at wt_dn2entry() rc=%d\n", rc ); goto done; } rc = wt_dn2id_has_children( &op, wc, e->e_id ); if( rc != WT_NOTFOUND ) { /* subordinate objects must be deleted first */ rc = -1; goto done; } rc = wc->session->begin_transaction(wc->session, NULL); if( rc ){ Debug( LDAP_DEBUG_ANY, "wt_tool_entry_delete: begin_transaction failed: %s (%d)\n", wiredtiger_strerror(rc), rc ); goto done; } /* delete from dn2id */ rc = wt_dn2id_delete( &op, wc, &e->e_nname); if ( rc ) { Debug( LDAP_DEBUG_TRACE, "<== wt_tool_entry_delete: dn2id failed: %s (%d)\n", wiredtiger_strerror(rc), rc ); wc->session->rollback_transaction(wc->session, NULL); goto done; } /* delete indices for old attributes */ rc = wt_index_entry_del( &op, wc, e ); if ( rc ) { Debug( LDAP_DEBUG_TRACE, "<== wt_tool_entry_delete: index delete failed: %s (%d)\n", wiredtiger_strerror(rc), rc ); wc->session->rollback_transaction(wc->session, NULL); goto done; } /* delete from id2entry */ rc = wt_id2entry_delete( &op, wc, e ); if ( rc ) { Debug( LDAP_DEBUG_TRACE, "<== wt_tool_entry_delete: id2entry failed: %s (%d)\n", wiredtiger_strerror(rc), rc ); wc->session->rollback_transaction(wc->session, NULL); goto done; } rc = wc->session->commit_transaction(wc->session, NULL); if( rc != 0 ) { snprintf( text->bv_val, text->bv_len, "txn_commit failed: %s (%d)", wiredtiger_strerror(rc), rc ); Debug( LDAP_DEBUG_ANY, "=> wt_tool_entry_delete: %s\n", text->bv_val ); goto done; } done: /* free entry */ if( e != NULL ) { wt_entry_return( e ); } return rc; } /* * Local variables: * indent-tabs-mode: t * tab-width: 4 * c-basic-offset: 4 * End: */