diff options
Diffstat (limited to 'servers/slapd/back-wt/tools.c')
-rw-r--r-- | servers/slapd/back-wt/tools.c | 712 |
1 files changed, 712 insertions, 0 deletions
diff --git a/servers/slapd/back-wt/tools.c b/servers/slapd/back-wt/tools.c new file mode 100644 index 0000000..658837f --- /dev/null +++ b/servers/slapd/back-wt/tools.c @@ -0,0 +1,712 @@ +/* OpenLDAP WiredTiger backend */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * 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 + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENTS: + * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp> + * based on back-bdb for inclusion in OpenLDAP Software. + * WiredTiger is a product of MongoDB Inc. + */ + +#include "portable.h" + +#include <stdio.h> +#include <ac/string.h> +#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; i<nholes; i++) { + fprintf(stderr, " entry %ld: %s\n", + holes[i].id, holes[i].dn.bv_val); + } + return -1; + } + + return 0; +} + +ID +wt_tool_entry_first_x( BackendDB *be, + struct berval *base, + int scope, + Filter *f ) +{ + tool_base = base; + tool_scope = scope; + tool_filter = f; + + return wt_tool_entry_next( be ); +} + +ID +wt_tool_entry_next( BackendDB *be ) +{ + int rc; + ID id; + + rc = reader->next(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; i<nholes; i++) { + if ( holes[i].id == e->e_id ) { + free(holes[i].dn.bv_val); + for (j=i;j<nholes;j++) holes[j] = holes[j+1]; + holes[j].id = 0; + nholes--; + break; + } else if ( holes[i].id > 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: + */ |