]> git.sur5r.net Git - openldap/blob - servers/slapd/back-ndb/modrdn.cpp
MySQL NDB Cluster backend (experimental)
[openldap] / servers / slapd / back-ndb / modrdn.cpp
1 /* modrdn.cpp - ndb backend modrdn routine */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2008 The OpenLDAP Foundation.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted only as authorized by the OpenLDAP
10  * Public License.
11  *
12  * A copy of this license is available in the file LICENSE in the
13  * top-level directory of the distribution or, alternatively, at
14  * <http://www.OpenLDAP.org/license.html>.
15  */
16 /* ACKNOWLEDGEMENTS:
17  * This work was initially developed by Howard Chu for inclusion
18  * in OpenLDAP Software. This work was sponsored by MySQL.
19  */
20
21 #include "portable.h"
22
23 #include <stdio.h>
24 #include <ac/string.h>
25
26 #include "back-ndb.h"
27
28 int
29 ndb_back_modrdn( Operation *op, SlapReply *rs )
30 {
31         struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
32         AttributeDescription *children = slap_schema.si_ad_children;
33         AttributeDescription *entry = slap_schema.si_ad_entry;
34         struct berval   new_dn = BER_BVNULL, new_ndn = BER_BVNULL;
35         Entry           e = {0};
36         Entry           e2 = {0};
37         char textbuf[SLAP_TEXT_BUFLEN];
38         size_t textlen = sizeof textbuf;
39
40         struct berval   *np_dn = NULL;                  /* newSuperior dn */
41         struct berval   *np_ndn = NULL;                 /* newSuperior ndn */
42
43         int             manageDSAit = get_manageDSAit( op );
44         int             num_retries = 0;
45
46         NdbArgs NA, NA2;
47         NdbRdns rdns, rdn2;
48         struct berval matched;
49
50         LDAPControl **preread_ctrl = NULL;
51         LDAPControl **postread_ctrl = NULL;
52         LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
53         int num_ctrls = 0;
54
55         int     rc;
56
57         Debug( LDAP_DEBUG_ARGS, "==>" LDAP_XSTRING(ndb_back_modrdn) "(%s,%s,%s)\n",
58                 op->o_req_dn.bv_val,op->oq_modrdn.rs_newrdn.bv_val,
59                 op->oq_modrdn.rs_newSup ? op->oq_modrdn.rs_newSup->bv_val : "NULL" );
60
61         ctrls[num_ctrls] = NULL;
62
63         slap_mods_opattrs( op, &op->orr_modlist, 1 );
64
65         e.e_name = op->o_req_dn;
66         e.e_nname = op->o_req_ndn;
67
68         /* Get our NDB handle */
69         rs->sr_err = ndb_thread_handle( op, &NA.ndb );
70         rdns.nr_num = 0;
71         NA.rdns = &rdns;
72         NA.e = &e;
73         NA2.ndb = NA.ndb;
74         NA2.e = &e2;
75         NA2.rdns = &rdn2;
76
77         if( 0 ) {
78 retry:  /* transaction retry */
79                 NA.txn->close();
80                 NA.txn = NULL;
81                 if ( e.e_attrs ) {
82                         attrs_free( e.e_attrs );
83                         e.e_attrs = NULL;
84                 }
85                 Debug( LDAP_DEBUG_TRACE, "==>" LDAP_XSTRING(ndb_back_modrdn)
86                                 ": retrying...\n", 0, 0, 0 );
87                 if ( op->o_abandon ) {
88                         rs->sr_err = SLAPD_ABANDON;
89                         goto return_results;
90                 }
91                 if ( NA.ocs ) {
92                         ber_bvarray_free( NA.ocs );
93                 }
94                 ndb_trans_backoff( ++num_retries );
95         }
96         NA.ocs = NULL;
97
98         /* begin transaction */
99         NA.txn = NA.ndb->startTransaction();
100         rs->sr_text = NULL;
101         if( !NA.txn ) {
102                 Debug( LDAP_DEBUG_TRACE,
103                         LDAP_XSTRING(ndb_back_modrdn) ": startTransaction failed: %s (%d)\n",
104                         NA.ndb->getNdbError().message, NA.ndb->getNdbError().code, 0 );
105                 rs->sr_err = LDAP_OTHER;
106                 rs->sr_text = "internal error";
107                 goto return_results;
108         }
109         NA2.txn = NA.txn;
110
111         /* get entry */
112         rs->sr_err = ndb_entry_get_info( op->o_bd, &NA, 1, &matched );
113         switch( rs->sr_err ) {
114         case 0:
115                 break;
116         case LDAP_NO_SUCH_OBJECT:
117                 Debug( LDAP_DEBUG_ARGS,
118                         "<=- ndb_back_modrdn: no such object %s\n",
119                         op->o_req_dn.bv_val, 0, 0 );
120                 rs->sr_matched = matched.bv_val;
121                 goto return_results;
122 #if 0
123         case DB_LOCK_DEADLOCK:
124         case DB_LOCK_NOTGRANTED:
125                 goto retry;
126 #endif
127         case LDAP_BUSY:
128                 rs->sr_text = "ldap server busy";
129                 goto return_results;
130         default:
131                 rs->sr_err = LDAP_OTHER;
132                 rs->sr_text = "internal error";
133                 goto return_results;
134         }
135
136         /* acquire and lock entry */
137         rs->sr_err = ndb_entry_get_data( op->o_bd, &NA, 1 );
138
139         if ( get_assert( op ) &&
140                 ( test_filter( op, &e, (Filter *)get_assertion( op )) != LDAP_COMPARE_TRUE ))
141         {
142                 rs->sr_err = LDAP_ASSERTION_FAILED;
143                 goto return_results;
144         }
145
146         /* check write on old entry */
147         rs->sr_err = access_allowed( op, &e, entry, NULL, ACL_WRITE, NULL );
148         if ( ! rs->sr_err ) {
149                 Debug( LDAP_DEBUG_TRACE, "no access to entry\n", 0,
150                         0, 0 );
151                 rs->sr_text = "no write access to old entry";
152                 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
153                 goto return_results;
154         }
155
156         /* Can't do it if we have kids */
157         rs->sr_err = ndb_has_children( &NA, &rc );
158         if ( rs->sr_err ) {
159                 Debug(LDAP_DEBUG_ARGS,
160                         "<=- " LDAP_XSTRING(ndb_back_modrdn)
161                         ": has_children failed: %s (%d)\n",
162                         NA.txn->getNdbError().message, NA.txn->getNdbError().code, 0 );
163                 rs->sr_err = LDAP_OTHER;
164                 rs->sr_text = "internal error";
165                 goto return_results;
166         }
167         if ( rc == LDAP_COMPARE_TRUE ) {
168                 Debug(LDAP_DEBUG_ARGS,
169                         "<=- " LDAP_XSTRING(ndb_back_modrdn)
170                         ": non-leaf %s\n",
171                         op->o_req_dn.bv_val, 0, 0);
172                 rs->sr_err = LDAP_NOT_ALLOWED_ON_NONLEAF;
173                 rs->sr_text = "subtree rename not supported";
174                 goto return_results;
175         }
176
177         if (!manageDSAit && is_entry_referral( &e ) ) {
178                 /* entry is a referral, don't allow modrdn */
179                 rs->sr_ref = get_entry_referrals( op, &e );
180
181                 Debug( LDAP_DEBUG_TRACE, LDAP_XSTRING(ndb_back_modrdn)
182                         ": entry %s is referral\n", e.e_dn, 0, 0 );
183
184                 rs->sr_err = LDAP_REFERRAL,
185                 rs->sr_matched = op->o_req_dn.bv_val;
186                 rs->sr_flags = REP_REF_MUSTBEFREED;
187                 goto return_results;
188         }
189
190         if ( be_issuffix( op->o_bd, &e.e_nname ) ) {
191                 /* There can only be one suffix entry */
192                 rs->sr_err = LDAP_NAMING_VIOLATION;
193                 rs->sr_text = "cannot rename suffix entry";
194                 goto return_results;
195         } else {
196                 dnParent( &e.e_nname, &e2.e_nname );
197                 dnParent( &e.e_name, &e2.e_name );
198         }
199
200         /* check parent for "children" acl */
201         rs->sr_err = access_allowed( op, &e2,
202                 children, NULL,
203                 op->oq_modrdn.rs_newSup == NULL ?
204                         ACL_WRITE : ACL_WDEL,
205                 NULL );
206
207         if ( ! rs->sr_err ) {
208                 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
209                 Debug( LDAP_DEBUG_TRACE, "no access to parent\n", 0,
210                         0, 0 );
211                 rs->sr_text = "no write access to old parent's children";
212                 goto return_results;
213         }
214
215         Debug( LDAP_DEBUG_TRACE,
216                 LDAP_XSTRING(ndb_back_modrdn) ": wr to children "
217                 "of entry %s OK\n", e2.e_name.bv_val, 0, 0 );
218         
219         if ( op->oq_modrdn.rs_newSup != NULL ) {
220                 Debug( LDAP_DEBUG_TRACE, 
221                         LDAP_XSTRING(ndb_back_modrdn)
222                         ": new parent \"%s\" requested...\n",
223                         op->oq_modrdn.rs_newSup->bv_val, 0, 0 );
224
225                 /*  newSuperior == oldParent? */
226                 if( dn_match( &e2.e_nname, op->oq_modrdn.rs_nnewSup ) ) {
227                         Debug( LDAP_DEBUG_TRACE, "bdb_back_modrdn: "
228                                 "new parent \"%s\" same as the old parent \"%s\"\n",
229                                 op->oq_modrdn.rs_newSup->bv_val, e2.e_name.bv_val, 0 );
230                         op->oq_modrdn.rs_newSup = NULL; /* ignore newSuperior */
231                 }
232         }
233
234         if ( op->oq_modrdn.rs_newSup != NULL ) {
235                 if ( op->oq_modrdn.rs_newSup->bv_len ) {
236                         rdn2.nr_num = 0;
237                         np_dn = op->oq_modrdn.rs_newSup;
238                         np_ndn = op->oq_modrdn.rs_nnewSup;
239
240                         /* newSuperior == oldParent? - checked above */
241                         /* newSuperior == entry being moved?, if so ==> ERROR */
242                         if ( dnIsSuffix( np_ndn, &e.e_nname )) {
243                                 rs->sr_err = LDAP_NO_SUCH_OBJECT;
244                                 rs->sr_text = "new superior not found";
245                                 goto return_results;
246                         }
247                         /* Get Entry with dn=newSuperior. Does newSuperior exist? */
248
249                         e2.e_name = *np_dn;
250                         e2.e_nname = *np_ndn;
251                         NA2.ocs = &matched;
252                         rs->sr_err = ndb_entry_get_info( op->o_bd, &NA2, 1, NULL );
253                         switch( rs->sr_err ) {
254                         case 0:
255                                 break;
256                         case LDAP_NO_SUCH_OBJECT:
257                                 Debug( LDAP_DEBUG_TRACE,
258                                         LDAP_XSTRING(ndb_back_modrdn)
259                                         ": newSup(ndn=%s) not here!\n",
260                                         np_ndn->bv_val, 0, 0);
261                                 rs->sr_text = "new superior not found";
262                                 goto return_results;
263 #if 0
264                         case DB_LOCK_DEADLOCK:
265                         case DB_LOCK_NOTGRANTED:
266                                 goto retry;
267 #endif
268                         case LDAP_BUSY:
269                                 rs->sr_text = "ldap server busy";
270                                 goto return_results;
271                         default:
272                                 rs->sr_err = LDAP_OTHER;
273                                 rs->sr_text = "internal error";
274                                 goto return_results;
275                         }
276                 }
277
278                 /* check newSuperior for "children" acl */
279                 rs->sr_err = access_allowed( op, &e2, children,
280                         NULL, ACL_WADD, NULL );
281                 if( ! rs->sr_err ) {
282                         Debug( LDAP_DEBUG_TRACE,
283                                 LDAP_XSTRING(ndb_back_modrdn)
284                                 ": no wr to newSup children\n",
285                                 0, 0, 0 );
286                         rs->sr_text = "no write access to new superior's children";
287                         rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
288                         goto return_results;
289                 }
290
291                 Debug( LDAP_DEBUG_TRACE,
292                         LDAP_XSTRING(ndb_back_modrdn)
293                         ": wr to new parent OK id=%ld\n",
294                         (long) e2.e_id, 0, 0 );
295         }
296
297         /* Build target dn and make sure target entry doesn't exist already. */
298         if (!new_dn.bv_val) {
299                 build_new_dn( &new_dn, &e2.e_name, &op->oq_modrdn.rs_newrdn, NULL ); 
300         }
301
302         if (!new_ndn.bv_val) {
303                 build_new_dn( &new_ndn, &e2.e_nname, &op->oq_modrdn.rs_nnewrdn, NULL ); 
304         }
305
306         Debug( LDAP_DEBUG_TRACE, LDAP_XSTRING(ndb_back_modrdn) ": new ndn=%s\n",
307                 new_ndn.bv_val, 0, 0 );
308
309         /* Allow rename to same DN */
310         if ( !bvmatch ( &new_ndn, &e.e_nname )) {
311                 rdn2.nr_num = 0;
312                 e2.e_name = new_dn;
313                 e2.e_nname = new_ndn;
314                 NA2.ocs = &matched;
315                 rs->sr_err = ndb_entry_get_info( op->o_bd, &NA2, 1, NULL );
316                 switch( rs->sr_err ) {
317 #if 0
318                 case DB_LOCK_DEADLOCK:
319                 case DB_LOCK_NOTGRANTED:
320                         goto retry;
321 #endif
322                 case LDAP_NO_SUCH_OBJECT:
323                         break;
324                 case 0:
325                         rs->sr_err = LDAP_ALREADY_EXISTS;
326                         goto return_results;
327                 default:
328                         rs->sr_err = LDAP_OTHER;
329                         rs->sr_text = "internal error";
330                         goto return_results;
331                 }
332         }
333
334         assert( op->orr_modlist != NULL );
335
336         if( op->o_preread ) {
337                 if( preread_ctrl == NULL ) {
338                         preread_ctrl = &ctrls[num_ctrls++];
339                         ctrls[num_ctrls] = NULL;
340                 }
341                 if( slap_read_controls( op, rs, &e,
342                         &slap_pre_read_bv, preread_ctrl ) )
343                 {
344                         Debug( LDAP_DEBUG_TRACE,        
345                                 "<=- " LDAP_XSTRING(ndb_back_modrdn)
346                                 ": pre-read failed!\n", 0, 0, 0 );
347                         if ( op->o_preread & SLAP_CONTROL_CRITICAL ) {
348                                 /* FIXME: is it correct to abort
349                                  * operation if control fails? */
350                                 goto return_results;
351                         }
352                 }                   
353         }
354
355         /* delete old DN */
356         rs->sr_err = ndb_entry_del_info( op->o_bd, &NA );
357         if ( rs->sr_err != 0 ) {
358                 Debug(LDAP_DEBUG_TRACE,
359                         "<=- " LDAP_XSTRING(ndb_back_modrdn)
360                         ": dn2id del failed: %s (%d)\n",
361                         NA.txn->getNdbError().message, NA.txn->getNdbError().code, 0 );
362 #if 0
363                 switch( rs->sr_err ) {
364                 case DB_LOCK_DEADLOCK:
365                 case DB_LOCK_NOTGRANTED:
366                         goto retry;
367                 }
368 #endif
369                 rs->sr_err = LDAP_OTHER;
370                 rs->sr_text = "DN index delete fail";
371                 goto return_results;
372         }
373
374         /* copy entry fields */
375         e2.e_attrs = e.e_attrs;
376         e2.e_id = e.e_id;
377
378         /* add new DN */
379         rs->sr_err = ndb_entry_put_info( op->o_bd, &NA2, 0 );
380         if ( rs->sr_err != 0 ) {
381                 Debug(LDAP_DEBUG_TRACE,
382                         "<=- " LDAP_XSTRING(ndb_back_modrdn)
383                         ": dn2id add failed: %s (%d)\n",
384                         NA.txn->getNdbError().message, NA.txn->getNdbError().code, 0 );
385 #if 0
386                 switch( rs->sr_err ) {
387                 case DB_LOCK_DEADLOCK:
388                 case DB_LOCK_NOTGRANTED:
389                         goto retry;
390                 }
391 #endif
392                 rs->sr_err = LDAP_OTHER;
393                 rs->sr_text = "DN index add failed";
394                 goto return_results;
395         }
396
397         /* modify entry */
398         rs->sr_err = ndb_modify_internal( op, &NA2,
399                 &rs->sr_text, textbuf, textlen );
400         if( rs->sr_err != LDAP_SUCCESS ) {
401                 Debug(LDAP_DEBUG_TRACE,
402                         "<=- " LDAP_XSTRING(ndb_back_modrdn)
403                         ": modify failed: %s (%d)\n",
404                         NA.txn->getNdbError().message, NA.txn->getNdbError().code, 0 );
405 #if 0
406                 switch( rs->sr_err ) {
407                 case DB_LOCK_DEADLOCK:
408                 case DB_LOCK_NOTGRANTED:
409                         goto retry;
410                 }
411 #endif
412                 goto return_results;
413         }
414
415         e.e_attrs = e2.e_attrs;
416
417         if( op->o_postread ) {
418                 if( postread_ctrl == NULL ) {
419                         postread_ctrl = &ctrls[num_ctrls++];
420                         ctrls[num_ctrls] = NULL;
421                 }
422                 if( slap_read_controls( op, rs, &e2,
423                         &slap_post_read_bv, postread_ctrl ) )
424                 {
425                         Debug( LDAP_DEBUG_TRACE,        
426                                 "<=- " LDAP_XSTRING(ndb_back_modrdn)
427                                 ": post-read failed!\n", 0, 0, 0 );
428                         if ( op->o_postread & SLAP_CONTROL_CRITICAL ) {
429                                 /* FIXME: is it correct to abort
430                                  * operation if control fails? */
431                                 goto return_results;
432                         }
433                 }                   
434         }
435
436         if( op->o_noop ) {
437                 if(( rs->sr_err = NA.txn->execute( Rollback )) != 0 ) {
438                         rs->sr_text = "txn_abort (no-op) failed";
439                 } else {
440                         rs->sr_err = LDAP_X_NO_OPERATION;
441                 }
442         } else {
443                 if(( rs->sr_err = NA.txn->execute( Commit )) != 0 ) {
444                         rs->sr_text = "txn_commit failed";
445                 } else {
446                         rs->sr_err = LDAP_SUCCESS;
447                 }
448         }
449  
450         if( rs->sr_err != LDAP_SUCCESS && rs->sr_err != LDAP_X_NO_OPERATION ) {
451                 Debug( LDAP_DEBUG_TRACE,
452                         LDAP_XSTRING(ndb_back_modrdn) ": txn_%s failed: %s (%d)\n",
453                         op->o_noop ? "abort (no-op)" : "commit",
454                         NA.txn->getNdbError().message, NA.txn->getNdbError().code );
455                 rs->sr_err = LDAP_OTHER;
456                 goto return_results;
457         }
458         NA.txn->close();
459         NA.txn = NULL;
460
461         Debug(LDAP_DEBUG_TRACE,
462                 LDAP_XSTRING(ndb_back_modrdn)
463                 ": rdn modified%s id=%08lx dn=\"%s\"\n",
464                 op->o_noop ? " (no-op)" : "",
465                 e.e_id, op->o_req_dn.bv_val );
466
467         rs->sr_err = LDAP_SUCCESS;
468         rs->sr_text = NULL;
469         if( num_ctrls ) rs->sr_ctrls = ctrls;
470
471 return_results:
472         if ( NA.ocs ) {
473                 ber_bvarray_free( NA.ocs );
474                 NA.ocs = NULL;
475         }
476
477         if ( e.e_attrs ) {
478                 attrs_free( e.e_attrs );
479                 e.e_attrs = NULL;
480         }
481
482         if( NA.txn != NULL ) {
483                 NA.txn->execute( Rollback );
484                 NA.txn->close();
485         }
486
487         send_ldap_result( op, rs );
488         slap_graduate_commit_csn( op );
489
490         if( new_dn.bv_val != NULL ) free( new_dn.bv_val );
491         if( new_ndn.bv_val != NULL ) free( new_ndn.bv_val );
492
493         if( preread_ctrl != NULL && (*preread_ctrl) != NULL ) {
494                 slap_sl_free( (*preread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
495                 slap_sl_free( *preread_ctrl, op->o_tmpmemctx );
496         }
497         if( postread_ctrl != NULL && (*postread_ctrl) != NULL ) {
498                 slap_sl_free( (*postread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
499                 slap_sl_free( *postread_ctrl, op->o_tmpmemctx );
500         }
501
502         rs->sr_text = NULL;
503         return rs->sr_err;
504 }