1/*	$NetBSD: id2entry.c,v 1.1.1.3 2010/12/12 15:22:55 adam Exp $	*/
2
3/* id2entry.c - routines to deal with the id2entry database */
4/* OpenLDAP: pkg/ldap/servers/slapd/back-bdb/id2entry.c,v 1.72.2.14 2010/04/13 20:23:24 kurt Exp */
5/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6 *
7 * Copyright 2000-2010 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
19#include "portable.h"
20
21#include <stdio.h>
22#include <ac/string.h>
23#include <ac/errno.h>
24
25#include "back-bdb.h"
26
27static int bdb_id2entry_put(
28	BackendDB *be,
29	DB_TXN *tid,
30	Entry *e,
31	int flag )
32{
33	struct bdb_info *bdb = (struct bdb_info *) be->be_private;
34	DB *db = bdb->bi_id2entry->bdi_db;
35	DBT key, data;
36	struct berval bv;
37	int rc;
38	ID nid;
39#ifdef BDB_HIER
40	struct berval odn, ondn;
41
42	/* We only store rdns, and they go in the dn2id database. */
43
44	odn = e->e_name; ondn = e->e_nname;
45
46	e->e_name = slap_empty_bv;
47	e->e_nname = slap_empty_bv;
48#endif
49	DBTzero( &key );
50
51	/* Store ID in BigEndian format */
52	key.data = &nid;
53	key.size = sizeof(ID);
54	BDB_ID2DISK( e->e_id, &nid );
55
56	rc = entry_encode( e, &bv );
57#ifdef BDB_HIER
58	e->e_name = odn; e->e_nname = ondn;
59#endif
60	if( rc != LDAP_SUCCESS ) {
61		return -1;
62	}
63
64	DBTzero( &data );
65	bv2DBT( &bv, &data );
66
67	rc = db->put( db, tid, &key, &data, flag );
68
69	free( bv.bv_val );
70	return rc;
71}
72
73/*
74 * This routine adds (or updates) an entry on disk.
75 * The cache should be already be updated.
76 */
77
78
79int bdb_id2entry_add(
80	BackendDB *be,
81	DB_TXN *tid,
82	Entry *e )
83{
84	return bdb_id2entry_put(be, tid, e, DB_NOOVERWRITE);
85}
86
87int bdb_id2entry_update(
88	BackendDB *be,
89	DB_TXN *tid,
90	Entry *e )
91{
92	return bdb_id2entry_put(be, tid, e, 0);
93}
94
95int bdb_id2entry(
96	BackendDB *be,
97	DB_TXN *tid,
98	ID id,
99	Entry **e )
100{
101	struct bdb_info *bdb = (struct bdb_info *) be->be_private;
102	DB *db = bdb->bi_id2entry->bdi_db;
103	DBT key, data;
104	DBC *cursor;
105	EntryHeader eh;
106	char buf[16];
107	int rc = 0, off;
108	ID nid;
109
110	*e = NULL;
111
112	DBTzero( &key );
113	key.data = &nid;
114	key.size = sizeof(ID);
115	BDB_ID2DISK( id, &nid );
116
117	DBTzero( &data );
118	data.flags = DB_DBT_USERMEM | DB_DBT_PARTIAL;
119
120	/* fetch it */
121	rc = db->cursor( db, tid, &cursor, bdb->bi_db_opflags );
122	if ( rc ) return rc;
123
124	/* Get the nattrs / nvals counts first */
125	data.ulen = data.dlen = sizeof(buf);
126	data.data = buf;
127	rc = cursor->c_get( cursor, &key, &data, DB_SET );
128	if ( rc ) goto finish;
129
130
131	eh.bv.bv_val = buf;
132	eh.bv.bv_len = data.size;
133	rc = entry_header( &eh );
134	if ( rc ) goto finish;
135
136	/* Get the size */
137	data.flags ^= DB_DBT_PARTIAL;
138	data.ulen = 0;
139	rc = cursor->c_get( cursor, &key, &data, DB_CURRENT );
140	if ( rc != DB_BUFFER_SMALL ) goto finish;
141
142	/* Allocate a block and retrieve the data */
143	off = eh.data - eh.bv.bv_val;
144	eh.bv.bv_len = eh.nvals * sizeof( struct berval ) + data.size;
145	eh.bv.bv_val = ch_malloc( eh.bv.bv_len );
146	eh.data = eh.bv.bv_val + eh.nvals * sizeof( struct berval );
147	data.data = eh.data;
148	data.ulen = data.size;
149
150	/* skip past already parsed nattr/nvals */
151	eh.data += off;
152
153	rc = cursor->c_get( cursor, &key, &data, DB_CURRENT );
154
155finish:
156	cursor->c_close( cursor );
157
158	if( rc != 0 ) {
159		return rc;
160	}
161
162#ifdef SLAP_ZONE_ALLOC
163	rc = entry_decode(&eh, e, bdb->bi_cache.c_zctx);
164#else
165	rc = entry_decode(&eh, e);
166#endif
167
168	if( rc == 0 ) {
169		(*e)->e_id = id;
170	} else {
171		/* only free on error. On success, the entry was
172		 * decoded in place.
173		 */
174#ifndef SLAP_ZONE_ALLOC
175		ch_free(eh.bv.bv_val);
176#endif
177	}
178#ifdef SLAP_ZONE_ALLOC
179	ch_free(eh.bv.bv_val);
180#endif
181
182	return rc;
183}
184
185int bdb_id2entry_delete(
186	BackendDB *be,
187	DB_TXN *tid,
188	Entry *e )
189{
190	struct bdb_info *bdb = (struct bdb_info *) be->be_private;
191	DB *db = bdb->bi_id2entry->bdi_db;
192	DBT key;
193	int rc;
194	ID nid;
195
196	DBTzero( &key );
197	key.data = &nid;
198	key.size = sizeof(ID);
199	BDB_ID2DISK( e->e_id, &nid );
200
201	/* delete from database */
202	rc = db->del( db, tid, &key, 0 );
203
204	return rc;
205}
206
207int bdb_entry_return(
208	Entry *e
209)
210{
211	/* Our entries are allocated in two blocks; the data comes from
212	 * the db itself and the Entry structure and associated pointers
213	 * are allocated in entry_decode. The db data pointer is saved
214	 * in e_bv.
215	 */
216	if ( e->e_bv.bv_val ) {
217		/* See if the DNs were changed by modrdn */
218		if( e->e_nname.bv_val < e->e_bv.bv_val || e->e_nname.bv_val >
219			e->e_bv.bv_val + e->e_bv.bv_len ) {
220			ch_free(e->e_name.bv_val);
221			ch_free(e->e_nname.bv_val);
222		}
223		e->e_name.bv_val = NULL;
224		e->e_nname.bv_val = NULL;
225		/* In tool mode the e_bv buffer is realloc'd, leave it alone */
226		if( !(slapMode & SLAP_TOOL_MODE) ) {
227			free( e->e_bv.bv_val );
228		}
229		BER_BVZERO( &e->e_bv );
230	}
231	entry_free( e );
232	return 0;
233}
234
235int bdb_entry_release(
236	Operation *op,
237	Entry *e,
238	int rw )
239{
240	struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private;
241	struct bdb_op_info *boi;
242	OpExtra *oex;
243
244	/* slapMode : SLAP_SERVER_MODE, SLAP_TOOL_MODE,
245			SLAP_TRUNCATE_MODE, SLAP_UNDEFINED_MODE */
246
247	if ( slapMode == SLAP_SERVER_MODE ) {
248		/* If not in our cache, just free it */
249		if ( !e->e_private ) {
250#ifdef SLAP_ZONE_ALLOC
251			return bdb_entry_return( bdb, e, -1 );
252#else
253			return bdb_entry_return( e );
254#endif
255		}
256		/* free entry and reader or writer lock */
257		LDAP_SLIST_FOREACH( oex, &op->o_extra, oe_next ) {
258			if ( oex->oe_key == bdb ) break;
259		}
260		boi = (struct bdb_op_info *)oex;
261
262		/* lock is freed with txn */
263		if ( !boi || boi->boi_txn ) {
264			bdb_unlocked_cache_return_entry_rw( bdb, e, rw );
265		} else {
266			struct bdb_lock_info *bli, *prev;
267			for ( prev=(struct bdb_lock_info *)&boi->boi_locks,
268				bli = boi->boi_locks; bli; prev=bli, bli=bli->bli_next ) {
269				if ( bli->bli_id == e->e_id ) {
270					bdb_cache_return_entry_rw( bdb, e, rw, &bli->bli_lock );
271					prev->bli_next = bli->bli_next;
272					/* Cleanup, or let caller know we unlocked */
273					if ( bli->bli_flag & BLI_DONTFREE )
274						bli->bli_flag = 0;
275					else
276						op->o_tmpfree( bli, op->o_tmpmemctx );
277					break;
278				}
279			}
280			if ( !boi->boi_locks ) {
281				LDAP_SLIST_REMOVE( &op->o_extra, &boi->boi_oe, OpExtra, oe_next );
282				if ( !(boi->boi_flag & BOI_DONTFREE))
283					op->o_tmpfree( boi, op->o_tmpmemctx );
284			}
285		}
286	} else {
287#ifdef SLAP_ZONE_ALLOC
288		int zseq = -1;
289		if (e->e_private != NULL) {
290			BEI(e)->bei_e = NULL;
291			zseq = BEI(e)->bei_zseq;
292		}
293#else
294		if (e->e_private != NULL)
295			BEI(e)->bei_e = NULL;
296#endif
297		e->e_private = NULL;
298#ifdef SLAP_ZONE_ALLOC
299		bdb_entry_return ( bdb, e, zseq );
300#else
301		bdb_entry_return ( e );
302#endif
303	}
304
305	return 0;
306}
307
308/* return LDAP_SUCCESS IFF we can retrieve the specified entry.
309 */
310int bdb_entry_get(
311	Operation *op,
312	struct berval *ndn,
313	ObjectClass *oc,
314	AttributeDescription *at,
315	int rw,
316	Entry **ent )
317{
318	struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private;
319	struct bdb_op_info *boi = NULL;
320	DB_TXN *txn = NULL;
321	Entry *e = NULL;
322	EntryInfo *ei;
323	int	rc;
324	const char *at_name = at ? at->ad_cname.bv_val : "(null)";
325
326	DB_LOCK		lock;
327
328	Debug( LDAP_DEBUG_ARGS,
329		"=> bdb_entry_get: ndn: \"%s\"\n", ndn->bv_val, 0, 0 );
330	Debug( LDAP_DEBUG_ARGS,
331		"=> bdb_entry_get: oc: \"%s\", at: \"%s\"\n",
332		oc ? oc->soc_cname.bv_val : "(null)", at_name, 0);
333
334	if( op ) {
335		OpExtra *oex;
336		LDAP_SLIST_FOREACH( oex, &op->o_extra, oe_next ) {
337			if ( oex->oe_key == bdb ) break;
338		}
339		boi = (struct bdb_op_info *)oex;
340		if ( boi )
341			txn = boi->boi_txn;
342	}
343
344	if ( !txn ) {
345		rc = bdb_reader_get( op, bdb->bi_dbenv, &txn );
346		switch(rc) {
347		case 0:
348			break;
349		default:
350			return LDAP_OTHER;
351		}
352	}
353
354dn2entry_retry:
355	/* can we find entry */
356	rc = bdb_dn2entry( op, txn, ndn, &ei, 0, &lock );
357	switch( rc ) {
358	case DB_NOTFOUND:
359	case 0:
360		break;
361	case DB_LOCK_DEADLOCK:
362	case DB_LOCK_NOTGRANTED:
363		/* the txn must abort and retry */
364		if ( txn ) {
365			if ( boi ) boi->boi_err = rc;
366			return LDAP_BUSY;
367		}
368		ldap_pvt_thread_yield();
369		goto dn2entry_retry;
370	default:
371		if ( boi ) boi->boi_err = rc;
372		return (rc != LDAP_BUSY) ? LDAP_OTHER : LDAP_BUSY;
373	}
374	if (ei) e = ei->bei_e;
375	if (e == NULL) {
376		Debug( LDAP_DEBUG_ACL,
377			"=> bdb_entry_get: cannot find entry: \"%s\"\n",
378				ndn->bv_val, 0, 0 );
379		return LDAP_NO_SUCH_OBJECT;
380	}
381
382	Debug( LDAP_DEBUG_ACL,
383		"=> bdb_entry_get: found entry: \"%s\"\n",
384		ndn->bv_val, 0, 0 );
385
386	if ( oc && !is_entry_objectclass( e, oc, 0 )) {
387		Debug( LDAP_DEBUG_ACL,
388			"<= bdb_entry_get: failed to find objectClass %s\n",
389			oc->soc_cname.bv_val, 0, 0 );
390		rc = LDAP_NO_SUCH_ATTRIBUTE;
391		goto return_results;
392	}
393
394	/* NOTE: attr_find() or attrs_find()? */
395	if ( at && attr_find( e->e_attrs, at ) == NULL ) {
396		Debug( LDAP_DEBUG_ACL,
397			"<= bdb_entry_get: failed to find attribute %s\n",
398			at->ad_cname.bv_val, 0, 0 );
399		rc = LDAP_NO_SUCH_ATTRIBUTE;
400		goto return_results;
401	}
402
403return_results:
404	if( rc != LDAP_SUCCESS ) {
405		/* free entry */
406		bdb_cache_return_entry_rw(bdb, e, rw, &lock);
407
408	} else {
409		if ( slapMode == SLAP_SERVER_MODE ) {
410			*ent = e;
411			/* big drag. we need a place to store a read lock so we can
412			 * release it later?? If we're in a txn, nothing is needed
413			 * here because the locks will go away with the txn.
414			 */
415			if ( op ) {
416				if ( !boi ) {
417					boi = op->o_tmpcalloc(1,sizeof(struct bdb_op_info),op->o_tmpmemctx);
418					boi->boi_oe.oe_key = bdb;
419					LDAP_SLIST_INSERT_HEAD( &op->o_extra, &boi->boi_oe, oe_next );
420				}
421				if ( !boi->boi_txn ) {
422					struct bdb_lock_info *bli;
423					bli = op->o_tmpalloc( sizeof(struct bdb_lock_info),
424						op->o_tmpmemctx );
425					bli->bli_next = boi->boi_locks;
426					bli->bli_id = e->e_id;
427					bli->bli_flag = 0;
428					bli->bli_lock = lock;
429					boi->boi_locks = bli;
430				}
431			}
432		} else {
433			*ent = entry_dup( e );
434			bdb_cache_return_entry_rw(bdb, e, rw, &lock);
435		}
436	}
437
438	Debug( LDAP_DEBUG_TRACE,
439		"bdb_entry_get: rc=%d\n",
440		rc, 0, 0 );
441	return(rc);
442}
443