repl5_protocol_util.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583
  1. /** BEGIN COPYRIGHT BLOCK
  2. * Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
  3. * Copyright (C) 2005 Red Hat, Inc.
  4. * All rights reserved.
  5. * END COPYRIGHT BLOCK **/
  6. /* repl5_protocol_util.c */
  7. /*
  8. Code common to both incremental and total protocols.
  9. */
  10. #include "repl5.h"
  11. #include "repl5_prot_private.h"
  12. /*
  13. * Obtain a current CSN (e.g. one that would have been
  14. * generated for an operation occurring at this time)
  15. * for a given replica.
  16. */
  17. CSN *
  18. get_current_csn(Slapi_DN *replarea_sdn)
  19. {
  20. Object *replica_obj;
  21. Replica *replica;
  22. Object *gen_obj;
  23. CSNGen *gen;
  24. CSN *current_csn = NULL;
  25. if (NULL != replarea_sdn)
  26. {
  27. replica_obj = replica_get_replica_from_dn(replarea_sdn);
  28. if (NULL != replica_obj)
  29. {
  30. replica = object_get_data(replica_obj);
  31. if (NULL != replica)
  32. {
  33. gen_obj = replica_get_csngen(replica);
  34. if (NULL != gen_obj)
  35. {
  36. gen = (CSNGen *)object_get_data(gen_obj);
  37. if (NULL != gen)
  38. {
  39. if (csngen_new_csn(gen, &current_csn,
  40. PR_FALSE /* notify */) != CSN_SUCCESS)
  41. {
  42. current_csn = NULL;
  43. }
  44. object_release(gen_obj);
  45. }
  46. }
  47. }
  48. }
  49. }
  50. return current_csn;
  51. }
  52. /*
  53. * Acquire exclusive access to a replica. Send a start replication extended
  54. * operation to the replica. The response will contain a success code, and
  55. * optionally the replica's update vector if acquisition is successful.
  56. * This function returns one of the following:
  57. * ACQUIRE_SUCCESS - the replica was acquired, and we have exclusive update access
  58. * ACQUIRE_REPLICA_BUSY - another master was updating the replica
  59. * ACQUIRE_FATAL_ERROR - something bad happened, and it's not likely to improve
  60. * if we wait.
  61. * ACQUIRE_TRANSIENT_ERROR - something bad happened, but it's probably worth
  62. * another try after waiting a while.
  63. * If ACQUIRE_SUCCESS is returned, then ruv will point to the replica's update
  64. * vector. It's possible that the replica does something goofy and doesn't
  65. * return us an update vector, so be prepared for ruv to be NULL (but this is
  66. * an error).
  67. */
  68. int
  69. acquire_replica(Private_Repl_Protocol *prp, char *prot_oid, RUV **ruv)
  70. {
  71. int return_value;
  72. ConnResult crc;
  73. Repl_Connection *conn;
  74. PR_ASSERT(prp && prot_oid);
  75. if (prp->replica_acquired) /* we already acquire replica */
  76. {
  77. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
  78. "%s: Remote replica already acquired\n",
  79. agmt_get_long_name(prp->agmt));
  80. return_value = ACQUIRE_FATAL_ERROR;
  81. return ACQUIRE_SUCCESS;
  82. }
  83. if (NULL != ruv)
  84. {
  85. ruv_destroy ( ruv );
  86. }
  87. if (strcmp(prot_oid, REPL_NSDS50_INCREMENTAL_PROTOCOL_OID) == 0)
  88. {
  89. Replica *replica;
  90. Object *supl_ruv_obj, *cons_ruv_obj;
  91. PRBool is_newer = PR_FALSE;
  92. object_acquire(prp->replica_object);
  93. replica = object_get_data(prp->replica_object);
  94. supl_ruv_obj = replica_get_ruv ( replica );
  95. cons_ruv_obj = agmt_get_consumer_ruv ( prp->agmt );
  96. is_newer = ruv_is_newer ( supl_ruv_obj, cons_ruv_obj );
  97. if ( supl_ruv_obj ) object_release ( supl_ruv_obj );
  98. if ( cons_ruv_obj ) object_release ( cons_ruv_obj );
  99. object_release (prp->replica_object);
  100. replica = NULL;
  101. if (is_newer == PR_FALSE) {
  102. prp->last_acquire_response_code = NSDS50_REPL_UPTODATE;
  103. return ACQUIRE_CONSUMER_WAS_UPTODATE;
  104. }
  105. }
  106. prp->last_acquire_response_code = NSDS50_REPL_REPLICA_NO_RESPONSE;
  107. /* Get the connection */
  108. conn = prp->conn;
  109. crc = conn_connect(conn);
  110. if (CONN_OPERATION_FAILED == crc)
  111. {
  112. return_value = ACQUIRE_TRANSIENT_ERROR;
  113. }
  114. else if (CONN_SSL_NOT_ENABLED == crc)
  115. {
  116. return_value = ACQUIRE_FATAL_ERROR;
  117. }
  118. else
  119. {
  120. /* we don't want the timer to go off in the middle of an operation */
  121. conn_cancel_linger(conn);
  122. /* Does the remote replica support the 5.0 protocol? */
  123. crc = conn_replica_supports_ds5_repl(conn);
  124. if (CONN_DOES_NOT_SUPPORT_DS5_REPL == crc)
  125. {
  126. return_value = ACQUIRE_FATAL_ERROR;
  127. }
  128. else if (CONN_NOT_CONNECTED == crc || CONN_OPERATION_FAILED == crc)
  129. {
  130. /* We don't know anything about the remote replica. Try again later. */
  131. return_value = ACQUIRE_TRANSIENT_ERROR;
  132. }
  133. else
  134. {
  135. /* Does the remote replica support the 7.1 protocol? */
  136. crc = conn_replica_supports_ds71_repl(conn);
  137. if (CONN_DOES_NOT_SUPPORT_DS71_REPL == crc)
  138. {
  139. prp->repl50consumer = 1;
  140. }
  141. if (CONN_NOT_CONNECTED == crc || CONN_OPERATION_FAILED == crc)
  142. {
  143. /* We don't know anything about the remote replica. Try again later. */
  144. return_value = ACQUIRE_TRANSIENT_ERROR;
  145. } else
  146. {
  147. CSN *current_csn = NULL;
  148. struct berval *retdata = NULL;
  149. char *retoid = NULL;
  150. Slapi_DN *replarea_sdn;
  151. /* Check if this is a fractional agreement, we need to
  152. * verify that the consumer is read-only */
  153. if (agmt_is_fractional(prp->agmt)) {
  154. crc = conn_replica_is_readonly(conn);
  155. if (CONN_IS_NOT_READONLY == crc) {
  156. /* This is a fatal error */
  157. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
  158. "%s: Unable to acquire replica: "
  159. "the agreement is fractional but the replica is not read-only. Fractional agreements must specify a read-only replica "
  160. "Replication is aborting.\n",
  161. agmt_get_long_name(prp->agmt));
  162. return_value = ACQUIRE_FATAL_ERROR;
  163. goto error;
  164. }
  165. }
  166. /* Good to go. Start the protocol. */
  167. /* Obtain a current CSN */
  168. replarea_sdn = agmt_get_replarea(prp->agmt);
  169. current_csn = get_current_csn(replarea_sdn);
  170. if (NULL != current_csn)
  171. {
  172. struct berval *payload = NSDS50StartReplicationRequest_new(
  173. prot_oid, slapi_sdn_get_ndn(replarea_sdn),
  174. NULL /* XXXggood need to provide referral(s) */, current_csn);
  175. /* JCMREPL - Need to extract the referrals from the RUV */
  176. csn_free(&current_csn);
  177. current_csn = NULL;
  178. crc = conn_send_extended_operation(conn,
  179. REPL_START_NSDS50_REPLICATION_REQUEST_OID, payload, NULL /* update control */, NULL /* Message ID */);
  180. if (CONN_OPERATION_SUCCESS != crc)
  181. {
  182. int operation, error;
  183. conn_get_error(conn, &operation, &error);
  184. /* Couldn't send the extended operation */
  185. return_value = ACQUIRE_TRANSIENT_ERROR; /* XXX right return value? */
  186. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
  187. "%s: Unable to send a startReplication "
  188. "extended operation to consumer (%s). Will retry later.\n",
  189. agmt_get_long_name(prp->agmt),
  190. error ? ldap_err2string(error) : "unknown error");
  191. }
  192. /* Since the operation request is async, we need to wait for the response here */
  193. crc = conn_read_result_ex(conn,&retoid,&retdata,NULL,NULL,1);
  194. ber_bvfree(payload);
  195. payload = NULL;
  196. /* Look at the response we got. */
  197. if (CONN_OPERATION_SUCCESS == crc)
  198. {
  199. /*
  200. * Extop was processed. Look at extop response to see if we're
  201. * permitted to go ahead.
  202. */
  203. struct berval **ruv_bervals = NULL;
  204. int extop_result;
  205. int extop_rc = decode_repl_ext_response(retdata, &extop_result,
  206. &ruv_bervals);
  207. if (0 == extop_rc)
  208. {
  209. prp->last_acquire_response_code = extop_result;
  210. switch (extop_result)
  211. {
  212. /* XXXggood handle other error codes here */
  213. case NSDS50_REPL_INTERNAL_ERROR:
  214. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
  215. "%s: Unable to acquire replica: "
  216. "an internal error occurred on the remote replica. "
  217. "Replication is aborting.\n",
  218. agmt_get_long_name(prp->agmt));
  219. return_value = ACQUIRE_FATAL_ERROR;
  220. break;
  221. case NSDS50_REPL_PERMISSION_DENIED:
  222. /* Not allowed to send updates */
  223. {
  224. char *repl_binddn = agmt_get_binddn(prp->agmt);
  225. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
  226. "%s: Unable to acquire replica: permission denied. "
  227. "The bind dn \"%s\" does not have permission to "
  228. "supply replication updates to the replica. "
  229. "Will retry later.\n",
  230. agmt_get_long_name(prp->agmt), repl_binddn);
  231. slapi_ch_free((void **)&repl_binddn);
  232. return_value = ACQUIRE_TRANSIENT_ERROR;
  233. break;
  234. }
  235. case NSDS50_REPL_NO_SUCH_REPLICA:
  236. /* There is no such replica on the consumer */
  237. {
  238. Slapi_DN *repl_root = agmt_get_replarea(prp->agmt);
  239. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
  240. "%s: Unable to acquire replica: there is no "
  241. "replicated area \"%s\" on the consumer server. "
  242. "Replication is aborting.\n",
  243. agmt_get_long_name(prp->agmt),
  244. slapi_sdn_get_dn(repl_root));
  245. slapi_sdn_free(&repl_root);
  246. return_value = ACQUIRE_FATAL_ERROR;
  247. break;
  248. }
  249. case NSDS50_REPL_EXCESSIVE_CLOCK_SKEW:
  250. /* Large clock skew between the consumer and the supplier */
  251. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
  252. "%s: Unable to acquire replica: "
  253. "Excessive clock skew between the supplier and "
  254. "the consumer. Replication is aborting.\n",
  255. agmt_get_long_name(prp->agmt));
  256. return_value = ACQUIRE_FATAL_ERROR;
  257. break;
  258. case NSDS50_REPL_DECODING_ERROR:
  259. /* We sent something the replica couldn't understand. */
  260. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
  261. "%s: Unable to acquire replica: "
  262. "the consumer was unable to decode the "
  263. "startReplicationRequest extended operation sent by the "
  264. "supplier. Replication is aborting.\n",
  265. agmt_get_long_name(prp->agmt));
  266. return_value = ACQUIRE_FATAL_ERROR;
  267. break;
  268. case NSDS50_REPL_REPLICA_BUSY:
  269. /* Someone else is updating the replica. Try later. */
  270. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
  271. "%s: Unable to acquire replica: "
  272. "the replica is currently being updated"
  273. "by another supplier. Will try later\n",
  274. agmt_get_long_name(prp->agmt));
  275. return_value = ACQUIRE_REPLICA_BUSY;
  276. break;
  277. case NSDS50_REPL_LEGACY_CONSUMER:
  278. /* remote replica is a legacy consumer */
  279. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
  280. "%s: Unable to acquire replica: the replica "
  281. "is supplied by a legacy supplier. "
  282. "Replication is aborting.\n", agmt_get_long_name(prp->agmt));
  283. return_value = ACQUIRE_FATAL_ERROR;
  284. break;
  285. case NSDS50_REPL_REPLICAID_ERROR:
  286. /* remote replica detected a duplicate ReplicaID */
  287. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
  288. "%s: Unable to aquire replica: the replica "
  289. "has the same Replica ID as this one. "
  290. "Replication is aborting.\n",
  291. agmt_get_long_name(prp->agmt));
  292. return_value = ACQUIRE_FATAL_ERROR;
  293. break;
  294. case NSDS50_REPL_REPLICA_READY:
  295. /* We've acquired the replica. */
  296. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
  297. "%s: Replica was successfully acquired.\n",
  298. agmt_get_long_name(prp->agmt));
  299. /* Parse the update vector */
  300. if (NULL != ruv_bervals && NULL != ruv)
  301. {
  302. if (ruv_init_from_bervals(ruv_bervals, ruv) != RUV_SUCCESS)
  303. {
  304. /* Couldn't parse the update vector */
  305. *ruv = NULL;
  306. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
  307. "%s: Warning: acquired replica, "
  308. "but could not parse update vector. "
  309. "The replica must be reinitialized.\n",
  310. agmt_get_long_name(prp->agmt));
  311. }
  312. }
  313. /* Save consumer's RUV in the replication agreement.
  314. It is used by the changelog trimming code */
  315. if (ruv && *ruv)
  316. agmt_set_consumer_ruv (prp->agmt, *ruv);
  317. return_value = ACQUIRE_SUCCESS;
  318. break;
  319. default:
  320. return_value = ACQUIRE_FATAL_ERROR;
  321. }
  322. }
  323. else
  324. {
  325. /* Couldn't parse the response */
  326. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
  327. "%s: Unable to parse the response to the "
  328. "startReplication extended operation. "
  329. "Replication is aborting.\n",
  330. agmt_get_long_name(prp->agmt));
  331. prp->last_acquire_response_code = NSDS50_REPL_INTERNAL_ERROR;
  332. return_value = ACQUIRE_FATAL_ERROR;
  333. }
  334. if (NULL != ruv_bervals)
  335. ber_bvecfree(ruv_bervals);
  336. }
  337. else
  338. {
  339. int operation, error;
  340. conn_get_error(conn, &operation, &error);
  341. /* Couldn't send the extended operation */
  342. return_value = ACQUIRE_TRANSIENT_ERROR; /* XXX right return value? */
  343. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
  344. "%s: Unable to receive the response for a startReplication "
  345. "extended operation to consumer (%s). Will retry later.\n",
  346. agmt_get_long_name(prp->agmt),
  347. error ? ldap_err2string(error) : "unknown error");
  348. }
  349. }
  350. else
  351. {
  352. /* Couldn't get a current CSN */
  353. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
  354. "%s: Unable to obtain current CSN. "
  355. "Replication is aborting.\n",
  356. agmt_get_long_name(prp->agmt));
  357. return_value = ACQUIRE_FATAL_ERROR;
  358. }
  359. slapi_sdn_free(&replarea_sdn);
  360. if (NULL != retoid)
  361. ldap_memfree(retoid);
  362. if (NULL != retdata)
  363. ber_bvfree(retdata);
  364. }
  365. }
  366. }
  367. error:
  368. if (ACQUIRE_SUCCESS != return_value)
  369. {
  370. /* could not acquire the replica, so reinstate the linger timer, since this
  371. means we won't call release_replica, which also reinstates the timer */
  372. conn_start_linger(conn);
  373. }
  374. else
  375. {
  376. /* replica successfully acquired */
  377. prp->replica_acquired = PR_TRUE;
  378. }
  379. return return_value;
  380. }
  381. /*
  382. * Release a replica by sending an "end replication" extended request.
  383. */
  384. void
  385. release_replica(Private_Repl_Protocol *prp)
  386. {
  387. int rc;
  388. struct berval *retdata = NULL;
  389. char *retoid = NULL;
  390. struct berval *payload = NULL;
  391. Slapi_DN *replarea_sdn = NULL;
  392. int sent_message_id = 0;
  393. int ret_message_id = 0;
  394. ConnResult conres = 0;
  395. PR_ASSERT(NULL != prp);
  396. PR_ASSERT(NULL != prp->conn);
  397. if (!prp->replica_acquired)
  398. return;
  399. replarea_sdn = agmt_get_replarea(prp->agmt);
  400. payload = NSDS50EndReplicationRequest_new((char *)slapi_sdn_get_dn(replarea_sdn)); /* XXXggood had to cast away const */
  401. slapi_sdn_free(&replarea_sdn);
  402. rc = conn_send_extended_operation(prp->conn,
  403. REPL_END_NSDS50_REPLICATION_REQUEST_OID, payload, NULL /* update control */, &sent_message_id /* Message ID */);
  404. if (0 != rc)
  405. {
  406. int operation, error;
  407. conn_get_error(prp->conn, &operation, &error);
  408. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
  409. "%s: Warning: unable to send endReplication extended operation (%s)\n",
  410. agmt_get_long_name(prp->agmt),
  411. error ? ldap_err2string(error) : "unknown error");
  412. goto error;
  413. }
  414. /* Since the operation request is async, we need to wait for the response here */
  415. conres = conn_read_result_ex(prp->conn,&retoid,&retdata,NULL,&ret_message_id,1);
  416. if (CONN_OPERATION_SUCCESS != conres)
  417. {
  418. int operation, error;
  419. conn_get_error(prp->conn, &operation, &error);
  420. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
  421. "%s: Warning: unable to receive endReplication extended operation response (%s)\n",
  422. agmt_get_long_name(prp->agmt),
  423. error ? ldap_err2string(error) : "unknown error");
  424. }
  425. else
  426. {
  427. struct berval **ruv_bervals = NULL; /* Shouldn't actually be returned */
  428. int extop_result;
  429. int extop_rc = 0;
  430. /* Check the message id's match */
  431. if (sent_message_id != sent_message_id)
  432. {
  433. int operation, error;
  434. conn_get_error(prp->conn, &operation, &error);
  435. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
  436. "%s: Warning: response message id does not match the request (%s)\n",
  437. agmt_get_long_name(prp->agmt),
  438. error ? ldap_err2string(error) : "unknown error");
  439. }
  440. extop_rc = decode_repl_ext_response(retdata, &extop_result,
  441. (struct berval ***)&ruv_bervals);
  442. if (0 == extop_rc)
  443. {
  444. if (NSDS50_REPL_REPLICA_RELEASE_SUCCEEDED == extop_result)
  445. {
  446. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
  447. "%s: Successfully released consumer\n", agmt_get_long_name(prp->agmt));
  448. }
  449. else
  450. {
  451. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
  452. "%s: Unable to release consumer: response code %d\n",
  453. agmt_get_long_name(prp->agmt), extop_result);
  454. /* disconnect from the consumer so that it does not stay locked */
  455. conn_disconnect (prp->conn);
  456. }
  457. }
  458. else
  459. {
  460. /* Couldn't parse the response */
  461. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
  462. "%s: Warning: Unable to parse the response "
  463. " to the endReplication extended operation.\n",
  464. agmt_get_long_name(prp->agmt));
  465. }
  466. if (NULL != ruv_bervals)
  467. ber_bvecfree(ruv_bervals);
  468. /* XXXggood free ruv_bervals if we got them for some reason */
  469. }
  470. if (NULL != payload)
  471. ber_bvfree(payload);
  472. if (NULL != retoid)
  473. ldap_memfree(retoid);
  474. if (NULL != retdata)
  475. ber_bvfree(retdata);
  476. /* replica is released, start the linger timer on the connection, which
  477. was stopped in acquire_replica */
  478. conn_start_linger(prp->conn);
  479. error:
  480. prp->replica_acquired = PR_FALSE;
  481. }
  482. /* converts consumer's response to a string */
  483. char *
  484. protocol_response2string (int response)
  485. {
  486. switch (response)
  487. {
  488. case NSDS50_REPL_REPLICA_READY: return "replica acquired";
  489. case NSDS50_REPL_REPLICA_BUSY: return "replica busy";
  490. case NSDS50_REPL_EXCESSIVE_CLOCK_SKEW: return "excessive clock skew";
  491. case NSDS50_REPL_PERMISSION_DENIED: return "permission denied";
  492. case NSDS50_REPL_DECODING_ERROR: return "decoding error";
  493. case NSDS50_REPL_UNKNOWN_UPDATE_PROTOCOL: return "unknown update protocol";
  494. case NSDS50_REPL_NO_SUCH_REPLICA: return "no such replica";
  495. case NSDS50_REPL_BELOW_PURGEPOINT: return "csn below purge point";
  496. case NSDS50_REPL_INTERNAL_ERROR: return "internal error";
  497. case NSDS50_REPL_REPLICA_RELEASE_SUCCEEDED: return "replica released";
  498. case NSDS50_REPL_LEGACY_CONSUMER: return "replica is a legacy consumer";
  499. case NSDS50_REPL_REPLICAID_ERROR: return "duplicate replica ID detected";
  500. case NSDS50_REPL_UPTODATE: return "no change to send";
  501. default: return "unknown error";
  502. }
  503. }
  504. int
  505. repl5_strip_fractional_mods(Repl_Agmt *agmt, LDAPMod ** mods)
  506. {
  507. int retval = 0;
  508. int i = 0;
  509. char **a = agmt_get_fractional_attrs(agmt);
  510. if (a) {
  511. /* Iterate through the fractional attr list */
  512. for ( i = 0; a[i] != NULL; i++ )
  513. {
  514. char *this_excluded_attr = a[i];
  515. int j = 0;
  516. for ( j = 0; NULL != mods[ j ]; )
  517. {
  518. /* For each one iterate through the attrs in this mod list */
  519. /* For any that match, remove the mod */
  520. LDAPMod *this_mod = mods[j];
  521. if (0 == slapi_attr_type_cmp(this_mod->mod_type,this_excluded_attr,SLAPI_TYPE_CMP_SUBTYPE))
  522. {
  523. /* Move down all subsequent mods */
  524. int k = 0;
  525. for (k = j; mods[k+1] ; k++)
  526. {
  527. mods[k] = mods[k+1];
  528. }
  529. /* Zero the end of the array */
  530. mods[k] = NULL;
  531. /* Adjust value of j, implicit in not incrementing it */
  532. /* Free this mod */
  533. ber_bvecfree(this_mod->mod_bvalues);
  534. slapi_ch_free((void **)&(this_mod->mod_type));
  535. slapi_ch_free((void **)&this_mod);
  536. } else {
  537. j++;
  538. }
  539. }
  540. }
  541. slapi_ch_array_free(a);
  542. }
  543. return retval;
  544. }