]> git.sur5r.net Git - openldap/blob - servers/slapd/back-ldbm/modrdn.c
b0e4fe1a76d19f5c3f8e5fd201eb31ea37e3df92
[openldap] / servers / slapd / back-ldbm / modrdn.c
1 /* modrdn.c - ldbm backend modrdn routine */
2
3 /*
4  * LDAP v3 newSuperior support. Add new rdn as an attribute.
5  * (Full support for v2 also used software/ideas contributed
6  * by Roy Hooper rhooper@cyberus.ca, thanks to him for his
7  * submission!.)
8  *
9  * Copyright 1999, Juan C. Gomez, All rights reserved.
10  * This software is not subject to any license of Silicon Graphics 
11  * Inc. or Purdue University.
12  *
13  * Redistribution and use in source and binary forms are permitted
14  * without restriction or fee of any kind as long as this notice
15  * is preserved.
16  *
17  */
18
19 #include "portable.h"
20
21 #include <stdio.h>
22
23 #include <ac/string.h>
24 #include <ac/socket.h>
25
26 #include "slap.h"
27 #include "back-ldbm.h"
28 #include "proto-back-ldbm.h"
29
30 int
31 ldbm_back_modrdn(
32     Backend     *be,
33     Connection  *conn,
34     Operation   *op,
35     char        *dn,
36     char        *newrdn,
37     int         deleteoldrdn,
38     char        *newSuperior
39 )
40 {
41         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
42         char            *matched = NULL;
43         char            *p_dn = NULL, *p_ndn = NULL;
44         char            *new_dn = NULL, *new_ndn = NULL;
45         Entry           *e, *p = NULL;
46         int                     rootlock = 0;
47         int                     rc = -1;
48         /* Added to support LDAP v2 correctly (deleteoldrdn thing) */
49         char            *new_rdn_val = NULL;    /* Val of new rdn */
50         char            *new_rdn_type = NULL;   /* Type of new rdn */
51         char            *old_rdn;               /* Old rdn's attr type & val */
52         char            *old_rdn_type = NULL;   /* Type of old rdn attr. */
53         char            *old_rdn_val = NULL;    /* Old rdn attribute value */
54         struct berval   bv;                     /* Stores new rdn att */
55         struct berval   *bvals[2];              /* Stores new rdn att */
56         LDAPMod         mod;                    /* Used to delete old rdn */
57         /* Added to support newSuperior */ 
58         Entry           *np = NULL;     /* newSuperior Entry */
59         char            *np_dn = NULL;  /* newSuperior dn */
60         char            *np_ndn = NULL; /* newSuperior ndn */
61         char            *new_parent_dn = NULL;  /* np_dn, p_dn, or NULL */
62
63         Debug( LDAP_DEBUG_TRACE, "==>ldbm_back_modrdn(newSuperior=%s)\n",
64                (newSuperior ? newSuperior : "NULL"),
65                0, 0 );
66
67         /* get entry with writer lock */
68         if ( (e = dn2entry_w( be, dn, &matched )) == NULL ) {
69                 send_ldap_result( conn, op, LDAP_NO_SUCH_OBJECT, matched, "" );
70                 if ( matched != NULL ) {
71                         free( matched );
72                 }
73                 return( -1 );
74         }
75
76 #ifdef SLAPD_CHILD_MODIFICATION_WITH_ENTRY_ACL
77                 /* check parent for "children" acl */
78         if ( ! access_allowed( be, conn, op, e,
79                 "entry", NULL, ACL_WRITE ) )
80         {
81                 Debug( LDAP_DEBUG_TRACE, "no access to entry\n", 0,
82                         0, 0 );
83                 send_ldap_result( conn, op, LDAP_INSUFFICIENT_ACCESS,
84                         "", "" );
85                 goto return_results;
86         }
87 #endif
88
89         if ( (p_ndn = dn_parent( be, e->e_ndn )) != NULL ) {
90
91                 /* Make sure parent entry exist and we can write its 
92                  * children.
93                  */
94
95                 if( (p = dn2entry_w( be, p_ndn, &matched )) == NULL) {
96                         Debug( LDAP_DEBUG_TRACE, "parent does not exist\n",
97                                 0, 0, 0);
98                         send_ldap_result( conn, op, LDAP_OPERATIONS_ERROR,
99                                 "", "");
100                         goto return_results;
101                 }
102
103                 /* check parent for "children" acl */
104                 if ( ! access_allowed( be, conn, op, p,
105                         "children", NULL, ACL_WRITE ) )
106                 {
107                         Debug( LDAP_DEBUG_TRACE, "no access to parent\n", 0,
108                                 0, 0 );
109                         send_ldap_result( conn, op, LDAP_INSUFFICIENT_ACCESS,
110                                 "", "" );
111                         goto return_results;
112                 }
113
114                 Debug( LDAP_DEBUG_TRACE,
115                        "ldbm_back_modrdn: wr to children of entry %s OK\n",
116                        p_ndn, 0, 0 );
117                 
118                 p_dn = dn_parent( be, e->e_dn );
119         
120
121                 Debug( LDAP_DEBUG_TRACE, "ldbm_back_modrdn: parent dn=%s\n",
122                        p_dn, 0, 0 );
123
124         } else {
125                 /* no parent, modrdn entry directly under root */
126                 if( ! be_isroot( be, op->o_ndn ) ) {
127                         Debug( LDAP_DEBUG_TRACE, "no parent & not root\n",
128                                 0, 0, 0);
129                         send_ldap_result( conn, op, LDAP_INSUFFICIENT_ACCESS,
130                                 "", "");
131                         goto return_results;
132                 }
133
134                 ldap_pvt_thread_mutex_lock(&li->li_root_mutex);
135                 rootlock = 1;
136                 
137                 Debug( LDAP_DEBUG_TRACE,
138                        "ldbm_back_modrdn: no parent, locked root\n",
139                        0, 0, 0 );
140
141         }/* if ( (p_ndn = dn_parent( be, e->e_ndn )) != NULL ) else */
142
143         new_parent_dn = p_dn;   /* New Parent unless newSuperior given */
144
145         if ( (np_dn = newSuperior) != NULL) {
146
147
148                 Debug( LDAP_DEBUG_TRACE, 
149                        "ldbm_back_modrdn: new parent requested...\n",
150                        0, 0, 0 );
151
152                 np_ndn = dn_normalize_case( ch_strdup( np_dn ) );
153
154                 /* newSuperior == oldParent?, if so ==> ERROR */
155
156                 /* newSuperior == entry being moved?, if so ==> ERROR */
157
158                 /* Get Entry with dn=newSuperior. Does newSuperior exist? */
159
160                 if( (np = dn2entry_w( be, np_ndn, &matched )) == NULL) {
161
162                         Debug( LDAP_DEBUG_TRACE,
163                                "ldbm_back_modrdn: newSup(ndn=%s) not here!\n",
164                                np_ndn, 0, 0);
165                         send_ldap_result( conn, op, LDAP_OPERATIONS_ERROR, "",
166                                           "");
167                         goto return_results;
168                 }
169
170                 Debug( LDAP_DEBUG_TRACE,
171                        "ldbm_back_modrdn: wr to new parent OK np=%p, id=%d\n",
172                        np, np->e_id, 0 );
173             
174                 /* check newSuperior for "children" acl */
175                 if ( !access_allowed( be, conn, op, np, "children", NULL,
176                                       ACL_WRITE ) )
177                 {
178                         Debug( LDAP_DEBUG_TRACE,
179                                "ldbm_back_modrdn: no wr to newSup children\n",
180                                0, 0, 0 );
181                         send_ldap_result( conn, op, LDAP_INSUFFICIENT_ACCESS,
182                                           "", "" );
183                         goto return_results;
184                 }
185
186                 Debug( LDAP_DEBUG_TRACE,
187                        "ldbm_back_modrdn: wr to new parent's children OK\n",
188                        0, 0 , 0 );
189
190
191                 new_parent_dn = np_dn;
192
193         }
194         
195         /* Build target dn and make sure target entry doesn't exist already. */
196
197         build_new_dn( &new_dn, e->e_dn, new_parent_dn, newrdn ); 
198
199
200         new_ndn = dn_normalize_case( ch_strdup(new_dn) );
201
202         Debug( LDAP_DEBUG_TRACE, "ldbm_back_modrdn: new ndn=%s\n",
203                new_ndn, 0, 0 );
204
205         if (dn2id ( be, new_ndn ) != NOID) {
206                 send_ldap_result( conn, op, LDAP_ALREADY_EXISTS, NULL, NULL );
207                 goto return_results;
208         }
209
210         Debug( LDAP_DEBUG_TRACE,
211                "ldbm_back_modrdn: new ndn=%s does not exist\n",
212                new_ndn, 0, 0 );
213
214         /* check for abandon */
215         ldap_pvt_thread_mutex_lock( &op->o_abandonmutex );
216         if ( op->o_abandon ) {
217                 ldap_pvt_thread_mutex_unlock( &op->o_abandonmutex );
218                 goto return_results;
219         }
220         ldap_pvt_thread_mutex_unlock( &op->o_abandonmutex );
221
222         /* delete old one */
223         if ( dn2id_delete( be, e->e_ndn ) != 0 ) {
224                 send_ldap_result( conn, op, LDAP_OPERATIONS_ERROR, NULL, NULL );
225                 goto return_results;
226         }
227
228         (void) cache_delete_entry( &li->li_cache, e );
229         free( e->e_dn );
230         free( e->e_ndn );
231         e->e_dn = new_dn;
232         e->e_ndn = new_ndn;
233
234         /* add new one */
235         if ( dn2id_add( be, e->e_ndn, e->e_id ) != 0 ) {
236                 send_ldap_result( conn, op, LDAP_OPERATIONS_ERROR, NULL, NULL );
237                 goto return_results;
238         }
239
240
241         /* Get attribute type and attribute value of our new rdn, we will
242          * need to add that to our new entry
243          */
244
245         if ( (new_rdn_type = rdn_attr_type( newrdn )) == NULL ) {
246             
247                 Debug( LDAP_DEBUG_TRACE,
248                        "ldbm_back_modrdn: can't figure out type of newrdn\n",
249                        0, 0, 0 );
250                 send_ldap_result( conn, op, LDAP_OPERATIONS_ERROR, "", "" );
251                 goto return_results;            
252
253         }
254
255         if ( (new_rdn_val = rdn_attr_value( newrdn )) == NULL ) {
256             
257                 Debug( LDAP_DEBUG_TRACE,
258                        "ldbm_back_modrdn: can't figure out val of newrdn\n",
259                        0, 0, 0 );
260                 send_ldap_result( conn, op, LDAP_OPERATIONS_ERROR, "", "" );
261                 goto return_results;            
262
263         }
264
265         Debug( LDAP_DEBUG_TRACE,
266                "ldbm_back_modrdn: new_rdn_val=\"%s\", new_rdn_type=\"%s\"\n",
267                new_rdn_val, new_rdn_type, 0 );
268
269         /* Retrieve the old rdn from the entry's dn */
270
271         if ( (old_rdn = dn_rdn( be, dn )) == NULL ) {
272
273                 Debug( LDAP_DEBUG_TRACE,
274                        "ldbm_back_modrdn: can't figure out old_rdn from dn\n",
275                        0, 0, 0 );
276                 send_ldap_result( conn, op, LDAP_OPERATIONS_ERROR, "", "" );
277                 goto return_results;            
278
279         }
280
281         if ( (old_rdn_type = rdn_attr_type( old_rdn )) == NULL ) {
282             
283                 Debug( LDAP_DEBUG_TRACE,
284                        "ldbm_back_modrdn: can't figure out the old_rdn type\n",
285                        0, 0, 0 );
286                 send_ldap_result( conn, op, LDAP_OPERATIONS_ERROR, "", "" );
287                 goto return_results;            
288                 
289         }
290         
291         if ( strcasecmp( old_rdn_type, new_rdn_type ) != 0 ) {
292
293             /* Not a big deal but we may say something */
294             Debug( LDAP_DEBUG_TRACE,
295                    "ldbm_back_modrdn: old_rdn_type=%s, new_rdn_type=%s!\n",
296                    old_rdn_type, new_rdn_type, 0 );
297             
298         }               
299
300         if ( dn_type( old_rdn ) == DN_X500 ) {
301
302                 Debug( LDAP_DEBUG_TRACE, "ldbm_back_modrdn: DN_X500\n",
303                        0, 0, 0 );
304
305                 bvals[0] = &bv;         /* Array of bervals */
306                 bvals[1] = NULL;
307                 
308
309                 /* Remove old rdn value if required */
310
311                 if (deleteoldrdn) {
312
313                         /* Get value of old rdn */
314         
315                         if ((old_rdn_val = rdn_attr_value( old_rdn ))
316                             == NULL) {
317                             
318                                 Debug( LDAP_DEBUG_TRACE,
319                                        "ldbm_back_modrdn: can't figure out old_rdn_val from old_rdn\n",
320                                        0, 0, 0 );
321                                 send_ldap_result( conn, op,
322                                                   LDAP_OPERATIONS_ERROR,
323                                                   "", "" );
324                                 goto return_results;            
325
326
327                         }
328
329                         /* Remove old value of rdn as an attribute. */
330                     
331                         bv.bv_val = old_rdn_val;
332                         bv.bv_len = strlen(old_rdn_val);
333
334                         /* No need to normalize old_rdn_type, delete_values()
335                          * does that for us
336                          */
337                         mod.mod_type = old_rdn_type;    
338                         mod.mod_bvalues = bvals;
339                         mod.mod_op = LDAP_MOD_DELETE;   /* XXX:really needed?*/
340
341                         /* Assembly mod structure */
342
343                         if ( delete_values( e, &mod, op->o_ndn )
344                              != LDAP_SUCCESS ) {
345
346                                 /* Could not find old_rdn as an attribute or
347                                  * the old value was not present. Return an 
348                                  * error.
349                                  */
350                                 Debug( LDAP_DEBUG_TRACE,
351                                        "ldbm_back_modrdn: old rdn not found or att type doesn't exist\n",
352                                        0, 0, 0);
353                                 send_ldap_result( conn, op,
354                                                   LDAP_OPERATIONS_ERROR,
355                                                   "", "");
356                                 goto return_results;
357
358                         }
359
360                         Debug( LDAP_DEBUG_TRACE,
361                                "ldbm_back_modrdn: removed old_rdn_val=%s\n",
362                                old_rdn_val, 0, 0 );
363                 
364                 }/* if (deleteoldrdn) */
365
366                 /* Add new attribute value to the entry.
367                  */
368
369                 bv.bv_val = new_rdn_val;
370                 bv.bv_len = strlen(new_rdn_val);
371                 
372                 
373                 Debug( LDAP_DEBUG_TRACE,
374                        "ldbm_back_modrdn: adding new rdn attr val =\"%s\"\n",
375                        new_rdn_val, 0, 0 );
376                 
377                 /* No need to normalize new_rdn_type, attr_merge does it */
378
379                 attr_merge( e, new_rdn_type, bvals );
380
381                 /* Update new_rdn_type if it is an index */
382
383                 index_add_values( be, new_rdn_type, bvals, e->e_id );
384         
385         } else {
386             
387
388                 Debug( LDAP_DEBUG_TRACE, "ldbm_back_modrdn: DNS DN\n",
389                        0, 0, 0 );
390                 /* XXXV3: not sure of what to do here */
391                 Debug( LDAP_DEBUG_TRACE,
392                        "ldbm_back_modrdn: not fully implemented...\n",
393                        0, 0, 0 );  
394
395         }
396
397         (void) cache_update_entry( &li->li_cache, e );
398
399         /* id2entry index */
400         if ( id2entry_add( be, e ) != 0 ) {
401                 entry_free( e );
402                 send_ldap_result( conn, op, LDAP_OPERATIONS_ERROR, "", "" );
403                 goto return_results;
404         }
405
406         send_ldap_result( conn, op, LDAP_SUCCESS, NULL, NULL );
407         rc = 0;
408
409 return_results:
410         /* NOTE:
411          * new_dn and new_ndn are not deallocated because they are used by
412          * the cache entry.
413          */
414         if( p_dn != NULL ) free( p_dn );
415         if( p_ndn != NULL ) free( p_ndn );
416
417         if( matched != NULL ) free( matched );
418
419         /* LDAP v2 supporting correct attribute handling. */
420         if( new_rdn_type != NULL ) free(new_rdn_type);
421         if( new_rdn_val != NULL ) free(new_rdn_val);
422         if( old_rdn != NULL ) free(old_rdn);
423         if( old_rdn_type != NULL ) free(old_rdn_type);
424         if( old_rdn_val != NULL ) free(old_rdn_val);
425
426
427         /* LDAP v3 Support */
428         if ( np_dn != NULL ) free( np_dn );
429         if ( np_ndn != NULL ) free( np_ndn );
430
431         if( p != NULL ) {
432                 /* free parent and writer lock */
433                 cache_return_entry_w( &li->li_cache, p );
434         }
435
436         if ( rootlock ) {
437                 /* release root writer lock */
438                 ldap_pvt_thread_mutex_unlock(&li->li_root_mutex);
439         }
440
441         /* free entry and writer lock */
442         cache_return_entry_w( &li->li_cache, e );
443         return( rc );
444 }