]> git.sur5r.net Git - openldap/blob - servers/slapd/back-ndb/modrdn.cpp
Happy new year (belated)
[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-2014 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 ( NA2.ocs ) {
92                         ber_bvarray_free_x( NA2.ocs, op->o_tmpmemctx );
93                 }
94                 if ( NA.ocs ) {
95                         ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx );
96                 }
97                 ndb_trans_backoff( ++num_retries );
98         }
99         NA.ocs = NULL;
100         NA2.ocs = NULL;
101
102         /* begin transaction */
103         NA.txn = NA.ndb->startTransaction();
104         rs->sr_text = NULL;
105         if( !NA.txn ) {
106                 Debug( LDAP_DEBUG_TRACE,
107                         LDAP_XSTRING(ndb_back_modrdn) ": startTransaction failed: %s (%d)\n",
108                         NA.ndb->getNdbError().message, NA.ndb->getNdbError().code, 0 );
109                 rs->sr_err = LDAP_OTHER;
110                 rs->sr_text = "internal error";
111                 goto return_results;
112         }
113         NA2.txn = NA.txn;
114
115         /* get entry */
116         rs->sr_err = ndb_entry_get_info( op, &NA, 1, &matched );
117         switch( rs->sr_err ) {
118         case 0:
119                 break;
120         case LDAP_NO_SUCH_OBJECT:
121                 Debug( LDAP_DEBUG_ARGS,
122                         "<=- ndb_back_modrdn: no such object %s\n",
123                         op->o_req_dn.bv_val, 0, 0 );
124                 rs->sr_matched = matched.bv_val;
125                 if ( NA.ocs )
126                         ndb_check_referral( op, rs, &NA );
127                 goto return_results;
128 #if 0
129         case DB_LOCK_DEADLOCK:
130         case DB_LOCK_NOTGRANTED:
131                 goto retry;
132 #endif
133         case LDAP_BUSY:
134                 rs->sr_text = "ldap server busy";
135                 goto return_results;
136         default:
137                 rs->sr_err = LDAP_OTHER;
138                 rs->sr_text = "internal error";
139                 goto return_results;
140         }
141
142         /* acquire and lock entry */
143         rs->sr_err = ndb_entry_get_data( op, &NA, 1 );
144         if ( rs->sr_err )
145                 goto return_results;
146
147         if ( !manageDSAit && is_entry_glue( &e )) {
148                 rs->sr_err = LDAP_NO_SUCH_OBJECT;
149                 goto return_results;
150         }
151         
152         if ( get_assert( op ) &&
153                 ( test_filter( op, &e, (Filter *)get_assertion( op )) != LDAP_COMPARE_TRUE ))
154         {
155                 rs->sr_err = LDAP_ASSERTION_FAILED;
156                 goto return_results;
157         }
158
159         /* check write on old entry */
160         rs->sr_err = access_allowed( op, &e, entry, NULL, ACL_WRITE, NULL );
161         if ( ! rs->sr_err ) {
162                 Debug( LDAP_DEBUG_TRACE, "no access to entry\n", 0,
163                         0, 0 );
164                 rs->sr_text = "no write access to old entry";
165                 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
166                 goto return_results;
167         }
168
169         /* Can't do it if we have kids */
170         rs->sr_err = ndb_has_children( &NA, &rc );
171         if ( rs->sr_err ) {
172                 Debug(LDAP_DEBUG_ARGS,
173                         "<=- " LDAP_XSTRING(ndb_back_modrdn)
174                         ": has_children failed: %s (%d)\n",
175                         NA.txn->getNdbError().message, NA.txn->getNdbError().code, 0 );
176                 rs->sr_err = LDAP_OTHER;
177                 rs->sr_text = "internal error";
178                 goto return_results;
179         }
180         if ( rc == LDAP_COMPARE_TRUE ) {
181                 Debug(LDAP_DEBUG_ARGS,
182                         "<=- " LDAP_XSTRING(ndb_back_modrdn)
183                         ": non-leaf %s\n",
184                         op->o_req_dn.bv_val, 0, 0);
185                 rs->sr_err = LDAP_NOT_ALLOWED_ON_NONLEAF;
186                 rs->sr_text = "subtree rename not supported";
187                 goto return_results;
188         }
189
190         if (!manageDSAit && is_entry_referral( &e ) ) {
191                 /* entry is a referral, don't allow modrdn */
192                 rs->sr_ref = get_entry_referrals( op, &e );
193
194                 Debug( LDAP_DEBUG_TRACE, LDAP_XSTRING(ndb_back_modrdn)
195                         ": entry %s is referral\n", e.e_dn, 0, 0 );
196
197                 rs->sr_err = LDAP_REFERRAL,
198                 rs->sr_matched = op->o_req_dn.bv_val;
199                 rs->sr_flags = REP_REF_MUSTBEFREED;
200                 goto return_results;
201         }
202
203         if ( be_issuffix( op->o_bd, &e.e_nname ) ) {
204                 /* There can only be one suffix entry */
205                 rs->sr_err = LDAP_NAMING_VIOLATION;
206                 rs->sr_text = "cannot rename suffix entry";
207                 goto return_results;
208         } else {
209                 dnParent( &e.e_nname, &e2.e_nname );
210                 dnParent( &e.e_name, &e2.e_name );
211         }
212
213         /* check parent for "children" acl */
214         rs->sr_err = access_allowed( op, &e2,
215                 children, NULL,
216                 op->oq_modrdn.rs_newSup == NULL ?
217                         ACL_WRITE : ACL_WDEL,
218                 NULL );
219
220         if ( ! rs->sr_err ) {
221                 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
222                 Debug( LDAP_DEBUG_TRACE, "no access to parent\n", 0,
223                         0, 0 );
224                 rs->sr_text = "no write access to old parent's children";
225                 goto return_results;
226         }
227
228         Debug( LDAP_DEBUG_TRACE,
229                 LDAP_XSTRING(ndb_back_modrdn) ": wr to children "
230                 "of entry %s OK\n", e2.e_name.bv_val, 0, 0 );
231         
232         if ( op->oq_modrdn.rs_newSup != NULL ) {
233                 Debug( LDAP_DEBUG_TRACE, 
234                         LDAP_XSTRING(ndb_back_modrdn)
235                         ": new parent \"%s\" requested...\n",
236                         op->oq_modrdn.rs_newSup->bv_val, 0, 0 );
237
238                 /*  newSuperior == oldParent? */
239                 if( dn_match( &e2.e_nname, op->oq_modrdn.rs_nnewSup ) ) {
240                         Debug( LDAP_DEBUG_TRACE, "bdb_back_modrdn: "
241                                 "new parent \"%s\" same as the old parent \"%s\"\n",
242                                 op->oq_modrdn.rs_newSup->bv_val, e2.e_name.bv_val, 0 );
243                         op->oq_modrdn.rs_newSup = NULL; /* ignore newSuperior */
244                 }
245         }
246
247         if ( op->oq_modrdn.rs_newSup != NULL ) {
248                 if ( op->oq_modrdn.rs_newSup->bv_len ) {
249                         rdn2.nr_num = 0;
250                         np_dn = op->oq_modrdn.rs_newSup;
251                         np_ndn = op->oq_modrdn.rs_nnewSup;
252
253                         /* newSuperior == oldParent? - checked above */
254                         /* newSuperior == entry being moved?, if so ==> ERROR */
255                         if ( dnIsSuffix( np_ndn, &e.e_nname )) {
256                                 rs->sr_err = LDAP_NO_SUCH_OBJECT;
257                                 rs->sr_text = "new superior not found";
258                                 goto return_results;
259                         }
260                         /* Get Entry with dn=newSuperior. Does newSuperior exist? */
261
262                         e2.e_name = *np_dn;
263                         e2.e_nname = *np_ndn;
264                         rs->sr_err = ndb_entry_get_info( op, &NA2, 1, NULL );
265                         switch( rs->sr_err ) {
266                         case 0:
267                                 break;
268                         case LDAP_NO_SUCH_OBJECT:
269                                 Debug( LDAP_DEBUG_TRACE,
270                                         LDAP_XSTRING(ndb_back_modrdn)
271                                         ": newSup(ndn=%s) not here!\n",
272                                         np_ndn->bv_val, 0, 0);
273                                 rs->sr_text = "new superior not found";
274                                 goto return_results;
275 #if 0
276                         case DB_LOCK_DEADLOCK:
277                         case DB_LOCK_NOTGRANTED:
278                                 goto retry;
279 #endif
280                         case LDAP_BUSY:
281                                 rs->sr_text = "ldap server busy";
282                                 goto return_results;
283                         default:
284                                 rs->sr_err = LDAP_OTHER;
285                                 rs->sr_text = "internal error";
286                                 goto return_results;
287                         }
288                         if ( NA2.ocs ) {
289                                 Attribute a;
290                                 int i;
291
292                                 for ( i=0; !BER_BVISNULL( &NA2.ocs[i] ); i++);
293                                 a.a_numvals = i;
294                                 a.a_desc = slap_schema.si_ad_objectClass;
295                                 a.a_vals = NA2.ocs;
296                                 a.a_nvals = NA2.ocs;
297                                 a.a_next = NULL;
298                                 e2.e_attrs = &a;
299
300                                 if ( is_entry_alias( &e2 )) {
301                                         /* parent is an alias, don't allow move */
302                                         Debug( LDAP_DEBUG_TRACE,
303                                                 LDAP_XSTRING(ndb_back_modrdn)
304                                                 ": entry is alias\n",
305                                                 0, 0, 0 );
306                                         rs->sr_text = "new superior is an alias";
307                                         rs->sr_err = LDAP_ALIAS_PROBLEM;
308                                         goto return_results;
309                                 }
310
311                                 if ( is_entry_referral( &e2 ) ) {
312                                         /* parent is a referral, don't allow move */
313                                         Debug( LDAP_DEBUG_TRACE,
314                                                 LDAP_XSTRING(ndb_back_modrdn)
315                                                 ": entry is referral\n",
316                                                 0, 0, 0 );
317                                         rs->sr_text = "new superior is a referral";
318                                         rs->sr_err = LDAP_OTHER;
319                                         goto return_results;
320                                 }
321                         }
322                 }
323
324                 /* check newSuperior for "children" acl */
325                 rs->sr_err = access_allowed( op, &e2, children,
326                         NULL, ACL_WADD, NULL );
327                 if( ! rs->sr_err ) {
328                         Debug( LDAP_DEBUG_TRACE,
329                                 LDAP_XSTRING(ndb_back_modrdn)
330                                 ": no wr to newSup children\n",
331                                 0, 0, 0 );
332                         rs->sr_text = "no write access to new superior's children";
333                         rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
334                         goto return_results;
335                 }
336
337                 Debug( LDAP_DEBUG_TRACE,
338                         LDAP_XSTRING(ndb_back_modrdn)
339                         ": wr to new parent OK id=%ld\n",
340                         (long) e2.e_id, 0, 0 );
341         }
342
343         /* Build target dn and make sure target entry doesn't exist already. */
344         if (!new_dn.bv_val) {
345                 build_new_dn( &new_dn, &e2.e_name, &op->oq_modrdn.rs_newrdn, NULL ); 
346         }
347
348         if (!new_ndn.bv_val) {
349                 build_new_dn( &new_ndn, &e2.e_nname, &op->oq_modrdn.rs_nnewrdn, NULL ); 
350         }
351
352         Debug( LDAP_DEBUG_TRACE, LDAP_XSTRING(ndb_back_modrdn) ": new ndn=%s\n",
353                 new_ndn.bv_val, 0, 0 );
354
355         /* Allow rename to same DN */
356         if ( !bvmatch ( &new_ndn, &e.e_nname )) {
357                 rdn2.nr_num = 0;
358                 e2.e_name = new_dn;
359                 e2.e_nname = new_ndn;
360                 NA2.ocs = &matched;
361                 rs->sr_err = ndb_entry_get_info( op, &NA2, 1, NULL );
362                 NA2.ocs = NULL;
363                 switch( rs->sr_err ) {
364 #if 0
365                 case DB_LOCK_DEADLOCK:
366                 case DB_LOCK_NOTGRANTED:
367                         goto retry;
368 #endif
369                 case LDAP_NO_SUCH_OBJECT:
370                         break;
371                 case 0:
372                         rs->sr_err = LDAP_ALREADY_EXISTS;
373                         goto return_results;
374                 default:
375                         rs->sr_err = LDAP_OTHER;
376                         rs->sr_text = "internal error";
377                         goto return_results;
378                 }
379         }
380
381         assert( op->orr_modlist != NULL );
382
383         if( op->o_preread ) {
384                 if( preread_ctrl == NULL ) {
385                         preread_ctrl = &ctrls[num_ctrls++];
386                         ctrls[num_ctrls] = NULL;
387                 }
388                 if( slap_read_controls( op, rs, &e,
389                         &slap_pre_read_bv, preread_ctrl ) )
390                 {
391                         Debug( LDAP_DEBUG_TRACE,        
392                                 "<=- " LDAP_XSTRING(ndb_back_modrdn)
393                                 ": pre-read failed!\n", 0, 0, 0 );
394                         if ( op->o_preread & SLAP_CONTROL_CRITICAL ) {
395                                 /* FIXME: is it correct to abort
396                                  * operation if control fails? */
397                                 goto return_results;
398                         }
399                 }                   
400         }
401
402         /* delete old DN */
403         rs->sr_err = ndb_entry_del_info( op->o_bd, &NA );
404         if ( rs->sr_err != 0 ) {
405                 Debug(LDAP_DEBUG_TRACE,
406                         "<=- " LDAP_XSTRING(ndb_back_modrdn)
407                         ": dn2id del failed: %s (%d)\n",
408                         NA.txn->getNdbError().message, NA.txn->getNdbError().code, 0 );
409 #if 0
410                 switch( rs->sr_err ) {
411                 case DB_LOCK_DEADLOCK:
412                 case DB_LOCK_NOTGRANTED:
413                         goto retry;
414                 }
415 #endif
416                 rs->sr_err = LDAP_OTHER;
417                 rs->sr_text = "DN index delete fail";
418                 goto return_results;
419         }
420
421         /* copy entry fields */
422         e2.e_attrs = e.e_attrs;
423         e2.e_id = e.e_id;
424
425         /* add new DN */
426         rs->sr_err = ndb_entry_put_info( op->o_bd, &NA2, 0 );
427         if ( rs->sr_err != 0 ) {
428                 Debug(LDAP_DEBUG_TRACE,
429                         "<=- " LDAP_XSTRING(ndb_back_modrdn)
430                         ": dn2id add failed: %s (%d)\n",
431                         NA.txn->getNdbError().message, NA.txn->getNdbError().code, 0 );
432 #if 0
433                 switch( rs->sr_err ) {
434                 case DB_LOCK_DEADLOCK:
435                 case DB_LOCK_NOTGRANTED:
436                         goto retry;
437                 }
438 #endif
439                 rs->sr_err = LDAP_OTHER;
440                 rs->sr_text = "DN index add failed";
441                 goto return_results;
442         }
443
444         /* modify entry */
445         rs->sr_err = ndb_modify_internal( op, &NA2,
446                 &rs->sr_text, textbuf, textlen );
447         if( rs->sr_err != LDAP_SUCCESS ) {
448                 Debug(LDAP_DEBUG_TRACE,
449                         "<=- " LDAP_XSTRING(ndb_back_modrdn)
450                         ": modify failed: %s (%d)\n",
451                         NA.txn->getNdbError().message, NA.txn->getNdbError().code, 0 );
452 #if 0
453                 switch( rs->sr_err ) {
454                 case DB_LOCK_DEADLOCK:
455                 case DB_LOCK_NOTGRANTED:
456                         goto retry;
457                 }
458 #endif
459                 goto return_results;
460         }
461
462         e.e_attrs = e2.e_attrs;
463
464         if( op->o_postread ) {
465                 if( postread_ctrl == NULL ) {
466                         postread_ctrl = &ctrls[num_ctrls++];
467                         ctrls[num_ctrls] = NULL;
468                 }
469                 if( slap_read_controls( op, rs, &e2,
470                         &slap_post_read_bv, postread_ctrl ) )
471                 {
472                         Debug( LDAP_DEBUG_TRACE,        
473                                 "<=- " LDAP_XSTRING(ndb_back_modrdn)
474                                 ": post-read failed!\n", 0, 0, 0 );
475                         if ( op->o_postread & SLAP_CONTROL_CRITICAL ) {
476                                 /* FIXME: is it correct to abort
477                                  * operation if control fails? */
478                                 goto return_results;
479                         }
480                 }                   
481         }
482
483         if( op->o_noop ) {
484                 if (( rs->sr_err=NA.txn->execute( NdbTransaction::Rollback,
485                         NdbOperation::AbortOnError, 1 )) != 0 ) {
486                         rs->sr_text = "txn_abort (no-op) failed";
487                 } else {
488                         rs->sr_err = LDAP_X_NO_OPERATION;
489                 }
490         } else {
491                 if (( rs->sr_err=NA.txn->execute( NdbTransaction::Commit,
492                         NdbOperation::AbortOnError, 1 )) != 0 ) {
493                         rs->sr_text = "txn_commit failed";
494                 } else {
495                         rs->sr_err = LDAP_SUCCESS;
496                 }
497         }
498  
499         if( rs->sr_err != LDAP_SUCCESS && rs->sr_err != LDAP_X_NO_OPERATION ) {
500                 Debug( LDAP_DEBUG_TRACE,
501                         LDAP_XSTRING(ndb_back_modrdn) ": txn_%s failed: %s (%d)\n",
502                         op->o_noop ? "abort (no-op)" : "commit",
503                         NA.txn->getNdbError().message, NA.txn->getNdbError().code );
504                 rs->sr_err = LDAP_OTHER;
505                 goto return_results;
506         }
507         NA.txn->close();
508         NA.txn = NULL;
509
510         Debug(LDAP_DEBUG_TRACE,
511                 LDAP_XSTRING(ndb_back_modrdn)
512                 ": rdn modified%s id=%08lx dn=\"%s\"\n",
513                 op->o_noop ? " (no-op)" : "",
514                 e.e_id, op->o_req_dn.bv_val );
515
516         rs->sr_err = LDAP_SUCCESS;
517         rs->sr_text = NULL;
518         if( num_ctrls ) rs->sr_ctrls = ctrls;
519
520 return_results:
521         if ( NA2.ocs ) {
522                 ber_bvarray_free_x( NA2.ocs, op->o_tmpmemctx );
523                 NA2.ocs = NULL;
524         }
525
526         if ( NA.ocs ) {
527                 ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx );
528                 NA.ocs = NULL;
529         }
530
531         if ( e.e_attrs ) {
532                 attrs_free( e.e_attrs );
533                 e.e_attrs = NULL;
534         }
535
536         if( NA.txn != NULL ) {
537                 NA.txn->execute( Rollback );
538                 NA.txn->close();
539         }
540
541         send_ldap_result( op, rs );
542         slap_graduate_commit_csn( op );
543
544         if( new_dn.bv_val != NULL ) free( new_dn.bv_val );
545         if( new_ndn.bv_val != NULL ) free( new_ndn.bv_val );
546
547         if( preread_ctrl != NULL && (*preread_ctrl) != NULL ) {
548                 slap_sl_free( (*preread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
549                 slap_sl_free( *preread_ctrl, op->o_tmpmemctx );
550         }
551         if( postread_ctrl != NULL && (*postread_ctrl) != NULL ) {
552                 slap_sl_free( (*postread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
553                 slap_sl_free( *postread_ctrl, op->o_tmpmemctx );
554         }
555
556         rs->sr_text = NULL;
557         return rs->sr_err;
558 }