]> git.sur5r.net Git - openldap/blob - servers/slapd/back-bdb/modify.c
if continuation line starts with a tab, rewrite it to a space
[openldap] / servers / slapd / back-bdb / modify.c
1 /* modify.c - bdb backend modify routine */
2 /* $OpenLDAP$ */
3 /*
4  * Copyright 1998-2000 The OpenLDAP Foundation, All Rights Reserved.
5  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
6  */
7
8 #include "portable.h"
9
10 #include <stdio.h>
11 #include <ac/string.h>
12 #include <ac/time.h>
13
14 #include "back-bdb.h"
15 #include "external.h"
16
17 static int add_values( Entry *e, Modification *mod, char *dn );
18 static int delete_values( Entry *e, Modification *mod, char *dn );
19 static int replace_values( Entry *e, Modification *mod, char *dn );
20
21 int bdb_modify_internal(
22         BackendDB *be,
23         Connection *conn,
24         Operation *op,
25         DB_TXN *tid,
26         Modifications *modlist,
27         Entry *e,
28         const char **text )
29 {
30         int rc, err;
31         Modification    *mod;
32         Modifications   *ml;
33         Attribute       *save_attrs;
34
35         Debug( LDAP_DEBUG_TRACE, "bdb_modify_internal: 0x%08lx: %s\n",
36                 e->e_id, e->e_dn, 0);
37
38         if ( !acl_check_modlist( be, conn, op, e, modlist )) {
39                 return LDAP_INSUFFICIENT_ACCESS;
40         }
41
42         save_attrs = e->e_attrs;
43         e->e_attrs = attrs_dup( e->e_attrs );
44
45         for ( ml = modlist; ml != NULL; ml = ml->sml_next ) {
46                 mod = &ml->sml_mod;
47
48                 switch ( mod->sm_op ) {
49                 case LDAP_MOD_ADD:
50                         Debug(LDAP_DEBUG_ARGS, "bdb_modify_internal: add\n", 0, 0, 0);
51                         err = add_values( e, mod, op->o_ndn );
52
53                         if( err != LDAP_SUCCESS ) {
54                                 *text = "modify: add values failed";
55                                 Debug(LDAP_DEBUG_ARGS, "bdb_modify_internal: %d %s\n",
56                                         err, *text, 0);
57                         }
58                         break;
59
60                 case LDAP_MOD_DELETE:
61                         Debug(LDAP_DEBUG_ARGS, "bdb_modify_internal: delete\n", 0, 0, 0);
62                         err = delete_values( e, mod, op->o_ndn );
63                         assert( err != LDAP_TYPE_OR_VALUE_EXISTS );
64                         if( err != LDAP_SUCCESS ) {
65                                 *text = "modify: delete values failed";
66                                 Debug(LDAP_DEBUG_ARGS, "bdb_modify_internal: %d %s\n",
67                                         err, *text, 0);
68                         }
69                         break;
70
71                 case LDAP_MOD_REPLACE:
72                         Debug(LDAP_DEBUG_ARGS, "bdb_modify_internal: replace\n", 0, 0, 0);
73                         err = replace_values( e, mod, op->o_ndn );
74                         assert( err != LDAP_TYPE_OR_VALUE_EXISTS );
75                         if( err != LDAP_SUCCESS ) {
76                                 *text = "modify: replace values failed";
77                                 Debug(LDAP_DEBUG_ARGS, "bdb_modify_internal: %d %s\n",
78                                         err, *text, 0);
79                         }
80                         break;
81
82                 case SLAP_MOD_SOFTADD:
83                         Debug(LDAP_DEBUG_ARGS, "bdb_modify_internal: softadd\n", 0, 0, 0);
84                         /* Avoid problems in index_add_mods()
85                          * We need to add index if necessary.
86                          */
87                         mod->sm_op = LDAP_MOD_ADD;
88                         err = add_values( e, mod, op->o_ndn );
89
90                         if ( err == LDAP_TYPE_OR_VALUE_EXISTS ) {
91                                 err = LDAP_SUCCESS;
92                         }
93
94                         if( err != LDAP_SUCCESS ) {
95                                 *text = "modify: (soft)add values failed";
96                                 Debug(LDAP_DEBUG_ARGS, "bdb_modify_internal: %d %s\n",
97                                         err, *text, 0);
98                         }
99                         break;
100
101                 default:
102                         Debug(LDAP_DEBUG_ANY, "bdb_modify_internal: invalid op %d\n",
103                                 mod->sm_op, 0, 0);
104                         *text = "Invalid modify operation";
105                         err = LDAP_OTHER;
106                         Debug(LDAP_DEBUG_ARGS, "bdb_modify_internal: %d %s\n",
107                                 err, *text, 0);
108                 }
109
110                 if ( err != LDAP_SUCCESS ) {
111                         attrs_free( e->e_attrs );
112                         e->e_attrs = save_attrs;
113                         /* unlock entry, delete from cache */
114                         return err; 
115                 }
116         }
117
118         /* check that the entry still obeys the schema */
119         rc = entry_schema_check( e, save_attrs, text );
120         if ( rc != LDAP_SUCCESS ) {
121                 attrs_free( e->e_attrs );
122                 e->e_attrs = save_attrs;
123                 Debug( LDAP_DEBUG_ANY, "entry failed schema check: %s\n",
124                         *text, 0, 0 );
125                 return rc;
126         }
127
128 #if 0
129         /* delete indices for old attributes */
130         rc = index_entry_del( be, tid, e, save_attrs);
131
132         /* add indices for new attributes */
133         rc = index_entry_add( be, tid, e, e->e_attrs);
134 #endif
135
136         attrs_free( save_attrs );
137
138         return rc;
139 }
140
141
142 int
143 bdb_modify(
144         BackendDB       *be,
145         Connection      *conn,
146         Operation       *op,
147         const char      *dn,
148         const char      *ndn,
149         Modifications   *modlist )
150 {
151         struct bdb_info *bdb = (struct bdb_info *) be->be_private;
152         int rc;
153         Entry           *matched;
154         Entry           *e;
155         int             manageDSAit = get_manageDSAit( op );
156         const char *text = NULL;
157         DB_TXN  *ltid;
158         struct bdb_op_info opinfo;
159
160         Debug( LDAP_DEBUG_ARGS, "bdb_modify: %s\n", dn, 0, 0 );
161
162         if (0) {
163 retry:  /* transaction retry */
164                 Debug(LDAP_DEBUG_TRACE,
165                         "bdb_modify: retrying...\n", 0, 0, 0);
166                 rc = txn_abort( ltid );
167                 ltid = NULL;
168                 op->o_private = NULL;
169                 if( rc != 0 ) {
170                         rc = LDAP_OTHER;
171                         text = "internal error";
172                         goto return_results;
173                 }
174         }
175
176         /* begin transaction */
177         rc = txn_begin( bdb->bi_dbenv, NULL, &ltid, 0 );
178         text = NULL;
179         if( rc != 0 ) {
180                 Debug( LDAP_DEBUG_TRACE,
181                         "bdb_modify: txn_begin failed: %s (%d)\n",
182                         db_strerror(rc), rc, 0 );
183                 rc = LDAP_OTHER;
184                 text = "internal error";
185                 goto return_results;
186         }
187
188         opinfo.boi_bdb = be;
189         opinfo.boi_txn = ltid;
190         opinfo.boi_err = 0;
191         op->o_private = &opinfo;
192
193         /* get entry */
194         rc = bdb_dn2entry( be, ltid, ndn, &e, &matched, 0 );
195
196         if ( rc != 0 ) {
197                 Debug( LDAP_DEBUG_TRACE,
198                         "bdb_modify: dn2entry failed (%d)\n",
199                         rc, 0, 0 );
200                 switch( rc ) {
201                 case DB_LOCK_DEADLOCK:
202                 case DB_LOCK_NOTGRANTED:
203                         goto retry;
204                 case DB_NOTFOUND:
205                         break;
206                 default:
207                         rc = LDAP_OTHER;
208                 }
209                 text = "internal error";
210                 goto return_results;
211         }
212
213         /* acquire and lock entry */
214         if ( e == NULL ) {
215                 char* matched_dn = NULL;
216                 struct berval **refs = NULL;
217
218                 if ( matched != NULL ) {
219                         matched_dn = ch_strdup( matched->e_dn );
220                         refs = is_entry_referral( matched )
221                                 ? get_entry_referrals( be, conn, op, matched )
222                                 : NULL;
223                         bdb_entry_return( be, matched );
224                         matched = NULL;
225
226                 } else {
227                         refs = default_referral;
228                 }
229
230                 send_ldap_result( conn, op, rc = LDAP_REFERRAL,
231                         matched_dn, NULL, refs, NULL );
232
233                 if ( matched != NULL ) {
234                         ber_bvecfree( refs );
235                         free( matched_dn );
236                 }
237
238                 return rc;
239         }
240
241         if ( !manageDSAit && is_entry_referral( e ) ) {
242                 /* parent is a referral, don't allow add */
243                 /* parent is an alias, don't allow add */
244                 struct berval **refs = get_entry_referrals( be,
245                         conn, op, e );
246
247                 Debug( LDAP_DEBUG_TRACE,
248                         "bdb_modify: entry is referral\n",
249                         0, 0, 0 );
250
251                 send_ldap_result( conn, op, rc = LDAP_REFERRAL,
252                         e->e_dn, NULL, refs, NULL );
253
254                 ber_bvecfree( refs );
255                 goto done;
256         }
257         
258         /* Modify the entry */
259         rc = bdb_modify_internal( be, conn, op, ltid, modlist, e, &text );
260
261         if( rc != LDAP_SUCCESS ) {
262                 Debug( LDAP_DEBUG_TRACE,
263                         "bdb_modify: modify failed (%d)\n",
264                         rc, 0, 0 );
265                 switch( rc ) {
266                 case DB_LOCK_DEADLOCK:
267                 case DB_LOCK_NOTGRANTED:
268                         goto retry;
269                 }
270                 goto return_results;
271         }
272
273         /* change the entry itself */
274         rc = bdb_id2entry_update( be, ltid, e );
275         if ( rc != 0 ) {
276                 Debug( LDAP_DEBUG_TRACE,
277                         "bdb_modify: id2entry update failed (%d)\n",
278                         rc, 0, 0 );
279                 switch( rc ) {
280                 case DB_LOCK_DEADLOCK:
281                 case DB_LOCK_NOTGRANTED:
282                         goto retry;
283                 }
284                 text = "entry update failed";
285                 goto return_results;
286         }
287
288         rc = txn_commit( ltid, 0 );
289         ltid = NULL;
290         op->o_private = NULL;
291
292         if( rc != 0 ) {
293                 Debug( LDAP_DEBUG_TRACE,
294                         "bdb_modify: txn_commit failed: %s (%d)\n",
295                         db_strerror(rc), rc, 0 );
296                 rc = LDAP_OTHER;
297                 text = "commit failed";
298         } else {
299                 Debug( LDAP_DEBUG_TRACE,
300                         "bdb_modify: updated id=%08x dn=\"%s\"\n",
301                         e->e_id, e->e_dn, 0 );
302                 rc = LDAP_SUCCESS;
303                 text = NULL;
304         }
305
306 return_results:
307         send_ldap_result( conn, op, rc,
308                 NULL, text, NULL, NULL );
309
310         if(rc == LDAP_SUCCESS && bdb->bi_txn_cp ) {
311                 ldap_pvt_thread_yield();
312                 txn_checkpoint( bdb->bi_dbenv,
313                         bdb->bi_txn_cp_kbyte, bdb->bi_txn_cp_min, 0 );
314         }
315
316 done:
317         if( ltid != NULL ) {
318                 txn_abort( ltid );
319                 op->o_private = NULL;
320         }
321
322         if( e != NULL ) {
323                 bdb_entry_return( be, e );
324         }
325         return rc;
326 }
327
328 static int
329 add_values(
330         Entry   *e,
331         Modification    *mod,
332         char    *dn
333 )
334 {
335         int             i;
336         Attribute       *a;
337
338         /* char *desc = mod->sm_desc->ad_cname->bv_val; */
339         MatchingRule *mr = mod->sm_desc->ad_type->sat_equality;
340
341         a = attr_find( e->e_attrs, mod->sm_desc );
342
343         /* check if the values we're adding already exist */
344         if ( a != NULL ) {
345                 if( mr == NULL || !mr->smr_match ) {
346                         /* do not allow add of additional attribute
347                                 if no equality rule exists */
348                         return LDAP_INAPPROPRIATE_MATCHING;
349                 }
350
351                 for ( i = 0; mod->sm_bvalues[i] != NULL; i++ ) {
352                         int rc;
353                         int j;
354                         const char *text = NULL;
355                         struct berval *asserted;
356
357                         rc = value_normalize( mod->sm_desc,
358                                 SLAP_MR_EQUALITY,
359                                 mod->sm_bvalues[i],
360                                 &asserted,
361                                 &text );
362
363                         if( rc != LDAP_SUCCESS ) return rc;
364
365                         for ( j = 0; a->a_vals[j] != NULL; j++ ) {
366                                 int match;
367                                 int rc = value_match( &match, mod->sm_desc, mr,
368                                         SLAP_MR_MODIFY_MATCHING,
369                                         a->a_vals[j], asserted, &text );
370
371                                 if( rc == LDAP_SUCCESS && match == 0 ) {
372                                         ber_bvfree( asserted );
373                                         return LDAP_TYPE_OR_VALUE_EXISTS;
374                                 }
375                         }
376
377                         ber_bvfree( asserted );
378                 }
379         }
380
381         /* no - add them */
382         if( attr_merge( e, mod->sm_desc, mod->sm_bvalues ) != 0 ) {
383                 /* this should return result return of attr_merge */
384                 return LDAP_OTHER;
385         }
386
387         return LDAP_SUCCESS;
388 }
389
390 static int
391 delete_values(
392         Entry   *e,
393         Modification    *mod,
394         char    *dn
395 )
396 {
397         int             i, j, k, found;
398         Attribute       *a;
399         char *desc = mod->sm_desc->ad_cname->bv_val;
400         MatchingRule *mr = mod->sm_desc->ad_type->sat_equality;
401
402         /* delete the entire attribute */
403         if ( mod->sm_bvalues == NULL ) {
404                 Debug( LDAP_DEBUG_TRACE,
405                         "bdb_modify_delete: removing entire attribute %s\n",
406                         desc, 0, 0 );
407                 return attr_delete( &e->e_attrs, mod->sm_desc )
408                         ? LDAP_NO_SUCH_ATTRIBUTE : LDAP_SUCCESS;
409         }
410
411         if( mr == NULL || !mr->smr_match ) {
412                 /* disallow specific attributes from being deleted if
413                         no equality rule */
414                 return LDAP_INAPPROPRIATE_MATCHING;
415         }
416
417         /* delete specific values - find the attribute first */
418         if ( (a = attr_find( e->e_attrs, mod->sm_desc )) == NULL ) {
419                 Debug( LDAP_DEBUG_TRACE,
420                         "bdb_modify_delete: could not find attribute %s\n",
421                         desc, 0, 0 );
422                 return LDAP_NO_SUCH_ATTRIBUTE;
423         }
424
425         /* find each value to delete */
426         for ( i = 0; mod->sm_bvalues[i] != NULL; i++ ) {
427                 int rc;
428                 const char *text = NULL;
429
430                 struct berval *asserted;
431
432                 rc = value_normalize( mod->sm_desc,
433                         SLAP_MR_EQUALITY,
434                         mod->sm_bvalues[i],
435                         &asserted,
436                         &text );
437
438                 if( rc != LDAP_SUCCESS ) return rc;
439
440                 found = 0;
441                 for ( j = 0; a->a_vals[j] != NULL; j++ ) {
442                         int match;
443                         int rc = value_match( &match, mod->sm_desc, mr,
444                                 SLAP_MR_MODIFY_MATCHING,
445                                 a->a_vals[j], asserted, &text );
446
447                         if( rc == LDAP_SUCCESS && match != 0 ) {
448                                 continue;
449                         }
450
451                         /* found a matching value */
452                         found = 1;
453
454                         /* delete it */
455                         ber_bvfree( a->a_vals[j] );
456                         for ( k = j + 1; a->a_vals[k] != NULL; k++ ) {
457                                 a->a_vals[k - 1] = a->a_vals[k];
458                         }
459                         a->a_vals[k - 1] = NULL;
460
461                         break;
462                 }
463
464                 ber_bvfree( asserted );
465
466                 /* looked through them all w/o finding it */
467                 if ( ! found ) {
468                         Debug( LDAP_DEBUG_TRACE,
469                                 "bdb_modify_delete: could not find value for attr %s\n",
470                                 desc, 0, 0 );
471                         return LDAP_NO_SUCH_ATTRIBUTE;
472                 }
473         }
474
475         /* if no values remain, delete the entire attribute */
476         if ( a->a_vals[0] == NULL ) {
477                 Debug( LDAP_DEBUG_TRACE,
478                         "bdb_modify_delete: removing entire attribute %s\n",
479                         desc, 0, 0 );
480                 if ( attr_delete( &e->e_attrs, mod->sm_desc ) ) {
481                         return LDAP_NO_SUCH_ATTRIBUTE;
482                 }
483         }
484
485         return LDAP_SUCCESS;
486 }
487
488 static int
489 replace_values(
490         Entry   *e,
491         Modification    *mod,
492         char    *dn
493 )
494 {
495         int rc = attr_delete( &e->e_attrs, mod->sm_desc );
496
497         if( rc != LDAP_SUCCESS && rc != LDAP_NO_SUCH_ATTRIBUTE ) {
498                 return rc;
499         }
500
501         if ( mod->sm_bvalues != NULL &&
502                 attr_merge( e, mod->sm_desc, mod->sm_bvalues ) != 0 )
503         {
504                 return LDAP_OTHER;
505         }
506
507         return LDAP_SUCCESS;
508 }