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