]> git.sur5r.net Git - openldap/blob - servers/slapd/back-bdb/modrdn.c
36c1bed49c387899210f3c61d5b9505065d85f73
[openldap] / servers / slapd / back-bdb / modrdn.c
1 /* modrdn.c - bdb backend modrdn 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
13 #include "back-bdb.h"
14 #include "external.h"
15
16 int
17 bdb_modrdn(
18         Backend *be,
19         Connection      *conn,
20         Operation       *op,
21         const char      *dn,
22         const char      *ndn,
23         const char      *newrdn,
24         int             deleteoldrdn,
25         const char      *newSuperior
26 )
27 {
28         struct bdb_info *bdb = (struct bdb_info *) be->be_private;
29         AttributeDescription *children = slap_schema.si_ad_children;
30         char            *p_dn = NULL, *p_ndn = NULL;
31         char            *new_dn = NULL, *new_ndn = NULL;
32         Entry           *e, *p = NULL;
33         Entry           *matched;
34         int                     rc;
35         const char *text = NULL;
36         DB_TXN *        ltid;
37         struct bdb_op_info opinfo;
38
39         ID                      id;
40         char            *new_rdn_val = NULL;    /* Val of new rdn */
41         char            *new_rdn_type = NULL;   /* Type of new rdn */
42         char            *old_rdn = NULL;                /* Old rdn's attr type & val */
43         char            *old_rdn_type = NULL;   /* Type of old rdn attr. */
44         char            *old_rdn_val = NULL;    /* Old rdn attribute value */
45
46         Entry           *np = NULL;                             /* newSuperior Entry */
47         char            *np_dn = NULL;                  /* newSuperior dn */
48         char            *np_ndn = NULL;                 /* newSuperior ndn */
49         char            *new_parent_dn = NULL;  /* np_dn, p_dn, or NULL */
50
51         /* Used to interface with bdb_modify_internal() */
52         struct berval   add_bv;                         /* Stores new rdn att */
53         struct berval   *add_bvals[2];          /* Stores new rdn att */
54         struct berval   del_bv;                         /* Stores old rdn att */
55         struct berval   *del_bvals[2];          /* Stores old rdn att */
56         Modifications   mod[2];                         /* Used to delete old rdn */
57
58         int             manageDSAit = get_manageDSAit( op );
59
60         Debug( LDAP_DEBUG_TRACE, "==>bdb_modrdn(%s,%s,%s)\n",
61                 dn, newrdn, (newSuperior ? newSuperior : "NULL") );
62
63         if (0) {
64                 /* transaction retry */
65 retry:  rc = txn_abort( ltid );
66                 ltid = NULL;
67                 op->o_private = NULL;
68                 if( rc != 0 ) {
69                         rc = LDAP_OTHER;
70                         text = "internal error";
71                         goto return_results;
72                 }
73         }
74
75         /* begin transaction */
76         rc = txn_begin( bdb->bi_dbenv, NULL, &ltid, 0 );
77         if( rc != 0 ) {
78                 Debug( LDAP_DEBUG_TRACE,
79                         "bdb_delete: txn_begin failed: %s (%d)\n",
80                         db_strerror(rc), rc, 0 );
81                 rc = LDAP_OTHER;
82                 text = "internal error";
83                 goto return_results;
84         }
85
86         opinfo.boi_bdb = be;
87         opinfo.boi_txn = ltid;
88         opinfo.boi_err = 0;
89         op->o_private = &opinfo;
90
91         /* get entry */
92         rc = bdb_dn2entry( be, ltid, ndn, &e, &matched, 0 );
93
94         switch( rc ) {
95         case 0:
96         case DB_NOTFOUND:
97                 break;
98         case DB_LOCK_DEADLOCK:
99         case DB_LOCK_NOTGRANTED:
100                 goto retry;
101         default:
102                 rc = LDAP_OTHER;
103                 text = "internal error";
104                 goto return_results;
105         }
106
107         if ( e == NULL ) {
108                 char* matched_dn = NULL;
109                 struct berval** refs = NULL;
110
111                 if( matched != NULL ) {
112                         matched_dn = strdup( matched->e_dn );
113                         refs = is_entry_referral( matched )
114                                 ? get_entry_referrals( be, conn, op, matched )
115                                 : NULL;
116                         bdb_entry_return( be, matched );
117                 } else {
118                         refs = default_referral;
119                 }
120
121                 send_ldap_result( conn, op, rc = LDAP_REFERRAL,
122                         matched_dn, NULL, refs, NULL );
123
124                 if ( matched != NULL ) {
125                         ber_bvecfree( refs );
126                         free( matched_dn );
127                 }
128
129                 goto done;
130         }
131
132         if (!manageDSAit && is_entry_referral( e ) ) {
133                 /* parent is a referral, don't allow add */
134                 /* parent is an alias, don't allow add */
135                 struct berval **refs = get_entry_referrals( be,
136                         conn, op, e );
137
138                 Debug( LDAP_DEBUG_TRACE, "bdb_modrdn: entry is referral\n",
139                         0, 0, 0 );
140
141                 send_ldap_result( conn, op, rc = LDAP_REFERRAL,
142                         e->e_dn, NULL, refs, NULL );
143
144                 ber_bvecfree( refs );
145                 goto done;
146         }
147
148         p_ndn = dn_parent( be, e->e_ndn );
149         if ( p_ndn != NULL ) {
150                 /* Make sure parent entry exist and we can write its 
151                  * children.
152                  */
153
154                 rc = bdb_dn2entry( be, ltid, p_ndn, &p, NULL, 0 );
155
156                 switch( rc ) {
157                 case 0:
158                 case DB_NOTFOUND:
159                         break;
160                 case DB_LOCK_DEADLOCK:
161                 case DB_LOCK_NOTGRANTED:
162                         goto retry;
163                 default:
164                         rc = LDAP_OTHER;
165                         text = "internal error";
166                         goto return_results;
167                 }
168
169                 if( p == NULL) {
170                         Debug( LDAP_DEBUG_TRACE, "bdb_modrdn: parent does not exist\n",
171                                 0, 0, 0);
172                         rc = LDAP_OTHER;
173                         goto return_results;
174                 }
175
176                 /* check parent for "children" acl */
177                 if ( ! access_allowed( be, conn, op, p,
178                         children, NULL, ACL_WRITE ) )
179                 {
180                         Debug( LDAP_DEBUG_TRACE, "no access to parent\n", 0,
181                                 0, 0 );
182                         send_ldap_result( conn, op, LDAP_INSUFFICIENT_ACCESS,
183                                 NULL, NULL, NULL, NULL );
184                         goto return_results;
185                 }
186
187                 Debug( LDAP_DEBUG_TRACE,
188                         "bdb_modrdn: wr to children of entry %s OK\n",
189                         p_ndn, 0, 0 );
190                 
191                 p_dn = dn_parent( be, e->e_dn );
192
193                 Debug( LDAP_DEBUG_TRACE,
194                         "bdb_modrdn: parent dn=%s\n",
195                         p_dn, 0, 0 );
196
197         } else {
198                 /* no parent, modrdn entry directly under root */
199                 if( ! be_isroot( be, op->o_ndn ) ) {
200                         Debug( LDAP_DEBUG_TRACE,
201                                 "bdb_modrdn: no parent & not root\n",
202                                 0, 0, 0);
203                         rc = LDAP_INSUFFICIENT_ACCESS;
204                         goto return_results;
205                 }
206
207                 Debug( LDAP_DEBUG_TRACE,
208                         "bdb_modrdn: no parent, locked root\n",
209                         0, 0, 0 );
210         }
211
212         new_parent_dn = p_dn;   /* New Parent unless newSuperior given */
213
214         if ( newSuperior != NULL ) {
215                 Debug( LDAP_DEBUG_TRACE, 
216                         "bdb_modrdn: new parent \"%s\" requested...\n",
217                         newSuperior, 0, 0 );
218
219                 np_dn = ch_strdup( newSuperior );
220                 np_ndn = ch_strdup( np_dn );
221                 (void) dn_normalize( np_ndn );
222
223                 /* newSuperior == oldParent?, if so ==> ERROR */
224                 /* newSuperior == entry being moved?, if so ==> ERROR */
225                 /* Get Entry with dn=newSuperior. Does newSuperior exist? */
226
227                 rc = bdb_dn2entry( be, ltid, np_ndn, &np, NULL, 0 );
228
229                 switch( rc ) {
230                 case 0:
231                 case DB_NOTFOUND:
232                         break;
233                 case DB_LOCK_DEADLOCK:
234                 case DB_LOCK_NOTGRANTED:
235                         goto retry;
236                 default:
237                         rc = LDAP_OTHER;
238                         text = "internal error";
239                         goto return_results;
240                 }
241
242                 if( np == NULL) {
243                         Debug( LDAP_DEBUG_TRACE,
244                                 "bdb_modrdn: newSup(ndn=%s) not here!\n",
245                                 np_ndn, 0, 0);
246                         rc = LDAP_OTHER;
247                         goto return_results;
248                 }
249
250                 Debug( LDAP_DEBUG_TRACE,
251                         "bdb_modrdn: wr to new parent OK np=%p, id=%ld\n",
252                         np, np->e_id, 0 );
253
254                 /* check newSuperior for "children" acl */
255                 if ( !access_allowed( be, conn, op, np, children, NULL, ACL_WRITE ) ) {
256                         Debug( LDAP_DEBUG_TRACE,
257                                 "bdb_modrdn: no wr to newSup children\n",
258                                 0, 0, 0 );
259                         rc = LDAP_INSUFFICIENT_ACCESS;
260                         goto return_results;
261                 }
262
263                 if ( is_entry_alias( np ) ) {
264                         /* entry is an alias, don't allow bind */
265                         Debug( LDAP_DEBUG_TRACE, "bdb_modrdn: entry is alias\n",
266                                 0, 0, 0 );
267
268                         rc = LDAP_ALIAS_PROBLEM;
269                         goto return_results;
270                 }
271
272                 if ( is_entry_referral( np ) ) {
273                         /* parent is a referral, don't allow add */
274                         /* parent is an alias, don't allow add */
275                         Debug( LDAP_DEBUG_TRACE, "bdb_modrdn: entry is referral\n",
276                                 0, 0, 0 );
277
278                         rc = LDAP_OPERATIONS_ERROR;
279                         goto return_results;
280                 }
281
282                 Debug( LDAP_DEBUG_TRACE,
283                         "bdb_modrdn: wr to new parent's children OK\n",
284                         0, 0, 0 );
285
286                 new_parent_dn = np_dn;
287         }
288         
289         /* Build target dn and make sure target entry doesn't exist already. */
290         build_new_dn( &new_dn, e->e_dn, new_parent_dn, newrdn ); 
291
292         new_ndn = ch_strdup(new_dn);
293         (void) dn_normalize( new_ndn );
294
295         Debug( LDAP_DEBUG_TRACE, "bdb_modrdn: new ndn=%s\n",
296                 new_ndn, 0, 0 );
297
298         rc = bdb_dn2id ( be, ltid, new_ndn, &id );
299         if( rc != 0 ) {
300                 switch( rc ) {
301                 case DB_LOCK_DEADLOCK:
302                 case DB_LOCK_NOTGRANTED:
303                         goto retry;
304                 default:
305                         rc = LDAP_OTHER;
306                         text = "internal error";
307                 }
308
309                 goto return_results;
310         }
311
312         Debug( LDAP_DEBUG_TRACE,
313                 "bdb_modrdn: new ndn=%s does not exist\n",
314                 new_ndn, 0, 0 );
315
316         /* Get attribute type and attribute value of our new rdn, we will
317          * need to add that to our new entry
318          */
319
320         new_rdn_type = rdn_attr_type( newrdn );
321         if ( new_rdn_type == NULL ) {
322                 Debug( LDAP_DEBUG_TRACE,
323                         "bdb_modrdn: can't figure out type of newrdn\n",
324                         0, 0, 0 );
325                 rc = LDAP_OPERATIONS_ERROR;
326                 text = "unknown type used in RDN";
327                 goto return_results;            
328         }
329
330         new_rdn_val = rdn_attr_value( newrdn );
331         if ( new_rdn_val == NULL ) {
332                 Debug( LDAP_DEBUG_TRACE,
333                         "bdb_modrdn: could not figure out val of newrdn\n",
334                         0, 0, 0 );
335                 rc = LDAP_OPERATIONS_ERROR;
336                 text = "could not parse RDN value";
337                 goto return_results;            
338         }
339
340         Debug( LDAP_DEBUG_TRACE,
341                 "bdb_modrdn: new_rdn_val=\"%s\", new_rdn_type=\"%s\"\n",
342                 new_rdn_val, new_rdn_type, 0 );
343
344         /* Retrieve the old rdn from the entry's dn */
345
346         if ( (old_rdn = dn_rdn( be, dn )) == NULL ) {
347                 Debug( LDAP_DEBUG_TRACE,
348                         "bdb_modrdn: can't figure out old_rdn from dn\n",
349                         0, 0, 0 );
350                 rc = LDAP_OTHER;
351                 text = "could not parse old DN";
352                 goto return_results;            
353         }
354
355         if ( (old_rdn_type = rdn_attr_type( old_rdn )) == NULL ) {
356                 Debug( LDAP_DEBUG_TRACE,
357                         "bdb_back_modrdn: can't figure out the old_rdn type\n",
358                         0, 0, 0 );
359                 rc = LDAP_OTHER;
360                 text = "cannot parse RDN from old DN";
361                 goto return_results;            
362         }
363         
364         if ( strcasecmp( old_rdn_type, new_rdn_type ) != 0 ) {
365                 /* Not a big deal but we may say something */
366                 Debug( LDAP_DEBUG_TRACE,
367                         "bdb_modrdn: old_rdn_type=%s, new_rdn_type=%s!\n",
368                         old_rdn_type, new_rdn_type, 0 );
369         }               
370
371         /* Add new attribute value to the entry */
372         add_bvals[0] = &add_bv;         /* Array of bervals */
373         add_bvals[1] = NULL;
374
375         add_bv.bv_val = new_rdn_val;
376         add_bv.bv_len = strlen(new_rdn_val);
377                 
378         mod[0].sml_desc = NULL;
379         rc = slap_str2ad( new_rdn_type, &mod[0].sml_desc, &text );
380
381         if( rc != LDAP_SUCCESS ) {
382                 Debug( LDAP_DEBUG_TRACE,
383                         "bdb_modrdn: %s: %s (new)\n",
384                         text, new_rdn_type, 0 );
385                 goto return_results;            
386         }
387         mod[0].sml_bvalues = add_bvals;
388         mod[0].sml_op = SLAP_MOD_SOFTADD;
389         mod[0].sml_next = NULL;
390
391         /* Remove old rdn value if required */
392
393         if (deleteoldrdn) {
394                 /* Get value of old rdn */
395                 old_rdn_val = rdn_attr_value( old_rdn );
396                 if ( old_rdn_val == NULL) {
397                         Debug( LDAP_DEBUG_TRACE,
398                                 "bdb_modrdn: can't figure out old_rdn_val from old_rdn\n",
399                                 0, 0, 0 );
400                         rc = LDAP_OTHER;
401                         text = "could not parse value from old RDN";
402                         goto return_results;            
403                 }
404
405                 del_bvals[0] = &del_bv;         /* Array of bervals */
406                 del_bvals[1] = NULL;
407
408                 /* Remove old value of rdn as an attribute. */
409                 del_bv.bv_val = old_rdn_val;
410                 del_bv.bv_len = strlen(old_rdn_val);
411
412                 mod[1].sml_desc = NULL;
413                 rc = slap_str2ad( old_rdn_type, &mod[1].sml_desc, &text );
414
415                 if( rc != LDAP_SUCCESS ) {
416                         Debug( LDAP_DEBUG_TRACE,
417                                 "bdb_modrdn: %s: %s (old)\n",
418                                 text, old_rdn_type, 0 );
419                         goto return_results;            
420                 }
421
422                 mod[0].sml_next = &mod[1];
423                 mod[1].sml_bvalues = del_bvals;
424                 mod[1].sml_op = LDAP_MOD_DELETE;
425                 mod[1].sml_next = NULL;
426         }
427         
428         /* delete old one */
429         rc = bdb_dn2id_delete( be, ltid, e->e_ndn, e->e_id );
430         if ( rc != 0 ) {
431                 rc = LDAP_OTHER;
432                 text = "DN index delete fail";
433                 goto return_results;
434         }
435
436         free( e->e_dn );
437         free( e->e_ndn );
438         e->e_dn = new_dn;
439         e->e_ndn = new_ndn;
440         new_dn = NULL;
441         new_ndn = NULL;
442
443         /* add new one */
444         rc = bdb_dn2id_add( be, ltid, e->e_ndn, e->e_id );
445         if ( rc != 0 ) {
446                 rc = LDAP_OTHER;
447                 text = "DN index add failed";
448                 goto return_results;
449         }
450
451         /* modify entry */
452         rc = bdb_modify_internal( be, conn, op, ltid, &mod[0], e, &text );
453
454         if( rc != LDAP_SUCCESS ) {
455                 goto return_results;
456         }
457         
458         /* NOTE: after this you must not free new_dn or new_ndn!
459          * They are used by cache.
460          */
461
462         /* id2entry index */
463         rc = bdb_id2entry_add( be, ltid, e );
464         if ( rc != 0 ) {
465                 rc = LDAP_OTHER;
466                 text = "entry update failed";
467                 goto return_results;
468         }
469
470         rc = LDAP_SUCCESS;
471
472         rc = txn_commit( ltid, 0 );
473         ltid = NULL;
474         op->o_private = NULL;
475
476         if( rc != 0 ) {
477                 Debug( LDAP_DEBUG_TRACE,
478                         "bdb_modrdn: txn_commit failed: %s (%d)\n",
479                         db_strerror(rc), rc, 0 );
480                 rc = LDAP_OTHER;
481                 text = "commit failed";
482         } else {
483                 Debug( LDAP_DEBUG_TRACE,
484                         "bdb_modrdn: added id=%08x dn=\"%s\"\n",
485                         e->e_id, e->e_dn, 0 );
486                 rc = LDAP_SUCCESS;
487                 text = NULL;
488         }
489
490 return_results:
491         send_ldap_result( conn, op, rc,
492                 NULL, text, NULL, NULL );
493
494 done:
495         if( new_dn != NULL ) free( new_dn );
496         if( new_ndn != NULL ) free( new_ndn );
497
498         if( p_dn != NULL ) free( p_dn );
499         if( p_ndn != NULL ) free( p_ndn );
500
501         /* LDAP v2 supporting correct attribute handling. */
502         if( new_rdn_type != NULL ) free(new_rdn_type);
503         if( new_rdn_val != NULL ) free(new_rdn_val);
504         if( old_rdn != NULL ) free(old_rdn);
505         if( old_rdn_type != NULL ) free(old_rdn_type);
506         if( old_rdn_val != NULL ) free(old_rdn_val);
507
508
509         /* LDAP v3 Support */
510         if ( np_dn != NULL ) free( np_dn );
511         if ( np_ndn != NULL ) free( np_ndn );
512
513         if( np != NULL ) {
514                 /* free new parent and writer lock */
515                 bdb_entry_return( be, np );
516         }
517
518         if( p != NULL ) {
519                 /* free parent and writer lock */
520                 bdb_entry_return( be, p );
521         }
522
523         /* free entry */
524         if( e != NULL ) {
525                 bdb_entry_return( be, e );
526         }
527
528         if( ltid != NULL ) {
529                 txn_abort( ltid );
530                 op->o_private = NULL;
531         }
532
533         return rc;
534 }