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