int hdb_dn2id( Operation *op, struct berval *in, EntryInfo *ei, DB_TXN *txn, DBC **cursor ) { struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private; DB *db = bdb->bi_dn2id->bdi_db; DBT key, data; int rc = 0, nrlen; diskNode *d; char *ptr; unsigned char dlen[2]; ID idp, parentID; Debug( LDAP_DEBUG_TRACE, "=> hdb_dn2id(\"%s\")\n", in->bv_val, 0, 0 ); nrlen = dn_rdnlen( op->o_bd, in ); if (!nrlen) nrlen = in->bv_len; DBTzero(&key); key.size = sizeof(ID); key.data = &idp; key.ulen = sizeof(ID); key.flags = DB_DBT_USERMEM; parentID = ( ei->bei_parent != NULL ) ? ei->bei_parent->bei_id : 0; BDB_ID2DISK( parentID, &idp ); DBTzero(&data); data.size = sizeof(diskNode) + nrlen - sizeof(ID) - 1; data.ulen = data.size * 3; data.dlen = data.ulen; data.flags = DB_DBT_USERMEM | DB_DBT_PARTIAL; rc = db->cursor( db, txn, cursor, bdb->bi_db_opflags ); if ( rc ) return rc; d = op->o_tmpalloc( data.size * 3, op->o_tmpmemctx ); d->nrdnlen[1] = nrlen & 0xff; d->nrdnlen[0] = (nrlen >> 8) | 0x80; dlen[0] = d->nrdnlen[0]; dlen[1] = d->nrdnlen[1]; ptr = lutil_strncopy( d->nrdn, in->bv_val, nrlen ); *ptr = '\0'; data.data = d; rc = (*cursor)->c_get( *cursor, &key, &data, DB_GET_BOTH_RANGE ); if ( rc == 0 && (dlen[1] != d->nrdnlen[1] || dlen[0] != d->nrdnlen[0] || strncmp( d->nrdn, in->bv_val, nrlen ))) { rc = DB_NOTFOUND; } if ( rc == 0 ) { ptr = (char *) data.data + data.size - sizeof(ID); BDB_DISK2ID( ptr, &ei->bei_id ); ei->bei_rdn.bv_len = data.size - sizeof(diskNode) - nrlen; ptr = d->nrdn + nrlen + 1; ber_str2bv( ptr, ei->bei_rdn.bv_len, 1, &ei->bei_rdn ); if ( ei->bei_parent != NULL && !ei->bei_parent->bei_dkids ) { db_recno_t dkids; /* How many children does the parent have? */ /* FIXME: do we need to lock the parent * entryinfo? Seems safe... */ (*cursor)->c_count( *cursor, &dkids, 0 ); ei->bei_parent->bei_dkids = dkids; } } op->o_tmpfree( d, op->o_tmpmemctx ); if( rc != 0 ) { Debug( LDAP_DEBUG_TRACE, "<= hdb_dn2id: get failed: %s (%d)\n", db_strerror( rc ), rc, 0 ); } else { Debug( LDAP_DEBUG_TRACE, "<= hdb_dn2id: got id=0x%lx\n", ei->bei_id, 0, 0 ); } return rc; }
/* We add two elements to the DN2ID database - a data item under the parent's * entryID containing the child's RDN and entryID, and an item under the * child's entryID containing the parent's entryID. */ int hdb_dn2id_add( Operation *op, DB_TXN *txn, EntryInfo *eip, Entry *e ) { struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private; DB *db = bdb->bi_dn2id->bdi_db; DBT key, data; ID nid; int rc, rlen, nrlen; diskNode *d; char *ptr; Debug( LDAP_DEBUG_TRACE, "=> hdb_dn2id_add 0x%lx: \"%s\"\n", e->e_id, e->e_ndn, 0 ); nrlen = dn_rdnlen( op->o_bd, &e->e_nname ); if (nrlen) { rlen = dn_rdnlen( op->o_bd, &e->e_name ); } else { nrlen = e->e_nname.bv_len; rlen = e->e_name.bv_len; } d = op->o_tmpalloc(sizeof(diskNode) + rlen + nrlen, op->o_tmpmemctx); d->nrdnlen[1] = nrlen & 0xff; d->nrdnlen[0] = (nrlen >> 8) | 0x80; ptr = lutil_strncopy( d->nrdn, e->e_nname.bv_val, nrlen ); *ptr++ = '\0'; ptr = lutil_strncopy( ptr, e->e_name.bv_val, rlen ); *ptr++ = '\0'; BDB_ID2DISK( e->e_id, ptr ); DBTzero(&key); DBTzero(&data); key.size = sizeof(ID); key.flags = DB_DBT_USERMEM; BDB_ID2DISK( eip->bei_id, &nid ); key.data = &nid; /* Need to make dummy root node once. Subsequent attempts * will fail harmlessly. */ if ( eip->bei_id == 0 ) { diskNode dummy = {{0, 0}, "", "", ""}; data.data = &dummy; data.size = sizeof(diskNode); data.flags = DB_DBT_USERMEM; db->put( db, txn, &key, &data, DB_NODUPDATA ); } data.data = d; data.size = sizeof(diskNode) + rlen + nrlen; data.flags = DB_DBT_USERMEM; rc = db->put( db, txn, &key, &data, DB_NODUPDATA ); if (rc == 0) { BDB_ID2DISK( e->e_id, &nid ); BDB_ID2DISK( eip->bei_id, ptr ); d->nrdnlen[0] ^= 0x80; rc = db->put( db, txn, &key, &data, DB_NODUPDATA ); } /* Update all parents' IDL cache entries */ if ( rc == 0 && bdb->bi_idl_cache_size ) { ID tmp[2]; char *ptr = ((char *)&tmp[1])-1; key.data = ptr; key.size = sizeof(ID)+1; tmp[1] = eip->bei_id; *ptr = DN_ONE_PREFIX; bdb_idl_cache_add_id( bdb, db, &key, e->e_id ); if ( eip->bei_parent ) { *ptr = DN_SUBTREE_PREFIX; for (; eip && eip->bei_parent->bei_id; eip = eip->bei_parent) { tmp[1] = eip->bei_id; bdb_idl_cache_add_id( bdb, db, &key, e->e_id ); } /* Handle DB with empty suffix */ if ( !op->o_bd->be_suffix[0].bv_len && eip ) { tmp[1] = eip->bei_id; bdb_idl_cache_add_id( bdb, db, &key, e->e_id ); } } } op->o_tmpfree( d, op->o_tmpmemctx ); Debug( LDAP_DEBUG_TRACE, "<= hdb_dn2id_add 0x%lx: %d\n", e->e_id, rc, 0 ); return rc; }
static int limits_get( Operation *op, struct slap_limits_set **limit ) { static struct berval empty_dn = BER_BVC( "" ); struct slap_limits **lm; struct berval *ndns[2]; assert( op != NULL ); assert( limit != NULL ); ndns[0] = &op->o_ndn; ndns[1] = &op->o_req_ndn; Debug( LDAP_DEBUG_TRACE, "==> limits_get: %s self=\"%s\" this=\"%s\"\n", op->o_log_prefix, BER_BVISNULL( ndns[0] ) ? "[anonymous]" : ndns[0]->bv_val, BER_BVISNULL( ndns[1] ) ? "" : ndns[1]->bv_val ); /* * default values */ *limit = &op->o_bd->be_def_limit; if ( op->o_bd->be_limits == NULL ) { return( 0 ); } for ( lm = op->o_bd->be_limits; lm[0] != NULL; lm++ ) { unsigned style = lm[0]->lm_flags & SLAP_LIMITS_MASK; unsigned type = lm[0]->lm_flags & SLAP_LIMITS_TYPE_MASK; unsigned isthis = type == SLAP_LIMITS_TYPE_THIS; struct berval *ndn = ndns[isthis]; if ( style == SLAP_LIMITS_ANY ) goto found_any; if ( BER_BVISEMPTY( ndn ) ) { if ( style == SLAP_LIMITS_ANONYMOUS ) goto found_nodn; if ( !isthis ) continue; ndn = &empty_dn; } switch ( style ) { case SLAP_LIMITS_EXACT: if ( type == SLAP_LIMITS_TYPE_GROUP ) { int rc = backend_group( op, NULL, &lm[0]->lm_pat, ndn, lm[0]->lm_group_oc, lm[0]->lm_group_ad ); if ( rc == 0 ) { goto found_group; } } else { if ( dn_match( &lm[0]->lm_pat, ndn ) ) { goto found_dn; } } break; case SLAP_LIMITS_ONE: case SLAP_LIMITS_SUBTREE: case SLAP_LIMITS_CHILDREN: { ber_len_t d; /* ndn shorter than lm_pat */ if ( ndn->bv_len < lm[0]->lm_pat.bv_len ) { break; } d = ndn->bv_len - lm[0]->lm_pat.bv_len; if ( d == 0 ) { /* allow exact match for SUBTREE only */ if ( style != SLAP_LIMITS_SUBTREE ) { break; } } else { /* check for unescaped rdn separator */ if ( !DN_SEPARATOR( ndn->bv_val[d - 1] ) ) { break; } } /* check that ndn ends with lm_pat */ if ( strcmp( lm[0]->lm_pat.bv_val, &ndn->bv_val[d] ) != 0 ) { break; } /* in case of ONE, require exactly one rdn below lm_pat */ if ( style == SLAP_LIMITS_ONE ) { if ( dn_rdnlen( NULL, ndn ) != d - 1 ) { break; } } goto found_dn; } case SLAP_LIMITS_REGEX: if ( regexec( &lm[0]->lm_regex, ndn->bv_val, 0, NULL, 0 ) == 0 ) { goto found_dn; } break; case SLAP_LIMITS_ANONYMOUS: break; case SLAP_LIMITS_USERS: found_nodn: Debug( LDAP_DEBUG_TRACE, "<== limits_get: type=%s match=%s\n", dn_source[isthis], limits2str( style ), 0 ); found_any: *limit = &lm[0]->lm_limits; return( 0 ); found_dn: Debug( LDAP_DEBUG_TRACE, "<== limits_get: type=%s match=%s dn=\"%s\"\n", dn_source[isthis], limits2str( style ), lm[0]->lm_pat.bv_val ); *limit = &lm[0]->lm_limits; return( 0 ); found_group: Debug( LDAP_DEBUG_TRACE, "<== limits_get: type=GROUP match=EXACT " "dn=\"%s\" oc=\"%s\" ad=\"%s\"\n", lm[0]->lm_pat.bv_val, lm[0]->lm_group_oc->soc_cname.bv_val, lm[0]->lm_group_ad->ad_cname.bv_val ); *limit = &lm[0]->lm_limits; return( 0 ); default: assert( 0 ); /* unreachable */ return( -1 ); } } return( 0 ); }
/* We add two elements to the DN2ID database - a data item under the parent's * entryID containing the child's RDN and entryID, and an item under the * child's entryID containing the parent's entryID. */ int mdb_dn2id_add( Operation *op, MDB_cursor *mcp, MDB_cursor *mcd, ID pid, ID nsubs, int upsub, Entry *e ) { struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private; MDB_val key, data; ID nid; int rc, rlen, nrlen; diskNode *d; char *ptr; Debug( LDAP_DEBUG_TRACE, "=> mdb_dn2id_add 0x%lx: \"%s\"\n", e->e_id, e->e_ndn ? e->e_ndn : "" ); nrlen = dn_rdnlen( op->o_bd, &e->e_nname ); if (nrlen) { rlen = dn_rdnlen( op->o_bd, &e->e_name ); } else { nrlen = e->e_nname.bv_len; rlen = e->e_name.bv_len; } d = op->o_tmpalloc(sizeof(diskNode) + rlen + nrlen + sizeof(ID), op->o_tmpmemctx); d->nrdnlen[1] = nrlen & 0xff; d->nrdnlen[0] = (nrlen >> 8) | 0x80; ptr = lutil_strncopy( d->nrdn, e->e_nname.bv_val, nrlen ); *ptr++ = '\0'; ptr = lutil_strncopy( ptr, e->e_name.bv_val, rlen ); *ptr++ = '\0'; memcpy( ptr, &e->e_id, sizeof( ID )); ptr += sizeof( ID ); memcpy( ptr, &nsubs, sizeof( ID )); key.mv_size = sizeof(ID); key.mv_data = &nid; nid = pid; /* Need to make dummy root node once. Subsequent attempts * will fail harmlessly. */ if ( pid == 0 ) { diskNode dummy = {{0, 0}, "", "", ""}; data.mv_data = &dummy; data.mv_size = sizeof(diskNode); mdb_cursor_put( mcp, &key, &data, MDB_NODUPDATA ); } data.mv_data = d; data.mv_size = sizeof(diskNode) + rlen + nrlen + sizeof( ID ); /* Add our child node under parent's key */ rc = mdb_cursor_put( mcp, &key, &data, MDB_NODUPDATA ); /* Add our own node */ if (rc == 0) { int flag = MDB_NODUPDATA; nid = e->e_id; /* drop subtree count */ data.mv_size -= sizeof( ID ); ptr -= sizeof( ID ); memcpy( ptr, &pid, sizeof( ID )); d->nrdnlen[0] ^= 0x80; if ((slapMode & SLAP_TOOL_MODE) || (e->e_id == mdb_read_nextid(mdb))) flag |= MDB_APPEND; rc = mdb_cursor_put( mcd, &key, &data, flag ); } op->o_tmpfree( d, op->o_tmpmemctx ); /* Add our subtree count to all superiors */ if ( rc == 0 && upsub && pid ) { ID subs; nid = pid; do { /* Get parent's RDN */ rc = mdb_cursor_get( mcp, &key, &data, MDB_SET ); if ( !rc ) { char *p2; ptr = (char *)data.mv_data + data.mv_size - sizeof( ID ); memcpy( &nid, ptr, sizeof( ID )); /* Get parent's node under grandparent */ d = data.mv_data; rlen = ( d->nrdnlen[0] << 8 ) | d->nrdnlen[1]; p2 = op->o_tmpalloc( rlen + 2, op->o_tmpmemctx ); memcpy( p2, data.mv_data, rlen+2 ); *p2 ^= 0x80; data.mv_data = p2; rc = mdb_cursor_get( mcp, &key, &data, MDB_GET_BOTH ); op->o_tmpfree( p2, op->o_tmpmemctx ); if ( !rc ) { /* Get parent's subtree count */ ptr = (char *)data.mv_data + data.mv_size - sizeof( ID ); memcpy( &subs, ptr, sizeof( ID )); subs += nsubs; p2 = op->o_tmpalloc( data.mv_size, op->o_tmpmemctx ); memcpy( p2, data.mv_data, data.mv_size - sizeof( ID )); memcpy( p2+data.mv_size - sizeof( ID ), &subs, sizeof( ID )); data.mv_data = p2; rc = mdb_cursor_put( mcp, &key, &data, MDB_CURRENT ); op->o_tmpfree( p2, op->o_tmpmemctx ); } } if ( rc ) break; } while ( nid ); } Debug( LDAP_DEBUG_TRACE, "<= mdb_dn2id_add 0x%lx: %d\n", e->e_id, rc ); return rc; }
/* Find the EntryInfo for the requested DN. If the DN cannot be found, return * the info for its closest ancestor. *res should be NULL to process a * complete DN starting from the tree root. Otherwise *res must be the * immediate parent of the requested DN, and only the RDN will be searched. * The EntryInfo is locked upon return and must be unlocked by the caller. */ int bdb_cache_find_ndn( Operation *op, DB_TXN *txn, struct berval *ndn, EntryInfo **res ) { struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private; EntryInfo ei, *eip, *ei2; int rc = 0; char *ptr; /* this function is always called with normalized DN */ if ( *res ) { /* we're doing a onelevel search for an RDN */ ei.bei_nrdn.bv_val = ndn->bv_val; ei.bei_nrdn.bv_len = dn_rdnlen( op->o_bd, ndn ); eip = *res; } else { /* we're searching a full DN from the root */ ptr = ndn->bv_val + ndn->bv_len - op->o_bd->be_nsuffix[0].bv_len; ei.bei_nrdn.bv_val = ptr; ei.bei_nrdn.bv_len = op->o_bd->be_nsuffix[0].bv_len; /* Skip to next rdn if suffix is empty */ if ( ei.bei_nrdn.bv_len == 0 ) { for (ptr = ei.bei_nrdn.bv_val - 2; ptr > ndn->bv_val && !DN_SEPARATOR(*ptr); ptr--) /* empty */; if ( ptr >= ndn->bv_val ) { if (DN_SEPARATOR(*ptr)) ptr++; ei.bei_nrdn.bv_len = ei.bei_nrdn.bv_val - ptr; ei.bei_nrdn.bv_val = ptr; } } eip = &bdb->bi_cache.c_dntree; } for ( bdb_cache_entryinfo_lock( eip ); eip; ) { eip->bei_state |= CACHE_ENTRY_REFERENCED; ei.bei_parent = eip; ei2 = (EntryInfo *)avl_find( eip->bei_kids, &ei, bdb_rdn_cmp ); if ( !ei2 ) { DBC *cursor; int len = ei.bei_nrdn.bv_len; if ( BER_BVISEMPTY( ndn )) { *res = eip; return LDAP_SUCCESS; } ei.bei_nrdn.bv_len = ndn->bv_len - (ei.bei_nrdn.bv_val - ndn->bv_val); eip->bei_finders++; bdb_cache_entryinfo_unlock( eip ); BDB_LOG_PRINTF( bdb->bi_dbenv, NULL, "slapd Reading %s", ei.bei_nrdn.bv_val ); cursor = NULL; rc = bdb_dn2id( op, &ei.bei_nrdn, &ei, txn, &cursor ); if (rc) { bdb_cache_entryinfo_lock( eip ); eip->bei_finders--; if ( cursor ) cursor->c_close( cursor ); *res = eip; return rc; } BDB_LOG_PRINTF( bdb->bi_dbenv, NULL, "slapd Read got %s(%d)", ei.bei_nrdn.bv_val, ei.bei_id ); /* DN exists but needs to be added to cache */ ei.bei_nrdn.bv_len = len; rc = bdb_entryinfo_add_internal( bdb, &ei, &ei2 ); /* add_internal left eip and c_rwlock locked */ eip->bei_finders--; ldap_pvt_thread_rdwr_wunlock( &bdb->bi_cache.c_rwlock ); if ( cursor ) cursor->c_close( cursor ); if ( rc ) { *res = eip; return rc; } } bdb_cache_entryinfo_lock( ei2 ); if ( ei2->bei_state & CACHE_ENTRY_DELETED ) { /* In the midst of deleting? Give it a chance to * complete. */ bdb_cache_entryinfo_unlock( ei2 ); bdb_cache_entryinfo_unlock( eip ); ldap_pvt_thread_yield(); bdb_cache_entryinfo_lock( eip ); *res = eip; return DB_NOTFOUND; } bdb_cache_entryinfo_unlock( eip ); eip = ei2; /* Advance to next lower RDN */ for (ptr = ei.bei_nrdn.bv_val - 2; ptr > ndn->bv_val && !DN_SEPARATOR(*ptr); ptr--) /* empty */; if ( ptr >= ndn->bv_val ) { if (DN_SEPARATOR(*ptr)) ptr++; ei.bei_nrdn.bv_len = ei.bei_nrdn.bv_val - ptr - 1; ei.bei_nrdn.bv_val = ptr; } if ( ptr < ndn->bv_val ) { *res = eip; break; } } return rc; }
int bdb_dn2id_add( BackendDB *be, DB_TXN *txn, struct berval *pdn, Entry *e ) { struct bdb_info *bdb = (struct bdb_info *) be->be_private; int rc, rlen, nrlen; DBT key, data; DB *db = bdb->bi_id2parent->bdi_db; diskNode *d; idNode *n; nrlen = dn_rdnlen( be, &e->e_nname ); if (nrlen) { rlen = dn_rdnlen( be, &e->e_name ); } else { rlen = 0; } d = ch_malloc(sizeof(diskNode) + rlen + nrlen + 2); d->rdn.bv_len = rlen; d->nrdn.bv_len = nrlen; d->rdn.bv_val = (char *)(d+1); d->nrdn.bv_val = d->rdn.bv_val + rlen + 1; strncpy(d->rdn.bv_val, e->e_dn, rlen); d->rdn.bv_val[rlen] = '\0'; strncpy(d->nrdn.bv_val, e->e_ndn, nrlen); d->nrdn.bv_val[nrlen] = '\0'; d->rdn.bv_val -= (long)d; d->nrdn.bv_val -= (long)d; if (pdn->bv_len) { bdb_dn2id(be, txn, pdn, &d->parent, 0); } else { d->parent = 0; } DBTzero(&key); DBTzero(&data); key.data = &e->e_id; key.size = sizeof(ID); key.flags = DB_DBT_USERMEM; data.data = d; data.size = sizeof(diskNode) + rlen + nrlen + 2; data.flags = DB_DBT_USERMEM; rc = db->put( db, txn, &key, &data, DB_NOOVERWRITE ); if (rc == 0) { ldap_pvt_thread_rdwr_wlock(&bdb->bi_tree_rdwr); n = bdb_add_node( e->e_id, data.data, bdb); ldap_pvt_thread_rdwr_wunlock(&bdb->bi_tree_rdwr); if (d->parent) { ldap_pvt_thread_rdwr_rlock(&bdb->bi_tree_rdwr); bdb_insert_kid(n, bdb->bi_tree); ldap_pvt_thread_rdwr_runlock(&bdb->bi_tree_rdwr); } } else { free(d); } return rc; }