示例#1
0
int ndb_modify_internal(
	Operation *op,
	NdbArgs *NA,
	const char **text,
	char *textbuf,
	size_t textlen )
{
	struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
	Modification	*mod;
	Modifications	*ml;
	Modifications	*modlist = op->orm_modlist;
	NdbAttrInfo **modai, *atmp;
	const NdbDictionary::Dictionary *myDict;
	const NdbDictionary::Table *myTable;
	int got_oc = 0, nmods = 0, nai = 0, i, j;
	int rc, indexed = 0;
	Attribute *old = NULL;

	Debug( LDAP_DEBUG_TRACE, "ndb_modify_internal: 0x%08lx: %s\n",
		NA->e->e_id, NA->e->e_dn, 0);

	if ( !acl_check_modlist( op, NA->e, modlist )) {
		return LDAP_INSUFFICIENT_ACCESS;
	}

	old = attrs_dup( NA->e->e_attrs );

	for ( ml = modlist; ml != NULL; ml = ml->sml_next ) {
		mod = &ml->sml_mod;
		nmods++;

		switch ( mod->sm_op ) {
		case LDAP_MOD_ADD:
			Debug(LDAP_DEBUG_ARGS,
				"ndb_modify_internal: add %s\n",
				mod->sm_desc->ad_cname.bv_val, 0, 0);
			rc = modify_add_values( NA->e, mod, get_permissiveModify(op),
				text, textbuf, textlen );
			if( rc != LDAP_SUCCESS ) {
				Debug(LDAP_DEBUG_ARGS, "ndb_modify_internal: %d %s\n",
					rc, *text, 0);
			}
			break;

		case LDAP_MOD_DELETE:
			Debug(LDAP_DEBUG_ARGS,
				"ndb_modify_internal: delete %s\n",
				mod->sm_desc->ad_cname.bv_val, 0, 0);
			rc = ndb_modify_delete( NA->e, mod, get_permissiveModify(op),
				text, textbuf, textlen, NULL );
			assert( rc != LDAP_TYPE_OR_VALUE_EXISTS );
			if( rc != LDAP_SUCCESS ) {
				Debug(LDAP_DEBUG_ARGS, "ndb_modify_internal: %d %s\n",
					rc, *text, 0);
			}
			break;

		case LDAP_MOD_REPLACE:
			Debug(LDAP_DEBUG_ARGS,
				"ndb_modify_internal: replace %s\n",
				mod->sm_desc->ad_cname.bv_val, 0, 0);
			rc = modify_replace_values( NA->e, mod, get_permissiveModify(op),
				text, textbuf, textlen );
			if( rc != LDAP_SUCCESS ) {
				Debug(LDAP_DEBUG_ARGS, "ndb_modify_internal: %d %s\n",
					rc, *text, 0);
			}
			break;

		case LDAP_MOD_INCREMENT:
			Debug(LDAP_DEBUG_ARGS,
				"ndb_modify_internal: increment %s\n",
				mod->sm_desc->ad_cname.bv_val, 0, 0);
			rc = modify_increment_values( NA->e, mod, get_permissiveModify(op),
				text, textbuf, textlen );
			if( rc != LDAP_SUCCESS ) {
				Debug(LDAP_DEBUG_ARGS,
					"ndb_modify_internal: %d %s\n",
					rc, *text, 0);
			}
			break;

		case SLAP_MOD_SOFTADD:
			Debug(LDAP_DEBUG_ARGS,
				"ndb_modify_internal: softadd %s\n",
				mod->sm_desc->ad_cname.bv_val, 0, 0);
 			mod->sm_op = LDAP_MOD_ADD;

			rc = modify_add_values( NA->e, mod, get_permissiveModify(op),
				text, textbuf, textlen );

 			mod->sm_op = SLAP_MOD_SOFTADD;

 			if ( rc == LDAP_TYPE_OR_VALUE_EXISTS ) {
 				rc = LDAP_SUCCESS;
 			}

			if( rc != LDAP_SUCCESS ) {
				Debug(LDAP_DEBUG_ARGS, "ndb_modify_internal: %d %s\n",
					rc, *text, 0);
			}
 			break;

		case SLAP_MOD_SOFTDEL:
			Debug(LDAP_DEBUG_ARGS,
				"ndb_modify_internal: softdel %s\n",
				mod->sm_desc->ad_cname.bv_val, 0, 0);
 			mod->sm_op = LDAP_MOD_DELETE;

			rc = modify_delete_values( NA->e, mod, get_permissiveModify(op),
				text, textbuf, textlen );

 			mod->sm_op = SLAP_MOD_SOFTDEL;

 			if ( rc == LDAP_NO_SUCH_ATTRIBUTE) {
 				rc = LDAP_SUCCESS;
 			}

			if( rc != LDAP_SUCCESS ) {
				Debug(LDAP_DEBUG_ARGS, "ndb_modify_internal: %d %s\n",
					rc, *text, 0);
			}
 			break;

		case SLAP_MOD_ADD_IF_NOT_PRESENT:
			Debug(LDAP_DEBUG_ARGS,
				"ndb_modify_internal: add_if_not_present %s\n",
				mod->sm_desc->ad_cname.bv_val, 0, 0);
			if ( attr_find( NA->e->e_attrs, mod->sm_desc ) ) {
				rc = LDAP_SUCCESS;
				break;
			}

 			mod->sm_op = LDAP_MOD_ADD;

			rc = modify_add_values( NA->e, mod, get_permissiveModify(op),
				text, textbuf, textlen );

 			mod->sm_op = SLAP_MOD_ADD_IF_NOT_PRESENT;

			if( rc != LDAP_SUCCESS ) {
				Debug(LDAP_DEBUG_ARGS, "ndb_modify_internal: %d %s\n",
					rc, *text, 0);
			}
 			break;

		default:
			Debug(LDAP_DEBUG_ANY, "ndb_modify_internal: invalid op %d\n",
				mod->sm_op, 0, 0);
			*text = "Invalid modify operation";
			rc = LDAP_OTHER;
			Debug(LDAP_DEBUG_ARGS, "ndb_modify_internal: %d %s\n",
				rc, *text, 0);
		}

		if ( rc != LDAP_SUCCESS ) {
			attrs_free( old );
			return rc; 
		}

		/* If objectClass was modified, reset the flags */
		if ( mod->sm_desc == slap_schema.si_ad_objectClass ) {
			NA->e->e_ocflags = 0;
			got_oc = 1;
		}
	}

	/* check that the entry still obeys the schema */
	rc = entry_schema_check( op, NA->e, NULL, get_relax(op), 0, NULL,
		text, textbuf, textlen );
	if ( rc != LDAP_SUCCESS || op->o_noop ) {
		if ( rc != LDAP_SUCCESS ) {
			Debug( LDAP_DEBUG_ANY,
				"entry failed schema check: %s\n",
				*text, 0, 0 );
		}
		attrs_free( old );
		return rc;
	}

	if ( got_oc ) {
		rc = ndb_entry_put_info( op->o_bd, NA, 1 );
		if ( rc ) {
			attrs_free( old );
			return rc;
		}
	}

	/* apply modifications to DB */
	modai = (NdbAttrInfo **)op->o_tmpalloc( nmods * sizeof(NdbAttrInfo*), op->o_tmpmemctx );

	/* Get the unique list of modified attributes */
	ldap_pvt_thread_rdwr_rlock( &ni->ni_ai_rwlock );
	for ( ml = modlist; ml != NULL; ml = ml->sml_next ) {
		/* Already took care of objectclass */
		if ( ml->sml_desc == slap_schema.si_ad_objectClass )
			continue;
		for ( i=0; i<nai; i++ ) {
			if ( ml->sml_desc->ad_type == modai[i]->na_attr )
				break;
		}
		/* This attr was already updated */
		if ( i < nai )
			continue;
		modai[nai] = ndb_ai_find( ni, ml->sml_desc->ad_type );
		if ( modai[nai]->na_flag & NDB_INFO_INDEX )
			indexed++;
		nai++;
	}
	ldap_pvt_thread_rdwr_runlock( &ni->ni_ai_rwlock );

	/* If got_oc, this was already done above */
	if ( indexed && !got_oc) {
		rc = ndb_entry_put_info( op->o_bd, NA, 1 );
		if ( rc ) {
			attrs_free( old );
			return rc;
		}
	}

	myDict = NA->ndb->getDictionary();

	/* sort modai so that OcInfo's are contiguous */
	{
		int j, k;
		for ( i=0; i<nai; i++ ) {
			for ( j=i+1; j<nai; j++ ) {
				if ( modai[i]->na_oi == modai[j]->na_oi )
					continue;
				for ( k=j+1; k<nai; k++ ) {
					if ( modai[i]->na_oi == modai[k]->na_oi ) {
						atmp = modai[j];
						modai[j] = modai[k];
						modai[k] = atmp;
						break;
					}
				}
				/* there are no more na_oi's that match modai[i] */
				if ( k == nai ) {
					i = j;
				}
			}
		}
	}

	/* One call per table... */
	for ( i=0; i<nai; i += j ) {
		atmp = modai[i];
		for ( j=i+1; j<nai; j++ )
			if ( atmp->na_oi != modai[j]->na_oi )
				break;
		j -= i;
		myTable = myDict->getTable( atmp->na_oi->no_table.bv_val );
		if ( !myTable )
			continue;
		rc = ndb_oc_attrs( NA->txn, myTable, NA->e, atmp->na_oi, &modai[i], j, old );
		if ( rc ) break;
	}
	attrs_free( old );
	return rc;
}
示例#2
0
ID ndb_tool_entry_put(
	BackendDB *be,
	Entry *e,
	struct berval *text )
{
	struct ndb_info *ni = (struct ndb_info *) be->be_private;
	struct dn_id dtmp, *dptr;
	NdbArgs NA;
	NdbRdns rdns;
	int rc, slow = 0;
	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? */

	Debug( LDAP_DEBUG_TRACE, "=> " LDAP_XSTRING(ndb_tool_entry_put)
		"( %ld, \"%s\" )\n", (long) e->e_id, e->e_dn, 0 );

	if ( !be_issuffix( be, &e->e_nname )) {
		dnParent( &e->e_nname, &dtmp.dn );
		dptr = (struct dn_id *)avl_find( myParents, &dtmp, ndb_dnid_cmp );
		if ( !dptr )
			slow = 1;
	}

	rdns.nr_num = 0;

	op.o_hdr = &ohdr;
	op.o_bd = be;
	op.o_tmpmemctx = NULL;
	op.o_tmpmfuncs = &ch_mfuncs;

	if ( !slow ) {
		rc = ndb_next_id( be, myNdb, &e->e_id );
		if ( rc ) {
			snprintf( text->bv_val, text->bv_len,
				"next_id failed: %s (%d)",
				myNdb->getNdbError().message, myNdb->getNdbError().code );
			Debug( LDAP_DEBUG_ANY,
				"=> ndb_tool_next_id: %s\n", text->bv_val, 0, 0 );
			return rc;
		}
	}

	if ( !myPutTxn )
		myPutTxn = myNdb->startTransaction();
	if ( !myPutTxn ) {
		snprintf( text->bv_val, text->bv_len,
			"start_transaction failed: %s (%d)",
			myNdb->getNdbError().message, myNdb->getNdbError().code );
		Debug( LDAP_DEBUG_ANY,
			"=> " LDAP_XSTRING(ndb_tool_entry_put) ": %s\n",
			 text->bv_val, 0, 0 );
		return NOID;
	}

	/* add dn2id indices */
	ndb_dn2rdns( &e->e_name, &rdns );
	NA.rdns = &rdns;
	NA.e = e;
	NA.ndb = myNdb;
	NA.txn = myPutTxn;
	if ( slow ) {
		rc = ndb_tool_next_id( &op, &NA, text, 0 );
		if( rc != 0 ) {
			goto done;
		}
	} else {
		rc = ndb_entry_put_info( be, &NA, 0 );
		if ( rc != 0 ) {
			goto done;
		}
	}

	/* id2entry index */
	rc = ndb_entry_put_data( be, &NA );
	if( rc != 0 ) {
		snprintf( text->bv_val, text->bv_len,
				"ndb_entry_put_data failed: %s (%d)",
				myNdb->getNdbError().message, myNdb->getNdbError().code );
		Debug( LDAP_DEBUG_ANY,
			"=> " LDAP_XSTRING(ndb_tool_entry_put) ": %s\n",
			text->bv_val, 0, 0 );
		goto done;
	}

done:
	if( rc == 0 ) {
		myPutCnt++;
		if ( !( myPutCnt & 0x0f )) {
			rc = myPutTxn->execute(NdbTransaction::Commit);
			if( rc != 0 ) {
				snprintf( text->bv_val, text->bv_len,
					"txn_commit failed: %s (%d)",
					myPutTxn->getNdbError().message, myPutTxn->getNdbError().code );
				Debug( LDAP_DEBUG_ANY,
					"=> " LDAP_XSTRING(ndb_tool_entry_put) ": %s\n",
					text->bv_val, 0, 0 );
				e->e_id = NOID;
			}
			myPutTxn->close();
			myPutTxn = NULL;
		}
	} else {
		snprintf( text->bv_val, text->bv_len,
			"txn_aborted! %s (%d)",
			myPutTxn->getNdbError().message, myPutTxn->getNdbError().code );
		Debug( LDAP_DEBUG_ANY,
			"=> " LDAP_XSTRING(ndb_tool_entry_put) ": %s\n",
			text->bv_val, 0, 0 );
		e->e_id = NOID;
		myPutTxn->close();
	}

	return e->e_id;
}
示例#3
0
文件: modrdn.cpp 项目: dago/openldap
int
ndb_back_modrdn( Operation *op, SlapReply *rs )
{
	struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
	AttributeDescription *children = slap_schema.si_ad_children;
	AttributeDescription *entry = slap_schema.si_ad_entry;
	struct berval	new_dn = BER_BVNULL, new_ndn = BER_BVNULL;
	Entry		e = {0};
	Entry		e2 = {0};
	char textbuf[SLAP_TEXT_BUFLEN];
	size_t textlen = sizeof textbuf;

	struct berval	*np_dn = NULL;			/* newSuperior dn */
	struct berval	*np_ndn = NULL;			/* newSuperior ndn */

	int		manageDSAit = get_manageDSAit( op );
	int		num_retries = 0;

	NdbArgs NA, NA2;
	NdbRdns rdns, rdn2;
	struct berval matched;

	LDAPControl **preread_ctrl = NULL;
	LDAPControl **postread_ctrl = NULL;
	LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
	int num_ctrls = 0;

	int	rc;

	Debug( LDAP_DEBUG_ARGS, "==>" LDAP_XSTRING(ndb_back_modrdn) "(%s,%s,%s)\n",
		op->o_req_dn.bv_val,op->oq_modrdn.rs_newrdn.bv_val,
		op->oq_modrdn.rs_newSup ? op->oq_modrdn.rs_newSup->bv_val : "NULL" );

	ctrls[num_ctrls] = NULL;

	slap_mods_opattrs( op, &op->orr_modlist, 1 );

	e.e_name = op->o_req_dn;
	e.e_nname = op->o_req_ndn;

	/* Get our NDB handle */
	rs->sr_err = ndb_thread_handle( op, &NA.ndb );
	rdns.nr_num = 0;
	NA.rdns = &rdns;
	NA.e = &e;
	NA2.ndb = NA.ndb;
	NA2.e = &e2;
	NA2.rdns = &rdn2;

	if( 0 ) {
retry:	/* transaction retry */
		NA.txn->close();
		NA.txn = NULL;
		if ( e.e_attrs ) {
			attrs_free( e.e_attrs );
			e.e_attrs = NULL;
		}
		Debug( LDAP_DEBUG_TRACE, "==>" LDAP_XSTRING(ndb_back_modrdn)
				": retrying...\n", 0, 0, 0 );
		if ( op->o_abandon ) {
			rs->sr_err = SLAPD_ABANDON;
			goto return_results;
		}
		if ( NA2.ocs ) {
			ber_bvarray_free_x( NA2.ocs, op->o_tmpmemctx );
		}
		if ( NA.ocs ) {
			ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx );
		}
		ndb_trans_backoff( ++num_retries );
	}
	NA.ocs = NULL;
	NA2.ocs = NULL;

	/* begin transaction */
	NA.txn = NA.ndb->startTransaction();
	rs->sr_text = NULL;
	if( !NA.txn ) {
		Debug( LDAP_DEBUG_TRACE,
			LDAP_XSTRING(ndb_back_modrdn) ": startTransaction failed: %s (%d)\n",
			NA.ndb->getNdbError().message, NA.ndb->getNdbError().code, 0 );
		rs->sr_err = LDAP_OTHER;
		rs->sr_text = "internal error";
		goto return_results;
	}
	NA2.txn = NA.txn;

	/* get entry */
	rs->sr_err = ndb_entry_get_info( op, &NA, 1, &matched );
	switch( rs->sr_err ) {
	case 0:
		break;
	case LDAP_NO_SUCH_OBJECT:
		Debug( LDAP_DEBUG_ARGS,
			"<=- ndb_back_modrdn: no such object %s\n",
			op->o_req_dn.bv_val, 0, 0 );
		rs->sr_matched = matched.bv_val;
		if ( NA.ocs )
			ndb_check_referral( op, rs, &NA );
		goto return_results;
#if 0
	case DB_LOCK_DEADLOCK:
	case DB_LOCK_NOTGRANTED:
		goto retry;
#endif
	case LDAP_BUSY:
		rs->sr_text = "ldap server busy";
		goto return_results;
	default:
		rs->sr_err = LDAP_OTHER;
		rs->sr_text = "internal error";
		goto return_results;
	}

	/* acquire and lock entry */
	rs->sr_err = ndb_entry_get_data( op, &NA, 1 );
	if ( rs->sr_err )
		goto return_results;

	if ( !manageDSAit && is_entry_glue( &e )) {
		rs->sr_err = LDAP_NO_SUCH_OBJECT;
		goto return_results;
	}
	
	if ( get_assert( op ) &&
		( test_filter( op, &e, (Filter *)get_assertion( op )) != LDAP_COMPARE_TRUE ))
	{
		rs->sr_err = LDAP_ASSERTION_FAILED;
		goto return_results;
	}

	/* check write on old entry */
	rs->sr_err = access_allowed( op, &e, entry, NULL, ACL_WRITE, NULL );
	if ( ! rs->sr_err ) {
		Debug( LDAP_DEBUG_TRACE, "no access to entry\n", 0,
			0, 0 );
		rs->sr_text = "no write access to old entry";
		rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
		goto return_results;
	}

	/* Can't do it if we have kids */
	rs->sr_err = ndb_has_children( &NA, &rc );
	if ( rs->sr_err ) {
		Debug(LDAP_DEBUG_ARGS,
			"<=- " LDAP_XSTRING(ndb_back_modrdn)
			": has_children failed: %s (%d)\n",
			NA.txn->getNdbError().message, NA.txn->getNdbError().code, 0 );
		rs->sr_err = LDAP_OTHER;
		rs->sr_text = "internal error";
		goto return_results;
	}
	if ( rc == LDAP_COMPARE_TRUE ) {
		Debug(LDAP_DEBUG_ARGS,
			"<=- " LDAP_XSTRING(ndb_back_modrdn)
			": non-leaf %s\n",
			op->o_req_dn.bv_val, 0, 0);
		rs->sr_err = LDAP_NOT_ALLOWED_ON_NONLEAF;
		rs->sr_text = "subtree rename not supported";
		goto return_results;
	}

	if (!manageDSAit && is_entry_referral( &e ) ) {
		/* entry is a referral, don't allow modrdn */
		rs->sr_ref = get_entry_referrals( op, &e );

		Debug( LDAP_DEBUG_TRACE, LDAP_XSTRING(ndb_back_modrdn)
			": entry %s is referral\n", e.e_dn, 0, 0 );

		rs->sr_err = LDAP_REFERRAL,
		rs->sr_matched = op->o_req_dn.bv_val;
		rs->sr_flags = REP_REF_MUSTBEFREED;
		goto return_results;
	}

	if ( be_issuffix( op->o_bd, &e.e_nname ) ) {
		/* There can only be one suffix entry */
		rs->sr_err = LDAP_NAMING_VIOLATION;
		rs->sr_text = "cannot rename suffix entry";
		goto return_results;
	} else {
		dnParent( &e.e_nname, &e2.e_nname );
		dnParent( &e.e_name, &e2.e_name );
	}

	/* check parent for "children" acl */
	rs->sr_err = access_allowed( op, &e2,
		children, NULL,
		op->oq_modrdn.rs_newSup == NULL ?
			ACL_WRITE : ACL_WDEL,
		NULL );

	if ( ! rs->sr_err ) {
		rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
		Debug( LDAP_DEBUG_TRACE, "no access to parent\n", 0,
			0, 0 );
		rs->sr_text = "no write access to old parent's children";
		goto return_results;
	}

	Debug( LDAP_DEBUG_TRACE,
		LDAP_XSTRING(ndb_back_modrdn) ": wr to children "
		"of entry %s OK\n", e2.e_name.bv_val, 0, 0 );
	
	if ( op->oq_modrdn.rs_newSup != NULL ) {
		Debug( LDAP_DEBUG_TRACE, 
			LDAP_XSTRING(ndb_back_modrdn)
			": new parent \"%s\" requested...\n",
			op->oq_modrdn.rs_newSup->bv_val, 0, 0 );

		/*  newSuperior == oldParent? */
		if( dn_match( &e2.e_nname, op->oq_modrdn.rs_nnewSup ) ) {
			Debug( LDAP_DEBUG_TRACE, "bdb_back_modrdn: "
				"new parent \"%s\" same as the old parent \"%s\"\n",
				op->oq_modrdn.rs_newSup->bv_val, e2.e_name.bv_val, 0 );
			op->oq_modrdn.rs_newSup = NULL; /* ignore newSuperior */
		}
	}

	if ( op->oq_modrdn.rs_newSup != NULL ) {
		if ( op->oq_modrdn.rs_newSup->bv_len ) {
			rdn2.nr_num = 0;
			np_dn = op->oq_modrdn.rs_newSup;
			np_ndn = op->oq_modrdn.rs_nnewSup;

			/* newSuperior == oldParent? - checked above */
			/* newSuperior == entry being moved?, if so ==> ERROR */
			if ( dnIsSuffix( np_ndn, &e.e_nname )) {
				rs->sr_err = LDAP_NO_SUCH_OBJECT;
				rs->sr_text = "new superior not found";
				goto return_results;
			}
			/* Get Entry with dn=newSuperior. Does newSuperior exist? */

			e2.e_name = *np_dn;
			e2.e_nname = *np_ndn;
			rs->sr_err = ndb_entry_get_info( op, &NA2, 1, NULL );
			switch( rs->sr_err ) {
			case 0:
				break;
			case LDAP_NO_SUCH_OBJECT:
				Debug( LDAP_DEBUG_TRACE,
					LDAP_XSTRING(ndb_back_modrdn)
					": newSup(ndn=%s) not here!\n",
					np_ndn->bv_val, 0, 0);
				rs->sr_text = "new superior not found";
				goto return_results;
#if 0
			case DB_LOCK_DEADLOCK:
			case DB_LOCK_NOTGRANTED:
				goto retry;
#endif
			case LDAP_BUSY:
				rs->sr_text = "ldap server busy";
				goto return_results;
			default:
				rs->sr_err = LDAP_OTHER;
				rs->sr_text = "internal error";
				goto return_results;
			}
			if ( NA2.ocs ) {
				Attribute a;
				int i;

				for ( i=0; !BER_BVISNULL( &NA2.ocs[i] ); i++);
				a.a_numvals = i;
				a.a_desc = slap_schema.si_ad_objectClass;
				a.a_vals = NA2.ocs;
				a.a_nvals = NA2.ocs;
				a.a_next = NULL;
				e2.e_attrs = &a;

				if ( is_entry_alias( &e2 )) {
					/* parent is an alias, don't allow move */
					Debug( LDAP_DEBUG_TRACE,
						LDAP_XSTRING(ndb_back_modrdn)
						": entry is alias\n",
						0, 0, 0 );
					rs->sr_text = "new superior is an alias";
					rs->sr_err = LDAP_ALIAS_PROBLEM;
					goto return_results;
				}

				if ( is_entry_referral( &e2 ) ) {
					/* parent is a referral, don't allow move */
					Debug( LDAP_DEBUG_TRACE,
						LDAP_XSTRING(ndb_back_modrdn)
						": entry is referral\n",
						0, 0, 0 );
					rs->sr_text = "new superior is a referral";
					rs->sr_err = LDAP_OTHER;
					goto return_results;
				}
			}
		}

		/* check newSuperior for "children" acl */
		rs->sr_err = access_allowed( op, &e2, children,
			NULL, ACL_WADD, NULL );
		if( ! rs->sr_err ) {
			Debug( LDAP_DEBUG_TRACE,
				LDAP_XSTRING(ndb_back_modrdn)
				": no wr to newSup children\n",
				0, 0, 0 );
			rs->sr_text = "no write access to new superior's children";
			rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
			goto return_results;
		}

		Debug( LDAP_DEBUG_TRACE,
			LDAP_XSTRING(ndb_back_modrdn)
			": wr to new parent OK id=%ld\n",
			(long) e2.e_id, 0, 0 );
	}

	/* Build target dn and make sure target entry doesn't exist already. */
	if (!new_dn.bv_val) {
		build_new_dn( &new_dn, &e2.e_name, &op->oq_modrdn.rs_newrdn, NULL ); 
	}

	if (!new_ndn.bv_val) {
		build_new_dn( &new_ndn, &e2.e_nname, &op->oq_modrdn.rs_nnewrdn, NULL ); 
	}

	Debug( LDAP_DEBUG_TRACE, LDAP_XSTRING(ndb_back_modrdn) ": new ndn=%s\n",
		new_ndn.bv_val, 0, 0 );

	/* Allow rename to same DN */
	if ( !bvmatch ( &new_ndn, &e.e_nname )) {
		rdn2.nr_num = 0;
		e2.e_name = new_dn;
		e2.e_nname = new_ndn;
		NA2.ocs = &matched;
		rs->sr_err = ndb_entry_get_info( op, &NA2, 1, NULL );
		NA2.ocs = NULL;
		switch( rs->sr_err ) {
#if 0
		case DB_LOCK_DEADLOCK:
		case DB_LOCK_NOTGRANTED:
			goto retry;
#endif
		case LDAP_NO_SUCH_OBJECT:
			break;
		case 0:
			rs->sr_err = LDAP_ALREADY_EXISTS;
			goto return_results;
		default:
			rs->sr_err = LDAP_OTHER;
			rs->sr_text = "internal error";
			goto return_results;
		}
	}

	assert( op->orr_modlist != NULL );

	if( op->o_preread ) {
		if( preread_ctrl == NULL ) {
			preread_ctrl = &ctrls[num_ctrls++];
			ctrls[num_ctrls] = NULL;
		}
		if( slap_read_controls( op, rs, &e,
			&slap_pre_read_bv, preread_ctrl ) )
		{
			Debug( LDAP_DEBUG_TRACE,        
				"<=- " LDAP_XSTRING(ndb_back_modrdn)
				": pre-read failed!\n", 0, 0, 0 );
			if ( op->o_preread & SLAP_CONTROL_CRITICAL ) {
				/* FIXME: is it correct to abort
				 * operation if control fails? */
				goto return_results;
			}
		}                   
	}

	/* delete old DN */
	rs->sr_err = ndb_entry_del_info( op->o_bd, &NA );
	if ( rs->sr_err != 0 ) {
		Debug(LDAP_DEBUG_TRACE,
			"<=- " LDAP_XSTRING(ndb_back_modrdn)
			": dn2id del failed: %s (%d)\n",
			NA.txn->getNdbError().message, NA.txn->getNdbError().code, 0 );
#if 0
		switch( rs->sr_err ) {
		case DB_LOCK_DEADLOCK:
		case DB_LOCK_NOTGRANTED:
			goto retry;
		}
#endif
		rs->sr_err = LDAP_OTHER;
		rs->sr_text = "DN index delete fail";
		goto return_results;
	}

	/* copy entry fields */
	e2.e_attrs = e.e_attrs;
	e2.e_id = e.e_id;

	/* add new DN */
	rs->sr_err = ndb_entry_put_info( op->o_bd, &NA2, 0 );
	if ( rs->sr_err != 0 ) {
		Debug(LDAP_DEBUG_TRACE,
			"<=- " LDAP_XSTRING(ndb_back_modrdn)
			": dn2id add failed: %s (%d)\n",
			NA.txn->getNdbError().message, NA.txn->getNdbError().code, 0 );
#if 0
		switch( rs->sr_err ) {
		case DB_LOCK_DEADLOCK:
		case DB_LOCK_NOTGRANTED:
			goto retry;
		}
#endif
		rs->sr_err = LDAP_OTHER;
		rs->sr_text = "DN index add failed";
		goto return_results;
	}

	/* modify entry */
	rs->sr_err = ndb_modify_internal( op, &NA2,
		&rs->sr_text, textbuf, textlen );
	if( rs->sr_err != LDAP_SUCCESS ) {
		Debug(LDAP_DEBUG_TRACE,
			"<=- " LDAP_XSTRING(ndb_back_modrdn)
			": modify failed: %s (%d)\n",
			NA.txn->getNdbError().message, NA.txn->getNdbError().code, 0 );
#if 0
		switch( rs->sr_err ) {
		case DB_LOCK_DEADLOCK:
		case DB_LOCK_NOTGRANTED:
			goto retry;
		}
#endif
		goto return_results;
	}

	e.e_attrs = e2.e_attrs;

	if( op->o_postread ) {
		if( postread_ctrl == NULL ) {
			postread_ctrl = &ctrls[num_ctrls++];
			ctrls[num_ctrls] = NULL;
		}
		if( slap_read_controls( op, rs, &e2,
			&slap_post_read_bv, postread_ctrl ) )
		{
			Debug( LDAP_DEBUG_TRACE,        
				"<=- " LDAP_XSTRING(ndb_back_modrdn)
				": post-read failed!\n", 0, 0, 0 );
			if ( op->o_postread & SLAP_CONTROL_CRITICAL ) {
				/* FIXME: is it correct to abort
				 * operation if control fails? */
				goto return_results;
			}
		}                   
	}

	if( op->o_noop ) {
		if (( rs->sr_err=NA.txn->execute( NdbTransaction::Rollback,
			NdbOperation::AbortOnError, 1 )) != 0 ) {
			rs->sr_text = "txn_abort (no-op) failed";
		} else {
			rs->sr_err = LDAP_X_NO_OPERATION;
		}
	} else {
		if (( rs->sr_err=NA.txn->execute( NdbTransaction::Commit,
			NdbOperation::AbortOnError, 1 )) != 0 ) {
			rs->sr_text = "txn_commit failed";
		} else {
			rs->sr_err = LDAP_SUCCESS;
		}
	}
 
	if( rs->sr_err != LDAP_SUCCESS && rs->sr_err != LDAP_X_NO_OPERATION ) {
		Debug( LDAP_DEBUG_TRACE,
			LDAP_XSTRING(ndb_back_modrdn) ": txn_%s failed: %s (%d)\n",
			op->o_noop ? "abort (no-op)" : "commit",
			NA.txn->getNdbError().message, NA.txn->getNdbError().code );
		rs->sr_err = LDAP_OTHER;
		goto return_results;
	}
	NA.txn->close();
	NA.txn = NULL;

	Debug(LDAP_DEBUG_TRACE,
		LDAP_XSTRING(ndb_back_modrdn)
		": rdn modified%s id=%08lx dn=\"%s\"\n",
		op->o_noop ? " (no-op)" : "",
		e.e_id, op->o_req_dn.bv_val );

	rs->sr_err = LDAP_SUCCESS;
	rs->sr_text = NULL;
	if( num_ctrls ) rs->sr_ctrls = ctrls;

return_results:
	if ( NA2.ocs ) {
		ber_bvarray_free_x( NA2.ocs, op->o_tmpmemctx );
		NA2.ocs = NULL;
	}

	if ( NA.ocs ) {
		ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx );
		NA.ocs = NULL;
	}

	if ( e.e_attrs ) {
		attrs_free( e.e_attrs );
		e.e_attrs = NULL;
	}

	if( NA.txn != NULL ) {
		NA.txn->execute( Rollback );
		NA.txn->close();
	}

	send_ldap_result( op, rs );
	slap_graduate_commit_csn( op );

	if( new_dn.bv_val != NULL ) free( new_dn.bv_val );
	if( new_ndn.bv_val != NULL ) free( new_ndn.bv_val );

	if( preread_ctrl != NULL && (*preread_ctrl) != NULL ) {
		slap_sl_free( (*preread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
		slap_sl_free( *preread_ctrl, op->o_tmpmemctx );
	}
	if( postread_ctrl != NULL && (*postread_ctrl) != NULL ) {
		slap_sl_free( (*postread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
		slap_sl_free( *postread_ctrl, op->o_tmpmemctx );
	}

	rs->sr_text = NULL;
	return rs->sr_err;
}
示例#4
0
static int ndb_tool_next_id(
	Operation *op,
	NdbArgs *NA,
	struct berval *text,
	int hole )
{
	struct berval ndn = NA->e->e_nname;
	int rc;

	if (ndn.bv_len == 0) {
		NA->e->e_id = 0;
		return 0;
	}

	rc = ndb_entry_get_info( op, NA, 0, NULL );
	if ( rc ) {
		Attribute *a, tmp = {0};
		if ( !be_issuffix( op->o_bd, &ndn ) ) {
			struct dn_id *dptr;
			struct berval npdn;
			dnParent( &ndn, &npdn );
			NA->e->e_nname = npdn;
			NA->rdns->nr_num--;
			rc = ndb_tool_next_id( op, NA, text, 1 );
			NA->e->e_nname = ndn;
			NA->rdns->nr_num++;
			if ( rc ) {
				return rc;
			}
			/* If parent didn't exist, it was created just now
			 * and its ID is now in e->e_id.
			 */
			dptr = (struct dn_id *)ch_malloc( sizeof( struct dn_id ) + npdn.bv_len + 1);
			dptr->id = NA->e->e_id;
			dptr->dn.bv_val = (char *)(dptr+1);
			strcpy(dptr->dn.bv_val, npdn.bv_val );
			dptr->dn.bv_len = npdn.bv_len;
			if ( avl_insert( &myParents, dptr, ndb_dnid_cmp, avl_dup_error )) {
				ch_free( dptr );
			}
		}
		rc = ndb_next_id( op->o_bd, myNdb, &NA->e->e_id );
		if ( rc ) {
			snprintf( text->bv_val, text->bv_len,
				"next_id failed: %s (%d)",
				myNdb->getNdbError().message, myNdb->getNdbError().code );
			Debug( LDAP_DEBUG_ANY,
				"=> ndb_tool_next_id: %s\n", text->bv_val, 0, 0 );
			return rc;
		}
		if ( hole ) {
			a = NA->e->e_attrs;
			NA->e->e_attrs = &tmp;
			tmp.a_desc = slap_schema.si_ad_objectClass;
			tmp.a_vals = glueval;
			tmp.a_nvals = tmp.a_vals;
			tmp.a_numvals = 1;
		}
		rc = ndb_entry_put_info( op->o_bd, NA, 0 );
		if ( hole ) {
			NA->e->e_attrs = a;
		}
		if ( rc ) {
			snprintf( text->bv_val, text->bv_len,
				"ndb_entry_put_info failed: %s (%d)",
				myNdb->getNdbError().message, myNdb->getNdbError().code );
		Debug( LDAP_DEBUG_ANY,
			"=> ndb_tool_next_id: %s\n", text->bv_val, 0, 0 );
		} else if ( hole ) {
			if ( nholes == nhmax - 1 ) {
				if ( holes == hbuf ) {
					holes = (dn_id *)ch_malloc( nhmax * sizeof(dn_id) * 2 );
					AC_MEMCPY( holes, hbuf, sizeof(hbuf) );
				} else {
					holes = (dn_id *)ch_realloc( holes, nhmax * sizeof(dn_id) * 2 );
				}
				nhmax *= 2;
			}
			ber_dupbv( &holes[nholes].dn, &ndn );
			holes[nholes++].id = NA->e->e_id;
		}
	} else if ( !hole ) {
		unsigned i;

		for ( i=0; i<nholes; i++) {
			if ( holes[i].id == NA->e->e_id ) {
				int j;
				free(holes[i].dn.bv_val);
				for (j=i;j<nholes;j++) holes[j] = holes[j+1];
				holes[j].id = 0;
				nholes--;
				rc = ndb_entry_put_info( op->o_bd, NA, 1 );
				break;
			} else if ( holes[i].id > NA->e->e_id ) {
				break;
			}
		}
	}
	return rc;
}
示例#5
0
extern "C" int
ndb_back_add(Operation *op, SlapReply *rs )
{
	struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
	Entry		p = {0};
	Attribute	poc;
	char textbuf[SLAP_TEXT_BUFLEN];
	size_t textlen = sizeof textbuf;
	AttributeDescription *children = slap_schema.si_ad_children;
	AttributeDescription *entry = slap_schema.si_ad_entry;
	NdbArgs NA;
	NdbRdns rdns;
	struct berval matched;
	struct berval pdn, pndn;

	int		num_retries = 0;
	int		success;

	LDAPControl **postread_ctrl = NULL;
	LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
	int num_ctrls = 0;

	Debug(LDAP_DEBUG_ARGS, "==> " LDAP_XSTRING(ndb_back_add) ": %s\n",
		op->oq_add.rs_e->e_name.bv_val, 0, 0);

	ctrls[num_ctrls] = 0;

	/* check entry's schema */
	rs->sr_err = entry_schema_check( op, op->oq_add.rs_e, NULL,
		get_relax(op), 1, NULL, &rs->sr_text, textbuf, textlen );
	if ( rs->sr_err != LDAP_SUCCESS ) {
		Debug( LDAP_DEBUG_TRACE,
			LDAP_XSTRING(ndb_back_add) ": entry failed schema check: "
			"%s (%d)\n", rs->sr_text, rs->sr_err, 0 );
		goto return_results;
	}

	/* add opattrs to shadow as well, only missing attrs will actually
	 * be added; helps compatibility with older OL versions */
	rs->sr_err = slap_add_opattrs( op, &rs->sr_text, textbuf, textlen, 1 );
	if ( rs->sr_err != LDAP_SUCCESS ) {
		Debug( LDAP_DEBUG_TRACE,
			LDAP_XSTRING(ndb_back_add) ": entry failed op attrs add: "
			"%s (%d)\n", rs->sr_text, rs->sr_err, 0 );
		goto return_results;
	}

	/* Get our NDB handle */
	rs->sr_err = ndb_thread_handle( op, &NA.ndb );

	/*
	 * Get the parent dn and see if the corresponding entry exists.
	 */
	if ( be_issuffix( op->o_bd, &op->oq_add.rs_e->e_nname ) ) {
		pdn = slap_empty_bv;
		pndn = slap_empty_bv;
	} else {
		dnParent( &op->ora_e->e_name, &pdn );
		dnParent( &op->ora_e->e_nname, &pndn );
	}
	p.e_name = op->ora_e->e_name;
	p.e_nname = op->ora_e->e_nname;

	op->ora_e->e_id = NOID;
	rdns.nr_num = 0;
	NA.rdns = &rdns;

	if( 0 ) {
retry:	/* transaction retry */
		NA.txn->close();
		NA.txn = NULL;
		if ( op->o_abandon ) {
			rs->sr_err = SLAPD_ABANDON;
			goto return_results;
		}
		ndb_trans_backoff( ++num_retries );
	}

	NA.txn = NA.ndb->startTransaction();
	rs->sr_text = NULL;
	if( !NA.txn ) {
		Debug( LDAP_DEBUG_TRACE,
			LDAP_XSTRING(ndb_back_add) ": startTransaction failed: %s (%d)\n",
			NA.ndb->getNdbError().message, NA.ndb->getNdbError().code, 0 );
		rs->sr_err = LDAP_OTHER;
		rs->sr_text = "internal error";
		goto return_results;
	}

	/* get entry or parent */
	NA.e = &p;
	NA.ocs = NULL;
	rs->sr_err = ndb_entry_get_info( op, &NA, 0, &matched );
	switch( rs->sr_err ) {
	case 0:
		rs->sr_err = LDAP_ALREADY_EXISTS;
		goto return_results;
	case LDAP_NO_SUCH_OBJECT:
		break;
#if 0
	case DB_LOCK_DEADLOCK:
	case DB_LOCK_NOTGRANTED:
		goto retry;
#endif
	case LDAP_BUSY:
		rs->sr_text = "ldap server busy";
		goto return_results;
	default:
		rs->sr_err = LDAP_OTHER;
		rs->sr_text = "internal error";
		goto return_results;
	}

	if ( NA.ocs ) {
		int i;
		for ( i=0; !BER_BVISNULL( &NA.ocs[i] ); i++ );
		poc.a_numvals = i;
		poc.a_desc = slap_schema.si_ad_objectClass;
		poc.a_vals = NA.ocs;
		poc.a_nvals = poc.a_vals;
		poc.a_next = NULL;
		p.e_attrs = &poc;
	}

	if ( ber_bvstrcasecmp( &pndn, &matched ) ) {
		rs->sr_matched = matched.bv_val;
		Debug( LDAP_DEBUG_TRACE,
			LDAP_XSTRING(ndb_back_add) ": parent "
			"does not exist\n", 0, 0, 0 );

		rs->sr_text = "parent does not exist";
		rs->sr_err = LDAP_NO_SUCH_OBJECT;
		if ( p.e_attrs && is_entry_referral( &p )) {
is_ref:			p.e_attrs = NULL;
			ndb_entry_get_data( op, &NA, 0 );
			rs->sr_ref = get_entry_referrals( op, &p );
			rs->sr_err = LDAP_REFERRAL;
			rs->sr_flags = REP_REF_MUSTBEFREED;
			attrs_free( p.e_attrs );
			p.e_attrs = NULL;
		}
		goto return_results;
	}

	p.e_name = pdn;
	p.e_nname = pndn;
	rs->sr_err = access_allowed( op, &p,
		children, NULL, ACL_WADD, NULL );

	if ( ! rs->sr_err ) {
		Debug( LDAP_DEBUG_TRACE,
			LDAP_XSTRING(ndb_back_add) ": no write access to parent\n",
			0, 0, 0 );
		rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
		rs->sr_text = "no write access to parent";
		goto return_results;
	}

	if ( NA.ocs ) {
		if ( is_entry_subentry( &p )) {
			/* parent is a subentry, don't allow add */
			Debug( LDAP_DEBUG_TRACE,
				LDAP_XSTRING(ndb_back_add) ": parent is subentry\n",
				0, 0, 0 );
			rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
			rs->sr_text = "parent is a subentry";
			goto return_results;
		}

		if ( is_entry_alias( &p ) ) {
			/* parent is an alias, don't allow add */
			Debug( LDAP_DEBUG_TRACE,
				LDAP_XSTRING(ndb_back_add) ": parent is alias\n",
				0, 0, 0 );
			rs->sr_err = LDAP_ALIAS_PROBLEM;
			rs->sr_text = "parent is an alias";
			goto return_results;
		}

		if ( is_entry_referral( &p ) ) {
			/* parent is a referral, don't allow add */
			rs->sr_matched = p.e_name.bv_val;
			goto is_ref;
		}
	}

	rs->sr_err = access_allowed( op, op->ora_e,
		entry, NULL, ACL_WADD, NULL );

	if ( ! rs->sr_err ) {
		Debug( LDAP_DEBUG_TRACE,
			LDAP_XSTRING(ndb_back_add) ": no write access to entry\n",
			0, 0, 0 );
		rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
		rs->sr_text = "no write access to entry";
		goto return_results;;
	}

	/* 
	 * Check ACL for attribute write access
	 */
	if (!acl_check_modlist(op, op->ora_e, op->ora_modlist)) {
		Debug( LDAP_DEBUG_TRACE,
			LDAP_XSTRING(bdb_add) ": no write access to attribute\n",
			0, 0, 0 );
		rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
		rs->sr_text = "no write access to attribute";
		goto return_results;;
	}


	/* acquire entry ID */
	if ( op->ora_e->e_id == NOID ) {
		rs->sr_err = ndb_next_id( op->o_bd, NA.ndb, &op->ora_e->e_id );
		if( rs->sr_err != 0 ) {
			Debug( LDAP_DEBUG_TRACE,
				LDAP_XSTRING(ndb_back_add) ": next_id failed (%d)\n",
				rs->sr_err, 0, 0 );
			rs->sr_err = LDAP_OTHER;
			rs->sr_text = "internal error";
			goto return_results;
		}
	}

	if ( matched.bv_val )
		rdns.nr_num++;
	NA.e = op->ora_e;
	/* dn2id index */
	rs->sr_err = ndb_entry_put_info( op->o_bd, &NA, 0 );
	if ( rs->sr_err ) {
		Debug( LDAP_DEBUG_TRACE,
			LDAP_XSTRING(ndb_back_add) ": ndb_entry_put_info failed (%d)\n",
			rs->sr_err, 0, 0 );
		rs->sr_text = "internal error";
		goto return_results;
	}

	/* id2entry index */
	rs->sr_err = ndb_entry_put_data( op->o_bd, &NA );
	if ( rs->sr_err ) {
		Debug( LDAP_DEBUG_TRACE,
			LDAP_XSTRING(ndb_back_add) ": ndb_entry_put_data failed (%d) %s(%d)\n",
			rs->sr_err, NA.txn->getNdbError().message, NA.txn->getNdbError().code );
		rs->sr_text = "internal error";
		goto return_results;
	}

	/* post-read */
	if( op->o_postread ) {
		if( postread_ctrl == NULL ) {
			postread_ctrl = &ctrls[num_ctrls++];
			ctrls[num_ctrls] = NULL;
		}
		if ( slap_read_controls( op, rs, op->oq_add.rs_e,
			&slap_post_read_bv, postread_ctrl ) )
		{
			Debug( LDAP_DEBUG_TRACE,
				"<=- " LDAP_XSTRING(ndb_back_add) ": post-read "
				"failed!\n", 0, 0, 0 );
			if ( op->o_postread & SLAP_CONTROL_CRITICAL ) {
				/* FIXME: is it correct to abort
				 * operation if control fails? */
				goto return_results;
			}
		}
	}

	if ( op->o_noop ) {
		if (( rs->sr_err=NA.txn->execute( NdbTransaction::Rollback,
			NdbOperation::AbortOnError, 1 )) != 0 ) {
			rs->sr_text = "txn (no-op) failed";
		} else {
			rs->sr_err = LDAP_X_NO_OPERATION;
		}

	} else {
		if(( rs->sr_err=NA.txn->execute( NdbTransaction::Commit,
			NdbOperation::AbortOnError, 1 )) != 0 ) {
			rs->sr_text = "txn_commit failed";
		} else {
			rs->sr_err = LDAP_SUCCESS;
		}
	}

	if ( rs->sr_err != LDAP_SUCCESS && rs->sr_err != LDAP_X_NO_OPERATION ) {
		Debug( LDAP_DEBUG_TRACE,
			LDAP_XSTRING(ndb_back_add) ": %s : %s (%d)\n",
			rs->sr_text, NA.txn->getNdbError().message, NA.txn->getNdbError().code );
		rs->sr_err = LDAP_OTHER;
		goto return_results;
	}
	NA.txn->close();
	NA.txn = NULL;

	Debug(LDAP_DEBUG_TRACE,
		LDAP_XSTRING(ndb_back_add) ": added%s id=%08lx dn=\"%s\"\n",
		op->o_noop ? " (no-op)" : "",
		op->oq_add.rs_e->e_id, op->oq_add.rs_e->e_dn );

	rs->sr_text = NULL;
	if( num_ctrls ) rs->sr_ctrls = ctrls;

return_results:
	success = rs->sr_err;
	send_ldap_result( op, rs );
	slap_graduate_commit_csn( op );

	if( NA.txn != NULL ) {
		NA.txn->execute( Rollback );
		NA.txn->close();
	}

	if( postread_ctrl != NULL && (*postread_ctrl) != NULL ) {
		slap_sl_free( (*postread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
		slap_sl_free( *postread_ctrl, op->o_tmpmemctx );
	}

	return rs->sr_err;
}