]> git.sur5r.net Git - openldap/blob - servers/slapd/back-bdb/modify.c
46717f07a72535358cfa842c711e253f93a7e70f
[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         const char *dn,
27         Modifications *modlist,
28         Entry *e,
29         const char **text )
30 {
31         int rc, err;
32         Modification    *mod;
33         Modifications   *ml;
34         Attribute       *save_attrs;
35
36         Debug(LDAP_DEBUG_TRACE, "bdb_modify_internal: %s\n", dn, 0, 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                                 Debug(LDAP_DEBUG_ARGS, "bdb_modify_internal: %d %s\n",
55                                         err, *text, 0);
56                                 *text = "modify: add values failed";
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                                 Debug(LDAP_DEBUG_ARGS, "bdb_modify_internal: %d %s\n",
66                                         err, *text, 0);
67                                 *text = "modify: delete values failed";
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                                 Debug(LDAP_DEBUG_ARGS, "bdb_modify_internal: %d %s\n",
77                                         err, *text, 0);
78                                 *text = "modify: replace values failed";
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                                 Debug(LDAP_DEBUG_ARGS, "bdb_modify_internal: %d %s\n",
96                                         err, *text, 0);
97                                 *text = "modify: (soft)add values failed";
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 {
152         struct bdb_info *bdb = (struct bdb_info *) be->be_private;
153         int rc;
154         Entry           *matched;
155         Entry           *e;
156         int             manageDSAit = get_manageDSAit( op );
157         const char *text = NULL;
158         DB_TXN  *ltid;
159         struct bdb_op_info opinfo;
160
161         Debug(LDAP_DEBUG_ARGS, "bdb_back_modify: %s\n", dn, 0, 0);
162
163         if (0) {
164                 /* transaction retry */
165 retry:  rc = txn_abort( ltid );
166                 ltid = NULL;
167                 op->o_private = NULL;
168                 if( rc != 0 ) {
169                         rc = LDAP_OTHER;
170                         text = "internal error";
171                         goto return_results;
172                 }
173         }
174
175         /* begin transaction */
176         rc = txn_begin( bdb->bi_dbenv, NULL, &ltid, 0 );
177         if( rc != 0 ) {
178                 Debug( LDAP_DEBUG_TRACE,
179                         "bdb_modify: txn_begin failed: %s (%d)\n",
180                         db_strerror(rc), rc, 0 );
181                 rc = LDAP_OTHER;
182                 text = "internal error";
183                 goto return_results;
184         }
185
186         opinfo.boi_bdb = be;
187         opinfo.boi_txn = ltid;
188         opinfo.boi_err = 0;
189         op->o_private = &opinfo;
190
191         /* get entry */
192         rc = bdb_dn2entry( be, ltid, ndn, &e, &matched, 0 );
193
194         if ( rc != 0 ) {
195                 Debug( LDAP_DEBUG_TRACE,
196                         "bdb_modify: dn2entry failed (%d)\n",
197                         rc, 0, 0 );
198                 switch( rc ) {
199                 case DB_LOCK_DEADLOCK:
200                 case DB_LOCK_NOTGRANTED:
201                         goto retry;
202                 case DB_NOTFOUND:
203                         break;
204                 default:
205                         rc = LDAP_OTHER;
206                 }
207                 text = "internal error";
208                 goto return_results;
209         }
210
211         /* acquire and lock entry */
212         if ( e == NULL ) {
213                 char* matched_dn = NULL;
214                 struct berval **refs = NULL;
215
216                 if ( matched != NULL ) {
217                         matched_dn = ch_strdup( matched->e_dn );
218                         refs = is_entry_referral( matched )
219                                 ? get_entry_referrals( be, conn, op, matched )
220                                 : NULL;
221                         bdb_entry_return( be, matched );
222
223                 } else {
224                         refs = default_referral;
225                 }
226
227                 send_ldap_result( conn, op, rc = LDAP_REFERRAL,
228                         matched_dn, NULL, refs, NULL );
229
230                 if ( matched != NULL ) {
231                         ber_bvecfree( refs );
232                         free( matched_dn );
233                 }
234
235                 return rc;
236         }
237
238         if ( !manageDSAit && is_entry_referral( e ) ) {
239                 /* parent is a referral, don't allow add */
240                 /* parent is an alias, don't allow add */
241                 struct berval **refs = get_entry_referrals( be,
242                         conn, op, e );
243
244                 Debug( LDAP_DEBUG_TRACE,
245                         "bdb_modify: entry is referral\n",
246                         0, 0, 0 );
247
248                 send_ldap_result( conn, op, rc = LDAP_REFERRAL,
249                         e->e_dn, NULL, refs, NULL );
250
251                 ber_bvecfree( refs );
252                 goto done;
253         }
254         
255         /* Modify the entry */
256         rc = bdb_modify_internal( be, conn, op, ltid, ndn, modlist, e, &text );
257
258         if( rc != LDAP_SUCCESS ) {
259                 Debug( LDAP_DEBUG_TRACE,
260                         "bdb_modify: modify failed (%d)\n",
261                         rc, 0, 0 );
262                 switch( rc ) {
263                 case DB_LOCK_DEADLOCK:
264                 case DB_LOCK_NOTGRANTED:
265                         goto retry;
266                 }
267                 goto return_results;
268         }
269
270         /* change the entry itself */
271         rc = bdb_id2entry_update( be, ltid, e );
272         if ( rc != 0 ) {
273                 Debug( LDAP_DEBUG_TRACE,
274                         "bdb_modify: id2entry update failed (%d)\n",
275                         rc, 0, 0 );
276                 switch( rc ) {
277                 case DB_LOCK_DEADLOCK:
278                 case DB_LOCK_NOTGRANTED:
279                         goto retry;
280                 }
281                 text = "entry update failed";
282                 goto return_results;
283         }
284
285         rc = txn_commit( ltid, 0 );
286         ltid = NULL;
287         op->o_private = NULL;
288
289         if( rc != 0 ) {
290                 Debug( LDAP_DEBUG_TRACE,
291                         "bdb_modify: txn_commit failed: %s (%d)\n",
292                         db_strerror(rc), rc, 0 );
293                 rc = LDAP_OTHER;
294                 text = "commit failed";
295         } else {
296                 Debug( LDAP_DEBUG_TRACE,
297                         "bdb_modify: added id=%08x dn=\"%s\"\n",
298                         e->e_id, e->e_dn, 0 );
299                 rc = LDAP_SUCCESS;
300                 text = NULL;
301         }
302
303 return_results:
304         send_ldap_result( conn, op, rc,
305                 NULL, text, NULL, NULL );
306
307 done:
308         if( ltid != NULL ) {
309                 txn_abort( ltid );
310                 op->o_private = NULL;
311         }
312
313         bdb_entry_return( be, e );
314         return rc;
315 }
316
317 static int
318 add_values(
319         Entry   *e,
320         Modification    *mod,
321         char    *dn
322 )
323 {
324         int             i;
325         Attribute       *a;
326
327         /* char *desc = mod->sm_desc->ad_cname->bv_val; */
328         MatchingRule *mr = mod->sm_desc->ad_type->sat_equality;
329
330         a = attr_find( e->e_attrs, mod->sm_desc );
331
332         /* check if the values we're adding already exist */
333         if ( a != NULL ) {
334                 if( mr == NULL || !mr->smr_match ) {
335                         /* do not allow add of additional attribute
336                                 if no equality rule exists */
337                         return LDAP_INAPPROPRIATE_MATCHING;
338                 }
339
340                 for ( i = 0; mod->sm_bvalues[i] != NULL; i++ ) {
341                         int rc;
342                         int j;
343                         const char *text = NULL;
344                         struct berval *asserted;
345
346                         rc = value_normalize( mod->sm_desc,
347                                 SLAP_MR_EQUALITY,
348                                 mod->sm_bvalues[i],
349                                 &asserted,
350                                 &text );
351
352                         if( rc != LDAP_SUCCESS ) return rc;
353
354                         for ( j = 0; a->a_vals[j] != NULL; j++ ) {
355                                 int match;
356                                 int rc = value_match( &match, mod->sm_desc, mr,
357                                         SLAP_MR_MODIFY_MATCHING,
358                                         a->a_vals[j], asserted, &text );
359
360                                 if( rc == LDAP_SUCCESS && match == 0 ) {
361                                         ber_bvfree( asserted );
362                                         return LDAP_TYPE_OR_VALUE_EXISTS;
363                                 }
364                         }
365
366                         ber_bvfree( asserted );
367                 }
368         }
369
370         /* no - add them */
371         if( attr_merge( e, mod->sm_desc, mod->sm_bvalues ) != 0 ) {
372                 /* this should return result return of attr_merge */
373                 return LDAP_OTHER;
374         }
375
376         return LDAP_SUCCESS;
377 }
378
379 static int
380 delete_values(
381         Entry   *e,
382         Modification    *mod,
383         char    *dn
384 )
385 {
386         int             i, j, k, found;
387         Attribute       *a;
388         char *desc = mod->sm_desc->ad_cname->bv_val;
389         MatchingRule *mr = mod->sm_desc->ad_type->sat_equality;
390
391         /* delete the entire attribute */
392         if ( mod->sm_bvalues == NULL ) {
393                 Debug( LDAP_DEBUG_TRACE,
394                         "bdb_modify_delete: removing entire attribute %s\n",
395                         desc, 0, 0 );
396                 return attr_delete( &e->e_attrs, mod->sm_desc )
397                         ? LDAP_NO_SUCH_ATTRIBUTE : LDAP_SUCCESS;
398         }
399
400         if( mr == NULL || !mr->smr_match ) {
401                 /* disallow specific attributes from being deleted if
402                         no equality rule */
403                 return LDAP_INAPPROPRIATE_MATCHING;
404         }
405
406         /* delete specific values - find the attribute first */
407         if ( (a = attr_find( e->e_attrs, mod->sm_desc )) == NULL ) {
408                 Debug( LDAP_DEBUG_TRACE,
409                         "bdb_modify_delete: could not find attribute %s\n",
410                         desc, 0, 0 );
411                 return LDAP_NO_SUCH_ATTRIBUTE;
412         }
413
414         /* find each value to delete */
415         for ( i = 0; mod->sm_bvalues[i] != NULL; i++ ) {
416                 int rc;
417                 const char *text = NULL;
418
419                 struct berval *asserted;
420
421                 rc = value_normalize( mod->sm_desc,
422                         SLAP_MR_EQUALITY,
423                         mod->sm_bvalues[i],
424                         &asserted,
425                         &text );
426
427                 if( rc != LDAP_SUCCESS ) return rc;
428
429                 found = 0;
430                 for ( j = 0; a->a_vals[j] != NULL; j++ ) {
431                         int match;
432                         int rc = value_match( &match, mod->sm_desc, mr,
433                                 SLAP_MR_MODIFY_MATCHING,
434                                 a->a_vals[j], asserted, &text );
435
436                         if( rc == LDAP_SUCCESS && match != 0 ) {
437                                 continue;
438                         }
439
440                         /* found a matching value */
441                         found = 1;
442
443                         /* delete it */
444                         ber_bvfree( a->a_vals[j] );
445                         for ( k = j + 1; a->a_vals[k] != NULL; k++ ) {
446                                 a->a_vals[k - 1] = a->a_vals[k];
447                         }
448                         a->a_vals[k - 1] = NULL;
449
450                         break;
451                 }
452
453                 ber_bvfree( asserted );
454
455                 /* looked through them all w/o finding it */
456                 if ( ! found ) {
457                         Debug( LDAP_DEBUG_TRACE,
458                                 "bdb_modify_delete: could not find value for attr %s\n",
459                                 desc, 0, 0 );
460                         return LDAP_NO_SUCH_ATTRIBUTE;
461                 }
462         }
463
464         /* if no values remain, delete the entire attribute */
465         if ( a->a_vals[0] == NULL ) {
466                 Debug( LDAP_DEBUG_TRACE,
467                         "bdb_modify_delete: removing entire attribute %s\n",
468                         desc, 0, 0 );
469                 if ( attr_delete( &e->e_attrs, mod->sm_desc ) ) {
470                         return LDAP_NO_SUCH_ATTRIBUTE;
471                 }
472         }
473
474         return LDAP_SUCCESS;
475 }
476
477 static int
478 replace_values(
479         Entry   *e,
480         Modification    *mod,
481         char    *dn
482 )
483 {
484         int rc = attr_delete( &e->e_attrs, mod->sm_desc );
485
486         if( rc != LDAP_SUCCESS && rc != LDAP_NO_SUCH_ATTRIBUTE ) {
487                 return rc;
488         }
489
490         if ( mod->sm_bvalues != NULL &&
491                 attr_merge( e, mod->sm_desc, mod->sm_bvalues ) != 0 )
492         {
493                 return LDAP_OTHER;
494         }
495
496         return LDAP_SUCCESS;
497 }