]> git.sur5r.net Git - openldap/blob - servers/slapd/back-mdb/id2entry.c
2171dcd2f2697ba29995592a3f6ed75308b05204
[openldap] / servers / slapd / back-mdb / id2entry.c
1 /* id2entry.c - routines to deal with the id2entry database */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2000-2017 The OpenLDAP Foundation.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted only as authorized by the OpenLDAP
10  * Public License.
11  *
12  * A copy of this license is available in the file LICENSE in the
13  * top-level directory of the distribution or, alternatively, at
14  * <http://www.OpenLDAP.org/license.html>.
15  */
16
17 #include "portable.h"
18
19 #include <stdio.h>
20 #include <ac/string.h>
21 #include <ac/errno.h>
22
23 #include "back-mdb.h"
24
25 typedef struct Ecount {
26         ber_len_t len;  /* total entry size */
27         ber_len_t dlen; /* contiguous data size */
28         int nattrs;
29         int nvals;
30         int offset;
31         Attribute *multi;
32 } Ecount;
33
34 static int mdb_entry_partsize(struct mdb_info *mdb, MDB_txn *txn, Entry *e,
35         Ecount *eh);
36 static int mdb_entry_encode(Operation *op, Entry *e, MDB_val *data,
37         Ecount *ec);
38 static Entry *mdb_entry_alloc( Operation *op, int nattrs, int nvals );
39
40 #define ID2VKSZ (sizeof(ID)+2)
41
42 int
43 mdb_id2v_compare(
44         const MDB_val *usrkey,
45         const MDB_val *curkey
46 )
47 {
48         unsigned short *uv, *cv;
49         ID ui, ci;
50         int rc;
51
52         memcpy(&ui, usrkey->mv_data, sizeof(ID));
53         memcpy(&ci, curkey->mv_data, sizeof(ID));
54         if (ui < ci)
55                 return -1;
56         if (ui > ci)
57                 return 1;
58         uv = usrkey->mv_data;
59         cv = curkey->mv_data;
60         return uv[sizeof(ID)/2] - cv[sizeof(ID)/2];
61 }
62
63 /* usrkey[0] is the key in DB format, as described at mdb_mval_put.
64  * usrkey[1] is the value we'll actually match against.
65  * usrkey[2] is the attributeDescription for this value.
66  */
67 int
68 mdb_id2v_dupsort(
69         const MDB_val *usrkey,
70         const MDB_val *curkey
71 )
72 {
73         AttributeDescription *ad = usrkey[2].mv_data;
74         struct berval bv1, bv2;
75         int rc, match, olen;
76         unsigned short s;
77         char *ptr;
78
79         ptr = curkey->mv_data + curkey->mv_size - 2;
80         memcpy(&s, ptr, 2);
81         bv2.bv_val = curkey->mv_data;
82         bv2.bv_len = curkey->mv_size - 3;
83         if (s)
84                 bv2.bv_len -= (s+1);
85
86         bv1.bv_val = usrkey[1].mv_data;
87         bv1.bv_len = usrkey[1].mv_size;
88
89         if (ad) {
90                 MatchingRule *mr = ad->ad_type->sat_equality;
91                 rc = mr->smr_match(&match, SLAP_MR_EQUALITY
92                 | SLAP_MR_VALUE_OF_ASSERTION_SYNTAX
93                 | SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH
94                 | SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH,
95                 ad->ad_type->sat_syntax, mr, &bv1, &bv2);
96         } else {
97                 match = ber_bvcmp(&bv1, &bv2);
98         }
99
100         return match;
101 }
102
103 /* Values are stored as
104  * [normalized-value NUL ] original-value NUL 2-byte-len
105  * The trailing 2-byte-len is zero if there is no normalized value.
106  * Otherwise, it is the length of the original-value.
107  */
108 int mdb_mval_put(Operation *op, MDB_cursor *mc, ID id, Attribute *a)
109 {
110         struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
111         MDB_val key, data[3];
112         char *buf;
113         char ivk[ID2VKSZ];
114         unsigned i;
115         unsigned short s;
116         int rc, len;
117
118         memcpy(ivk, &id, sizeof(id));
119         s = mdb->mi_adxs[a->a_desc->ad_index];
120         memcpy(ivk+sizeof(ID), &s, 2);
121         key.mv_data = &ivk;
122         key.mv_size = sizeof(ivk);
123         if ((a->a_desc->ad_type->sat_flags & SLAP_AT_ORDERED) || a->a_desc == slap_schema.si_ad_objectClass)
124                 data[2].mv_data = NULL;
125         else
126                 data[2].mv_data = a->a_desc;
127
128         for (i=0; i<a->a_numvals; i++) {
129                 len = a->a_nvals[i].bv_len + 1 + 2;
130                 if (a->a_nvals != a->a_vals) {
131                         len += a->a_vals[i].bv_len + 1;
132                         data[1].mv_data = a->a_nvals[i].bv_val;
133                         data[1].mv_size = a->a_nvals[i].bv_len;
134                 } else {
135                         data[1].mv_data = a->a_vals[i].bv_val;
136                         data[1].mv_size = a->a_vals[i].bv_len;
137                 }
138                 data[0].mv_size = len;
139                 buf = op->o_tmpalloc( len, op->o_tmpmemctx );
140                 data[0].mv_data = buf;
141                 memcpy(buf, a->a_nvals[i].bv_val, a->a_nvals[i].bv_len);
142                 buf += a->a_nvals[i].bv_len;
143                 *buf++ = 0;
144                 if (a->a_nvals != a->a_vals) {
145                         s = a->a_vals[i].bv_len;
146                         memcpy(buf, a->a_vals[i].bv_val, a->a_vals[i].bv_len);
147                         buf += a->a_vals[i].bv_len;
148                         *buf++ = 0;
149                         memcpy(buf, &s, 2);
150                 } else {
151                         *buf++ = 0;
152                         *buf++ = 0;
153                 }
154                 rc = mdb_cursor_put(mc, &key, data, 0);
155                 op->o_tmpfree( data[0].mv_data, op->o_tmpmemctx );
156                 if (rc)
157                         return rc;
158         }
159         return 0;
160 }
161
162 int mdb_mval_del(Operation *op, MDB_cursor *mc, ID id, Attribute *a)
163 {
164         struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
165         MDB_val key, data[3];
166         char *ptr;
167         char ivk[ID2VKSZ];
168         unsigned i;
169         int rc;
170         unsigned short s;
171
172         memcpy(ivk, &id, sizeof(id));
173         s = mdb->mi_adxs[a->a_desc->ad_index];
174         memcpy(ivk+sizeof(ID), &s, 2);
175         key.mv_data = &ivk;
176         key.mv_size = sizeof(ivk);
177         if ((a->a_desc->ad_type->sat_flags & SLAP_AT_ORDERED) || a->a_desc == slap_schema.si_ad_objectClass)
178                 data[2].mv_data = NULL;
179         else
180                 data[2].mv_data = a->a_desc;
181
182         if (a->a_numvals) {
183                 for (i=0; i<a->a_numvals; i++) {
184                         data[0].mv_data = a->a_nvals[i].bv_val;
185                         data[0].mv_size = a->a_nvals[i].bv_len+1;
186                         if (a->a_nvals != a->a_vals) {
187                                 data[1].mv_data = a->a_nvals[i].bv_val;
188                                 data[1].mv_size = a->a_nvals[i].bv_len;
189                         } else {
190                                 data[1].mv_data = a->a_vals[i].bv_val;
191                                 data[1].mv_size = a->a_vals[i].bv_len;
192                         }
193                         rc = mdb_cursor_get(mc, &key, data, MDB_GET_BOTH_RANGE);
194                         if (rc)
195                                 return rc;
196                         rc = mdb_cursor_del(mc, 0);
197                         if (rc)
198                                 return rc;
199                 }
200         } else {
201                 rc = mdb_cursor_get(mc, &key, data, MDB_SET);
202                 if (rc)
203                         return rc;
204                 rc = mdb_cursor_del(mc, MDB_NODUPDATA);
205         }
206         return rc;
207 }
208
209 static int mdb_mval_get(Operation *op, MDB_cursor *mc, ID id, Attribute *a, int have_nvals)
210 {
211         struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
212         MDB_val key, data[3];
213         char *ptr;
214         char ivk[ID2VKSZ];
215         unsigned i;
216         int rc;
217         unsigned short s;
218
219         memcpy(ivk, &id, sizeof(id));
220         s = mdb->mi_adxs[a->a_desc->ad_index];
221         memcpy(ivk+sizeof(ID), &s, 2);
222         key.mv_data = &ivk;
223         key.mv_size = sizeof(ivk);
224
225         /* not needed */
226         if ((a->a_desc->ad_type->sat_flags & SLAP_AT_ORDERED) || a->a_desc == slap_schema.si_ad_objectClass)
227                 data[2].mv_data = NULL;
228         else
229                 data[2].mv_data = a->a_desc;
230
231         if (have_nvals)
232                 a->a_nvals = a->a_vals + a->a_numvals + 1;
233         else
234                 a->a_nvals = a->a_vals;
235         for (i=0; i<a->a_numvals; i++) {
236                 if (!i)
237                         rc = mdb_cursor_get(mc, &key, data, MDB_SET);
238                 else
239                         rc = mdb_cursor_get(mc, &key, data, MDB_NEXT_DUP);
240                 if (rc)
241                         return rc;
242                 ptr = (char*)data[0].mv_data + data[0].mv_size - 2;
243                 memcpy(&s, ptr, 2);
244                 if (have_nvals) {
245                         a->a_nvals[i].bv_val = data[0].mv_data;
246                         a->a_vals[i].bv_len = s;
247                         a->a_vals[i].bv_val = ptr - a->a_vals[i].bv_len - 1;
248                         a->a_nvals[i].bv_len = a->a_vals[i].bv_val - a->a_nvals[i].bv_val - 1;
249                 } else {
250                         assert(!s);
251                         a->a_vals[i].bv_val = data[0].mv_data;
252                         a->a_vals[i].bv_len = data[0].mv_size - 3;
253                 }
254         }
255         BER_BVZERO(&a->a_vals[i]);
256         if (have_nvals) {
257                 BER_BVZERO(&a->a_nvals[i]);
258         }
259         return 0;
260 }
261
262 #define ADD_FLAGS       (MDB_NOOVERWRITE|MDB_APPEND)
263
264 static int mdb_id2entry_put(
265         Operation *op,
266         MDB_txn *txn,
267         MDB_cursor *mc,
268         Entry *e,
269         int flag )
270 {
271         struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
272         Ecount ec;
273         MDB_val key, data;
274         int rc, adding = flag;
275
276         /* We only store rdns, and they go in the dn2id database. */
277
278         key.mv_data = &e->e_id;
279         key.mv_size = sizeof(ID);
280
281         rc = mdb_entry_partsize( mdb, txn, e, &ec );
282         if (rc)
283                 return LDAP_OTHER;
284
285         flag |= MDB_RESERVE;
286
287         if (e->e_id < mdb->mi_nextid)
288                 flag &= ~MDB_APPEND;
289
290         if (mdb->mi_maxentrysize && ec.len > mdb->mi_maxentrysize)
291                 return LDAP_ADMINLIMIT_EXCEEDED;
292
293 again:
294         data.mv_size = ec.dlen;
295         if ( mc )
296                 rc = mdb_cursor_put( mc, &key, &data, flag );
297         else
298                 rc = mdb_put( txn, mdb->mi_id2entry, &key, &data, flag );
299         if (rc == MDB_SUCCESS) {
300                 rc = mdb_entry_encode( op, e, &data, &ec );
301                 if( rc != LDAP_SUCCESS )
302                         return rc;
303                 /* Handle adds of large multi-valued attrs here.
304                  * Modifies handle them directly.
305                  */
306                 if (adding && ec.multi) {
307                         MDB_cursor *mvc;
308                         Attribute *a;
309                         rc = mdb_cursor_open( txn, mdb->mi_dbis[MDB_ID2VAL], &mvc );
310                         if( rc )
311                                 return rc;
312                         for ( a = ec.multi; a; a=a->a_next ) {
313                                 if (!(a->a_flags & SLAP_ATTR_BIG_MULTI))
314                                         continue;
315                                 rc = mdb_mval_put( op, mvc, e->e_id, a );
316                                 if( rc != LDAP_SUCCESS )
317                                         break;
318                         }
319                         mdb_cursor_close( mvc );
320                         if ( rc )
321                                 return rc;
322                 }
323         }
324         if (rc) {
325                 /* Was there a hole from slapadd? */
326                 if ( (flag & MDB_NOOVERWRITE) && data.mv_size == 0 ) {
327                         flag &= ~ADD_FLAGS;
328                         goto again;
329                 }
330                 Debug( LDAP_DEBUG_ANY,
331                         "mdb_id2entry_put: mdb_put failed: %s(%d) \"%s\"\n",
332                         mdb_strerror(rc), rc,
333                         e->e_nname.bv_val );
334                 if ( rc != MDB_KEYEXIST )
335                         rc = LDAP_OTHER;
336         }
337         return rc;
338 }
339
340 /*
341  * This routine adds (or updates) an entry on disk.
342  */
343 int mdb_id2entry_add(
344         Operation *op,
345         MDB_txn *txn,
346         MDB_cursor *mc,
347         Entry *e )
348 {
349         return mdb_id2entry_put(op, txn, mc, e, ADD_FLAGS);
350 }
351
352 int mdb_id2entry_update(
353         Operation *op,
354         MDB_txn *txn,
355         MDB_cursor *mc,
356         Entry *e )
357 {
358         return mdb_id2entry_put(op, txn, mc, e, 0);
359 }
360
361 int mdb_id2edata(
362         Operation *op,
363         MDB_cursor *mc,
364         ID id,
365         MDB_val *data )
366 {
367         MDB_val key;
368         int rc;
369
370         key.mv_data = &id;
371         key.mv_size = sizeof(ID);
372
373         /* fetch it */
374         rc = mdb_cursor_get( mc, &key, data, MDB_SET );
375         /* stubs from missing parents - DB is actually invalid */
376         if ( rc == MDB_SUCCESS && !data->mv_size )
377                 rc = MDB_NOTFOUND;
378         return rc;
379 }
380
381 int mdb_id2entry(
382         Operation *op,
383         MDB_cursor *mc,
384         ID id,
385         Entry **e )
386 {
387         struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
388         MDB_val key, data;
389         int rc = 0;
390
391         *e = NULL;
392
393         key.mv_data = &id;
394         key.mv_size = sizeof(ID);
395
396         /* fetch it */
397         rc = mdb_cursor_get( mc, &key, &data, MDB_SET );
398         if ( rc == MDB_NOTFOUND ) {
399                 /* Looking for root entry on an empty-dn suffix? */
400                 if ( !id && BER_BVISEMPTY( &op->o_bd->be_nsuffix[0] )) {
401                         struct berval gluebv = BER_BVC("glue");
402                         Entry *r = mdb_entry_alloc(op, 2, 4);
403                         Attribute *a = r->e_attrs;
404                         struct berval *bptr;
405
406                         r->e_id = 0;
407                         r->e_ocflags = SLAP_OC_GLUE|SLAP_OC__END;
408                         bptr = a->a_vals;
409                         a->a_flags = SLAP_ATTR_DONT_FREE_DATA | SLAP_ATTR_DONT_FREE_VALS;
410                         a->a_desc = slap_schema.si_ad_objectClass;
411                         a->a_nvals = a->a_vals;
412                         a->a_numvals = 1;
413                         *bptr++ = gluebv;
414                         BER_BVZERO(bptr);
415                         bptr++;
416                         a->a_next = a+1;
417                         a = a->a_next;
418                         a->a_flags = SLAP_ATTR_DONT_FREE_DATA | SLAP_ATTR_DONT_FREE_VALS;
419                         a->a_desc = slap_schema.si_ad_structuralObjectClass;
420                         a->a_vals = bptr;
421                         a->a_nvals = a->a_vals;
422                         a->a_numvals = 1;
423                         *bptr++ = gluebv;
424                         BER_BVZERO(bptr);
425                         a->a_next = NULL;
426                         *e = r;
427                         return MDB_SUCCESS;
428                 }
429         }
430         /* stubs from missing parents - DB is actually invalid */
431         if ( rc == MDB_SUCCESS && !data.mv_size )
432                 rc = MDB_NOTFOUND;
433         if ( rc ) return rc;
434
435         rc = mdb_entry_decode( op, mdb_cursor_txn( mc ), &data, id, e );
436         if ( rc ) return rc;
437
438         (*e)->e_id = id;
439         (*e)->e_name.bv_val = NULL;
440         (*e)->e_nname.bv_val = NULL;
441
442         return rc;
443 }
444
445 int mdb_id2entry_delete(
446         BackendDB *be,
447         MDB_txn *tid,
448         Entry *e )
449 {
450         struct mdb_info *mdb = (struct mdb_info *) be->be_private;
451         MDB_dbi dbi = mdb->mi_id2entry;
452         MDB_val key;
453         MDB_cursor *mvc;
454         int rc;
455
456         key.mv_data = &e->e_id;
457         key.mv_size = sizeof(ID);
458
459         /* delete from database */
460         rc = mdb_del( tid, dbi, &key, NULL );
461         if (rc)
462                 return rc;
463         rc = mdb_cursor_open( tid, mdb->mi_dbis[MDB_ID2VAL], &mvc );
464         if (rc)
465                 return rc;
466
467         rc = mdb_cursor_get( mvc, &key, NULL, MDB_SET_RANGE );
468         if (rc) {
469                 if (rc == MDB_NOTFOUND)
470                         rc = MDB_SUCCESS;
471                 return rc;
472         }
473         while (*(ID *)key.mv_data == e->e_id ) {
474                 rc = mdb_cursor_del( mvc, MDB_NODUPDATA );
475                 if (rc)
476                         return rc;
477                 rc = mdb_cursor_get( mvc, &key, NULL, MDB_GET_CURRENT );
478                 if (rc) {
479                         if (rc == MDB_NOTFOUND)
480                                 rc = MDB_SUCCESS;
481                         break;
482                 }
483         }
484         return rc;
485 }
486
487 static Entry * mdb_entry_alloc(
488         Operation *op,
489         int nattrs,
490         int nvals )
491 {
492         Entry *e = op->o_tmpalloc( sizeof(Entry) +
493                 nattrs * sizeof(Attribute) +
494                 nvals * sizeof(struct berval), op->o_tmpmemctx );
495         BER_BVZERO(&e->e_bv);
496         e->e_private = e;
497         if (nattrs) {
498                 e->e_attrs = (Attribute *)(e+1);
499                 e->e_attrs->a_vals = (struct berval *)(e->e_attrs+nattrs);
500         } else {
501                 e->e_attrs = NULL;
502         }
503
504         return e;
505 }
506
507 int mdb_entry_return(
508         Operation *op,
509         Entry *e
510 )
511 {
512         if ( !e )
513                 return 0;
514         if ( e->e_private ) {
515                 if ( op->o_hdr && op->o_tmpmfuncs ) {
516                         op->o_tmpfree( e->e_nname.bv_val, op->o_tmpmemctx );
517                         op->o_tmpfree( e->e_name.bv_val, op->o_tmpmemctx );
518                         op->o_tmpfree( e, op->o_tmpmemctx );
519                 } else {
520                         ch_free( e->e_nname.bv_val );
521                         ch_free( e->e_name.bv_val );
522                         ch_free( e );
523                 }
524         } else {
525                 entry_free( e );
526         }
527         return 0;
528 }
529
530 int mdb_entry_release(
531         Operation *op,
532         Entry *e,
533         int rw )
534 {
535         struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
536         struct mdb_op_info *moi = NULL;
537         int rc;
538  
539         /* slapMode : SLAP_SERVER_MODE, SLAP_TOOL_MODE,
540                         SLAP_TRUNCATE_MODE, SLAP_UNDEFINED_MODE */
541  
542         int release = 1;
543         if ( slapMode & SLAP_SERVER_MODE ) {
544                 OpExtra *oex;
545                 LDAP_SLIST_FOREACH( oex, &op->o_extra, oe_next ) {
546                         release = 0;
547                         if ( oex->oe_key == mdb ) {
548                                 mdb_entry_return( op, e );
549                                 moi = (mdb_op_info *)oex;
550                                 /* If it was setup by entry_get we should probably free it */
551                                 if (( moi->moi_flag & (MOI_FREEIT|MOI_KEEPER)) == MOI_FREEIT ) {
552                                         moi->moi_ref--;
553                                         if ( moi->moi_ref < 1 ) {
554                                                 mdb_txn_reset( moi->moi_txn );
555                                                 moi->moi_ref = 0;
556                                                 LDAP_SLIST_REMOVE( &op->o_extra, &moi->moi_oe, OpExtra, oe_next );
557                                                 op->o_tmpfree( moi, op->o_tmpmemctx );
558                                         }
559                                 }
560                                 break;
561                         }
562                 }
563         }
564
565         if (release)
566                 mdb_entry_return( op, e );
567  
568         return 0;
569 }
570
571 /* return LDAP_SUCCESS IFF we can retrieve the specified entry.
572  */
573 int mdb_entry_get(
574         Operation *op,
575         struct berval *ndn,
576         ObjectClass *oc,
577         AttributeDescription *at,
578         int rw,
579         Entry **ent )
580 {
581         struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
582         struct mdb_op_info *moi = NULL;
583         MDB_txn *txn = NULL;
584         Entry *e = NULL;
585         int     rc;
586         const char *at_name = at ? at->ad_cname.bv_val : "(null)";
587
588         Debug( LDAP_DEBUG_ARGS,
589                 "=> mdb_entry_get: ndn: \"%s\"\n", ndn->bv_val, 0, 0 ); 
590         Debug( LDAP_DEBUG_ARGS,
591                 "=> mdb_entry_get: oc: \"%s\", at: \"%s\"\n",
592                 oc ? oc->soc_cname.bv_val : "(null)", at_name, 0);
593
594         rc = mdb_opinfo_get( op, mdb, rw == 0, &moi );
595         if ( rc )
596                 return LDAP_OTHER;
597         txn = moi->moi_txn;
598
599         /* can we find entry */
600         rc = mdb_dn2entry( op, txn, NULL, ndn, &e, NULL, 0 );
601         switch( rc ) {
602         case MDB_NOTFOUND:
603         case 0:
604                 break;
605         default:
606                 return (rc != LDAP_BUSY) ? LDAP_OTHER : LDAP_BUSY;
607         }
608         if (e == NULL) {
609                 Debug( LDAP_DEBUG_ACL,
610                         "=> mdb_entry_get: cannot find entry: \"%s\"\n",
611                                 ndn->bv_val, 0, 0 ); 
612                 rc = LDAP_NO_SUCH_OBJECT;
613                 goto return_results;
614         }
615         
616         Debug( LDAP_DEBUG_ACL,
617                 "=> mdb_entry_get: found entry: \"%s\"\n",
618                 ndn->bv_val, 0, 0 ); 
619
620         if ( oc && !is_entry_objectclass( e, oc, 0 )) {
621                 Debug( LDAP_DEBUG_ACL,
622                         "<= mdb_entry_get: failed to find objectClass %s\n",
623                         oc->soc_cname.bv_val, 0, 0 ); 
624                 rc = LDAP_NO_SUCH_ATTRIBUTE;
625                 goto return_results;
626         }
627
628         /* NOTE: attr_find() or attrs_find()? */
629         if ( at && attr_find( e->e_attrs, at ) == NULL ) {
630                 Debug( LDAP_DEBUG_ACL,
631                         "<= mdb_entry_get: failed to find attribute %s\n",
632                         at->ad_cname.bv_val, 0, 0 ); 
633                 rc = LDAP_NO_SUCH_ATTRIBUTE;
634                 goto return_results;
635         }
636
637 return_results:
638         if( rc != LDAP_SUCCESS ) {
639                 /* free entry */
640                 mdb_entry_release( op, e, rw );
641         } else {
642                 *ent = e;
643         }
644
645         Debug( LDAP_DEBUG_TRACE,
646                 "mdb_entry_get: rc=%d\n",
647                 rc, 0, 0 ); 
648         return(rc);
649 }
650
651 static void
652 mdb_reader_free( void *key, void *data )
653 {
654         MDB_txn *txn = data;
655
656         if ( txn ) mdb_txn_abort( txn );
657 }
658
659 /* free up any keys used by the main thread */
660 void
661 mdb_reader_flush( MDB_env *env )
662 {
663         void *data;
664         void *ctx = ldap_pvt_thread_pool_context();
665
666         if ( !ldap_pvt_thread_pool_getkey( ctx, env, &data, NULL ) ) {
667                 ldap_pvt_thread_pool_setkey( ctx, env, NULL, 0, NULL, NULL );
668                 mdb_reader_free( env, data );
669         }
670 }
671
672 extern MDB_txn *mdb_tool_txn;
673
674 int
675 mdb_opinfo_get( Operation *op, struct mdb_info *mdb, int rdonly, mdb_op_info **moip )
676 {
677         int rc, renew = 0;
678         void *data;
679         void *ctx;
680         mdb_op_info *moi = NULL;
681         OpExtra *oex;
682
683         assert( op != NULL );
684
685         if ( !mdb || !moip ) return -1;
686
687         /* If no op was provided, try to find the ctx anyway... */
688         if ( op ) {
689                 ctx = op->o_threadctx;
690         } else {
691                 ctx = ldap_pvt_thread_pool_context();
692         }
693
694         if ( op ) {
695                 LDAP_SLIST_FOREACH( oex, &op->o_extra, oe_next ) {
696                         if ( oex->oe_key == mdb ) break;
697                 }
698                 moi = (mdb_op_info *)oex;
699         }
700
701         if ( !moi ) {
702                 moi = *moip;
703
704                 if ( !moi ) {
705                         if ( op ) {
706                                 moi = op->o_tmpalloc(sizeof(struct mdb_op_info),op->o_tmpmemctx);
707                         } else {
708                                 moi = ch_malloc(sizeof(mdb_op_info));
709                         }
710                         moi->moi_flag = MOI_FREEIT;
711                         *moip = moi;
712                 }
713                 LDAP_SLIST_INSERT_HEAD( &op->o_extra, &moi->moi_oe, oe_next );
714                 moi->moi_oe.oe_key = mdb;
715                 moi->moi_ref = 0;
716                 moi->moi_txn = NULL;
717         }
718
719         if ( !rdonly ) {
720                 /* This op started as a reader, but now wants to write. */
721                 if ( moi->moi_flag & MOI_READER ) {
722                         moi = *moip;
723                         LDAP_SLIST_INSERT_HEAD( &op->o_extra, &moi->moi_oe, oe_next );
724                 } else {
725                 /* This op is continuing an existing write txn */
726                         *moip = moi;
727                 }
728                 moi->moi_ref++;
729                 if ( !moi->moi_txn ) {
730                         if (( slapMode & SLAP_TOOL_MODE ) && mdb_tool_txn ) {
731                                 moi->moi_txn = mdb_tool_txn;
732                         } else {
733                                 int flag = 0;
734                                 if ( get_lazyCommit( op ))
735                                         flag |= MDB_NOMETASYNC;
736                                 rc = mdb_txn_begin( mdb->mi_dbenv, NULL, flag, &moi->moi_txn );
737                                 if (rc) {
738                                         Debug( LDAP_DEBUG_ANY, "mdb_opinfo_get: err %s(%d)\n",
739                                                 mdb_strerror(rc), rc, 0 );
740                                 }
741                                 return rc;
742                         }
743                 }
744                 return 0;
745         }
746
747         /* OK, this is a reader */
748         if ( !moi->moi_txn ) {
749                 if (( slapMode & SLAP_TOOL_MODE ) && mdb_tool_txn ) {
750                         moi->moi_txn = mdb_tool_txn;
751                         goto ok;
752                 }
753                 if ( !ctx ) {
754                         /* Shouldn't happen unless we're single-threaded */
755                         rc = mdb_txn_begin( mdb->mi_dbenv, NULL, MDB_RDONLY, &moi->moi_txn );
756                         if (rc) {
757                                 Debug( LDAP_DEBUG_ANY, "mdb_opinfo_get: err %s(%d)\n",
758                                         mdb_strerror(rc), rc, 0 );
759                         }
760                         return rc;
761                 }
762                 if ( ldap_pvt_thread_pool_getkey( ctx, mdb->mi_dbenv, &data, NULL ) ) {
763                         rc = mdb_txn_begin( mdb->mi_dbenv, NULL, MDB_RDONLY, &moi->moi_txn );
764                         if (rc) {
765                                 Debug( LDAP_DEBUG_ANY, "mdb_opinfo_get: err %s(%d)\n",
766                                         mdb_strerror(rc), rc, 0 );
767                                 return rc;
768                         }
769                         data = moi->moi_txn;
770                         if ( ( rc = ldap_pvt_thread_pool_setkey( ctx, mdb->mi_dbenv,
771                                 data, mdb_reader_free, NULL, NULL ) ) ) {
772                                 mdb_txn_abort( moi->moi_txn );
773                                 moi->moi_txn = NULL;
774                                 Debug( LDAP_DEBUG_ANY, "mdb_opinfo_get: thread_pool_setkey failed err (%d)\n",
775                                         rc, 0, 0 );
776                                 return rc;
777                         }
778                 } else {
779                         moi->moi_txn = data;
780                         renew = 1;
781                 }
782                 moi->moi_flag |= MOI_READER;
783         }
784 ok:
785         if ( moi->moi_ref < 1 ) {
786                 moi->moi_ref = 0;
787         }
788         if ( renew ) {
789                 rc = mdb_txn_renew( moi->moi_txn );
790                 assert(!rc);
791         }
792         moi->moi_ref++;
793         if ( *moip != moi )
794                 *moip = moi;
795
796         return 0;
797 }
798
799 #ifdef LDAP_X_TXN
800 int mdb_txn( Operation *op, int txnop, OpExtra **ptr )
801 {
802         struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
803         mdb_op_info **moip = (mdb_op_info **)ptr, *moi = *moip;
804         int rc;
805
806         switch( txnop ) {
807         case SLAP_TXN_BEGIN:
808                 rc = mdb_opinfo_get( op, mdb, 0, moip );
809                 if ( !rc ) {
810                         moi = *moip;
811                         moi->moi_flag |= MOI_KEEPER;
812                 }
813                 return rc;
814         case SLAP_TXN_COMMIT:
815                 rc = mdb_txn_commit( moi->moi_txn );
816                 if ( rc )
817                         mdb->mi_numads = 0;
818                 op->o_tmpfree( moi, op->o_tmpmemctx );
819                 return rc;
820         case SLAP_TXN_ABORT:
821                 mdb->mi_numads = 0;
822                 mdb_txn_abort( moi->moi_txn );
823                 op->o_tmpfree( moi, op->o_tmpmemctx );
824                 return 0;
825         }
826         return LDAP_OTHER;
827 }
828 #endif
829
830 /* Count up the sizes of the components of an entry */
831 static int mdb_entry_partsize(struct mdb_info *mdb, MDB_txn *txn, Entry *e,
832         Ecount *eh)
833 {
834         ber_len_t len, dlen;
835         int i, nat = 0, nval = 0, nnval = 0, doff = 0;
836         Attribute *a;
837
838         eh->multi = NULL;
839         len = 4*sizeof(int);    /* nattrs, nvals, ocflags, offset */
840         dlen = len;
841         for (a=e->e_attrs; a; a=a->a_next) {
842                 /* For AttributeDesc, we only store the attr index */
843                 nat++;
844                 if (a->a_desc->ad_index >= MDB_MAXADS) {
845                         Debug( LDAP_DEBUG_ANY, "mdb_entry_partsize: too many AttributeDescriptions used\n",
846                                 0, 0, 0 );
847                         return LDAP_OTHER;
848                 }
849                 if (!mdb->mi_adxs[a->a_desc->ad_index]) {
850                         int rc = mdb_ad_get(mdb, txn, a->a_desc);
851                         if (rc)
852                                 return rc;
853                 }
854                 len += 2*sizeof(int);   /* AD index, numvals */
855                 dlen += 2*sizeof(int);
856                 nval += a->a_numvals + 1;       /* empty berval at end */
857                 if (a->a_numvals > mdb->mi_multi_hi)
858                         a->a_flags |= SLAP_ATTR_BIG_MULTI;
859                 if (a->a_flags & SLAP_ATTR_BIG_MULTI)
860                         doff += a->a_numvals;
861                 for (i=0; i<a->a_numvals; i++) {
862                         int alen = a->a_vals[i].bv_len + 1 + sizeof(int);       /* len */
863                         len += alen;
864                         if (a->a_flags & SLAP_ATTR_BIG_MULTI) {
865                                 if (!eh->multi)
866                                         eh->multi = a;
867                         } else {
868                                 dlen += alen;
869                         }
870                 }
871                 if (a->a_nvals != a->a_vals) {
872                         nval += a->a_numvals + 1;
873                         nnval++;
874                         if (a->a_flags & SLAP_ATTR_BIG_MULTI)
875                                 doff += a->a_numvals;
876                         for (i=0; i<a->a_numvals; i++) {
877                                 int alen = a->a_nvals[i].bv_len + 1 + sizeof(int);
878                                 len += alen;
879                                 if (!(a->a_flags & SLAP_ATTR_BIG_MULTI))
880                                         dlen += alen;
881                         }
882                 }
883         }
884         /* padding */
885         dlen = (dlen + sizeof(ID)-1) & ~(sizeof(ID)-1);
886         eh->len = len;
887         eh->dlen = dlen;
888         eh->nattrs = nat;
889         eh->nvals = nval;
890         eh->offset = nat + nval - nnval - doff;
891         return 0;
892 }
893
894 /* Flag bits for an encoded attribute */
895 #define MDB_AT_SORTED   (1<<(sizeof(unsigned int)*CHAR_BIT-1))
896         /* the values are in sorted order */
897 #define MDB_AT_MULTI    (1<<(sizeof(unsigned int)*CHAR_BIT-2))
898         /* the values of this multi-valued attr are stored separately */
899
900 #define MDB_AT_NVALS    (1<<(sizeof(unsigned int)*CHAR_BIT-1))
901         /* this attribute has normalized values */
902
903 /* Flatten an Entry into a buffer. The buffer starts with the count of the
904  * number of attributes in the entry, the total number of values in the
905  * entry, and the e_ocflags. It then contains a list of integers for each
906  * attribute. For each attribute the first integer gives the index of the
907  * matching AttributeDescription, followed by the number of values in the
908  * attribute. If the MDB_AT_SORTED bit of the attr index is set, the
909  * attribute's values are already sorted. If the MDB_AT_MULTI bit of the
910  * attr index is set, the values are stored separately.
911  *
912  * If the MDB_AT_NVALS bit of numvals is set, the attribute also has
913  * normalized values present. (Note - a_numvals is an unsigned int, so this
914  * means it's possible to receive an attribute that we can't encode due
915  * to size overflow. In practice, this should not be an issue.)
916  *
917  * Then the length of each value is listed. If there are normalized values,
918  * their lengths come next. This continues for each attribute. After all
919  * of the lengths for the last attribute, the actual values are copied,
920  * with a NUL terminator after each value.
921  * The buffer is padded to the sizeof(ID). The entire buffer size is
922  * precomputed so that a single malloc can be performed.
923  */
924 static int mdb_entry_encode(Operation *op, Entry *e, MDB_val *data, Ecount *eh)
925 {
926         struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
927         ber_len_t len, i;
928         int rc;
929         Attribute *a;
930         unsigned char *ptr;
931         unsigned int *lp, l;
932
933         Debug( LDAP_DEBUG_TRACE, "=> mdb_entry_encode(0x%08lx): %s\n",
934                 (long) e->e_id, e->e_dn, 0 );
935
936         /* make sure e->e_ocflags is set */
937         if (is_entry_referral(e))
938                 ;       /* empty */
939
940         lp = (unsigned int *)data->mv_data;
941         *lp++ = eh->nattrs;
942         *lp++ = eh->nvals;
943         *lp++ = (unsigned int)e->e_ocflags;
944         *lp++ = eh->offset;
945         ptr = (unsigned char *)(lp + eh->offset);
946
947         for (a=e->e_attrs; a; a=a->a_next) {
948                 if (!a->a_desc->ad_index)
949                         return LDAP_UNDEFINED_TYPE;
950                 l = mdb->mi_adxs[a->a_desc->ad_index];
951                 if (a->a_flags & SLAP_ATTR_BIG_MULTI)
952                         l |= MDB_AT_MULTI;
953                 if (a->a_flags & SLAP_ATTR_SORTED_VALS)
954                         l |= MDB_AT_SORTED;
955                 *lp++ = l;
956                 l = a->a_numvals;
957                 if (a->a_nvals != a->a_vals)
958                         l |= MDB_AT_NVALS;
959                 *lp++ = l;
960                 if (a->a_flags & SLAP_ATTR_BIG_MULTI) {
961                         continue;
962                 } else {
963                         if (a->a_vals) {
964                                 for (i=0; a->a_vals[i].bv_val; i++);
965                                 assert( i == a->a_numvals );
966                                 for (i=0; i<a->a_numvals; i++) {
967                                         *lp++ = a->a_vals[i].bv_len;
968                                         memcpy(ptr, a->a_vals[i].bv_val,
969                                                 a->a_vals[i].bv_len);
970                                         ptr += a->a_vals[i].bv_len;
971                                         *ptr++ = '\0';
972                                 }
973                                 if (a->a_nvals != a->a_vals) {
974                                         for (i=0; i<a->a_numvals; i++) {
975                                                 *lp++ = a->a_nvals[i].bv_len;
976                                                 memcpy(ptr, a->a_nvals[i].bv_val,
977                                                         a->a_nvals[i].bv_len);
978                                                 ptr += a->a_nvals[i].bv_len;
979                                                 *ptr++ = '\0';
980                                         }
981                                 }
982                         }
983                 }
984         }
985
986         Debug( LDAP_DEBUG_TRACE, "<= mdb_entry_encode(0x%08lx): %s\n",
987                 (long) e->e_id, e->e_dn, 0 );
988
989         return 0;
990 }
991
992 /* Retrieve an Entry that was stored using entry_encode above.
993  *
994  * Note: everything is stored in a single contiguous block, so
995  * you can not free individual attributes or names from this
996  * structure. Attempting to do so will likely corrupt memory.
997  */
998
999 int mdb_entry_decode(Operation *op, MDB_txn *txn, MDB_val *data, ID id, Entry **e)
1000 {
1001         struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
1002         int i, j, nattrs, nvals;
1003         int rc;
1004         Attribute *a;
1005         Entry *x;
1006         const char *text;
1007         AttributeDescription *ad;
1008         unsigned int *lp = (unsigned int *)data->mv_data;
1009         unsigned char *ptr;
1010         BerVarray bptr;
1011         MDB_cursor *mvc = NULL;
1012
1013         Debug( LDAP_DEBUG_TRACE,
1014                 "=> mdb_entry_decode:\n",
1015                 0, 0, 0 );
1016
1017         nattrs = *lp++;
1018         nvals = *lp++;
1019         x = mdb_entry_alloc(op, nattrs, nvals);
1020         x->e_ocflags = *lp++;
1021         if (!nvals) {
1022                 goto done;
1023         }
1024         a = x->e_attrs;
1025         bptr = a->a_vals;
1026         i = *lp++;
1027         ptr = (unsigned char *)(lp + i);
1028
1029         for (;nattrs>0; nattrs--) {
1030                 int have_nval = 0, multi = 0;
1031                 a->a_flags = SLAP_ATTR_DONT_FREE_DATA | SLAP_ATTR_DONT_FREE_VALS;
1032                 i = *lp++;
1033                 if (i & MDB_AT_SORTED) {
1034                         i ^= MDB_AT_SORTED;
1035                         a->a_flags |= SLAP_ATTR_SORTED_VALS;
1036                 }
1037                 if (i & MDB_AT_MULTI) {
1038                         i ^= MDB_AT_MULTI;
1039                         a->a_flags |= SLAP_ATTR_BIG_MULTI;
1040                         multi = 1;
1041                 }
1042                 if (i > mdb->mi_numads) {
1043                         rc = mdb_ad_read(mdb, txn);
1044                         if (rc)
1045                                 goto leave;
1046                         if (i > mdb->mi_numads) {
1047                                 Debug( LDAP_DEBUG_ANY,
1048                                         "mdb_entry_decode: attribute index %d not recognized\n",
1049                                         i, 0, 0 );
1050                                 rc = LDAP_OTHER;
1051                                 goto leave;
1052                         }
1053                 }
1054                 a->a_desc = mdb->mi_ads[i];
1055                 a->a_numvals = *lp++;
1056                 if (a->a_numvals & MDB_AT_NVALS) {
1057                         a->a_numvals ^= MDB_AT_NVALS;
1058                         have_nval = 1;
1059                 }
1060                 a->a_vals = bptr;
1061                 if (multi) {
1062                         if (!mvc) {
1063                                 rc = mdb_cursor_open(txn, mdb->mi_dbis[MDB_ID2VAL], &mvc);
1064                                 if (rc)
1065                                         goto leave;
1066                         }
1067                         mdb_mval_get(op, mvc, id, a, have_nval);
1068                         bptr += a->a_numvals + 1;
1069                         if (have_nval)
1070                                 bptr += a->a_numvals + 1;
1071                 } else {
1072                         for (i=0; i<a->a_numvals; i++) {
1073                                 bptr->bv_len = *lp++;
1074                                 bptr->bv_val = (char *)ptr;
1075                                 ptr += bptr->bv_len+1;
1076                                 bptr++;
1077                         }
1078                         bptr->bv_val = NULL;
1079                         bptr->bv_len = 0;
1080                         bptr++;
1081
1082                         if (have_nval) {
1083                                 a->a_nvals = bptr;
1084                                 for (i=0; i<a->a_numvals; i++) {
1085                                         bptr->bv_len = *lp++;
1086                                         bptr->bv_val = (char *)ptr;
1087                                         ptr += bptr->bv_len+1;
1088                                         bptr++;
1089                                 }
1090                                 bptr->bv_val = NULL;
1091                                 bptr->bv_len = 0;
1092                                 bptr++;
1093                         } else {
1094                                 a->a_nvals = a->a_vals;
1095                         }
1096                 }
1097
1098                 /* FIXME: This is redundant once a sorted entry is saved into the DB */
1099                 if (( a->a_desc->ad_type->sat_flags & SLAP_AT_SORTED_VAL )
1100                         && !(a->a_flags & SLAP_ATTR_SORTED_VALS)) {
1101                         rc = slap_sort_vals( (Modifications *)a, &text, &j, NULL );
1102                         if ( rc == LDAP_SUCCESS ) {
1103                                 a->a_flags |= SLAP_ATTR_SORTED_VALS;
1104                         } else if ( rc == LDAP_TYPE_OR_VALUE_EXISTS ) {
1105                                 /* should never happen */
1106                                 Debug( LDAP_DEBUG_ANY,
1107                                         "mdb_entry_decode: attributeType %s value #%d provided more than once\n",
1108                                         a->a_desc->ad_cname.bv_val, j, 0 );
1109                                 goto leave;
1110                         }
1111                 }
1112                 a->a_next = a+1;
1113                 a = a->a_next;
1114         }
1115         a[-1].a_next = NULL;
1116 done:
1117         Debug(LDAP_DEBUG_TRACE, "<= mdb_entry_decode\n",
1118                 0, 0, 0 );
1119         *e = x;
1120         rc = 0;
1121
1122 leave:
1123         if (mvc)
1124                 mdb_cursor_close(mvc);
1125         return rc;
1126 }