dse_modify(Slapi_PBlock *pb) /* JCM There should only be one exit point from this function! */
{
    int err;             /*House keeping stuff*/
    LDAPMod **mods;      /*Used to apply the modifications*/
    LDAPMod **original_mods = NULL; /* some mods can be removed by callback, save them for later logging */
    char *errbuf = NULL; /* To get error back */
    struct dse *pdse;
    Slapi_Entry *ec = NULL;
    Slapi_Entry *ecc = NULL;
    int returncode = LDAP_SUCCESS;
    char returntext[SLAPI_DSE_RETURNTEXT_SIZE] = "";
    Slapi_DN *sdn = NULL;
    int dont_write_file = 0; /* default */
    int rc = SLAPI_DSE_CALLBACK_DO_NOT_APPLY;
    int retval = -1;
    int need_be_postop = 0;
    int plugin_started = 0;
    int internal_op = 0;
    PRBool global_lock_owned = PR_FALSE;
    Operation *pb_op = NULL;

    PR_ASSERT(pb);
    if (slapi_pblock_get(pb, SLAPI_PLUGIN_PRIVATE, &pdse) < 0 ||
        /* slapi_pblock_get( pb, SLAPI_MODIFY_TARGET, &dn ) < 0 || */
        slapi_pblock_get(pb, SLAPI_MODIFY_TARGET_SDN, &sdn) < 0 ||
        slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &mods) < 0 || (NULL == pdse)) {
        returncode = LDAP_OPERATIONS_ERROR;
        goto done;
    }

    slapi_pblock_get(pb, SLAPI_DSE_DONT_WRITE_WHEN_ADDING, &dont_write_file);
    if (!dont_write_file && dse_check_for_readonly_error(pb, pdse)) {
        /* already returned result */
        return retval;
    }

    slapi_pblock_get(pb, SLAPI_OPERATION, &pb_op);
    if (pb_op){
        internal_op = operation_is_flag_set(pb_op, OP_FLAG_INTERNAL);
    }
    /* Find the entry we are about to modify. */
    ec = dse_get_entry_copy(pdse, sdn, DSE_USE_LOCK);
    if (ec == NULL) {
        returncode = LDAP_NO_SUCH_OBJECT;
        goto done;
    }

    /* Check acl */
    err = plugin_call_acl_mods_access(pb, ec, mods, &errbuf);
    if (err != LDAP_SUCCESS) {
        returncode = err;
        if (errbuf) {
            PL_strncpyz(returntext, errbuf, sizeof(returntext));
            slapi_ch_free_string(&errbuf);
        }
        goto done;
    }

    /* Save away a copy of the entry, before modifications */
    slapi_pblock_set(pb, SLAPI_ENTRY_PRE_OP, slapi_entry_dup(ec)); /* JCM - When does this get free'd? */
    /* richm - it is freed in modify.c */

    /* Modify a copy of the entry*/
    ecc = slapi_entry_dup(ec);
    err = entry_apply_mods(ecc, mods);

    /* Possibly acquire the global backend lock */
    if (global_backend_lock_requested()) {
        global_backend_lock_lock();
        global_lock_owned = PR_TRUE;
    }
    original_mods = copy_mods(mods);

    /* XXXmcs: should we expand objectclass values here?? */
    /* give the dse callbacks the first crack at the modify */
    rc = dse_call_callback(pdse, pb, SLAPI_OPERATION_MODIFY, DSE_FLAG_PREOP, ec, ecc, &returncode, returntext);
    if (SLAPI_DSE_CALLBACK_OK == rc) {
        int plugin_rc;

        /* next, give the be plugins a crack at it */
        slapi_pblock_set(pb, SLAPI_RESULT_CODE, &returncode);
        slapi_pblock_set(pb, SLAPI_MODIFY_EXISTING_ENTRY, ecc);
        plugin_rc = plugin_call_plugins(pb, SLAPI_PLUGIN_BE_PRE_MODIFY_FN);
        need_be_postop = 1; /* if the be preops were called, have to call the be postops too */
        if (!returncode) {
            slapi_pblock_get(pb, SLAPI_RESULT_CODE, &returncode);
        }
        if (!plugin_rc && !returncode) {
            /* finally, give the betxn plugins a crack at it */
            plugin_rc = plugin_call_plugins(pb, SLAPI_PLUGIN_BE_TXN_PRE_MODIFY_FN);
            if (!returncode) {
                slapi_pblock_get(pb, SLAPI_RESULT_CODE, &returncode);
            }
            if (plugin_rc || returncode) {
                slapi_log_err(SLAPI_DSE_TRACELEVEL,
                              "dse_modify", "SLAPI_PLUGIN_BE_TXN_PRE_MODIFY_FN failed - rc %d LDAP error %d:%s\n",
                              plugin_rc, returncode, ldap_err2string(returncode));
            }
        } else {
            slapi_log_err(SLAPI_DSE_TRACELEVEL,
                          "dse_modify", "SLAPI_PLUGIN_BE_PRE_MODIFY_FN failed - rc %d LDAP error %d:%s\n",
                          rc, returncode, ldap_err2string(returncode));
        }
        if (plugin_rc || returncode) {
            char *ldap_result_message = NULL;
            rc = SLAPI_DSE_CALLBACK_ERROR;
            if (!returncode) {
                slapi_log_err(SLAPI_DSE_TRACELEVEL,
                              "dse_modify", "PRE_MODIFY plugin returned non-zero but did not set an LDAP error\n");
                returncode = LDAP_OPERATIONS_ERROR;
            }
            if (!returntext[0]) {
                slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &ldap_result_message);
                if (ldap_result_message && ldap_result_message[0]) {
                    PL_strncpyz(returntext, ldap_result_message, sizeof(returntext));
                }
            }
        } else {
            /*
             * If we are using dynamic plugins, and we are modifying a plugin
             * we need to do some additional checks.  First, check if we are
             * enabling/disabling a plugin.  Then make sure the plugin still
             * starts after applying the plugin changes.
             */
            rc = SLAPI_DSE_CALLBACK_OK;
            if (slapi_entry_attr_hasvalue(ec, SLAPI_ATTR_OBJECTCLASS, "nsSlapdPlugin")) {
                if (config_get_dynamic_plugins()) {
                    if ((plugin_started = dse_modify_plugin(ec, ecc, returntext)) == -1) {
                        returncode = LDAP_UNWILLING_TO_PERFORM;
                        rc = SLAPI_DSE_CALLBACK_ERROR;
                        retval = -1;
                        goto done;
                    }
                    /*
                     * If this is not a internal operation, make sure the plugin
                     * can be restarted.
                     */
                    if (!internal_op) {
                        if (dse_pre_modify_plugin(ec, ecc, mods)) {
                            char *errtext;
                            slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &errtext);
                            if (errtext) {
                                PL_strncpyz(returntext,
                                            "Failed to apply plugin config change, "
                                            "check the errors log for more info.",
                                            sizeof(returntext));
                            }
                            returncode = LDAP_UNWILLING_TO_PERFORM;
                            rc = SLAPI_DSE_CALLBACK_ERROR;
                            retval = -1;
                            goto done;
                        }
                    }
                } else {
                    slapi_log_err(SLAPI_LOG_NOTICE, "dse_modify", "A plugin has been enabled or disabled, but nsslapd-dynamic-plugins is off. A server restart is required to change this plugin state.\n");
                } /* end config_get_dynamic_plugins */
            } /* end has nsSlapdPlugin */
        }
    }

    switch (rc) {
    case SLAPI_DSE_CALLBACK_ERROR:
        /* Error occured in the callback -- return error code from callback */
        goto done;
        break;
    case SLAPI_DSE_CALLBACK_DO_NOT_APPLY:
        /* Callback says don't apply the changes -- return Success */
        returncode = LDAP_SUCCESS;
        returntext[0] = '\0';
        retval = 0;
        goto done;
        break;
    case SLAPI_DSE_CALLBACK_OK: {
        /* The callback may alter the mods in the pblock.  This happens
           for example in the schema code.  Since the schema attributes
           are managed exclusively by the schema code, we should not
           apply those mods.  However, for reasons unknown to me, we
           must in the general case call entry_apply_mods before calling
           the modify callback above.  In the case of schema, the schema
           code will remove the schema attributes from the mods.  So, we
           reapply the mods to the entry for the attributes we manage in
           the dse code (e.g. aci)
        */
        int reapply_mods = 0; /* default is to not reapply entry_apply_mods */
        slapi_pblock_get(pb, SLAPI_DSE_REAPPLY_MODS, &reapply_mods);
        /* Callback says apply the changes */
        if (reapply_mods) {
            LDAPMod **modsagain = NULL; /*Used to apply the modifications*/
            slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &modsagain);
            if (NULL != modsagain) {
                /* the dse modify callback must have modified ecc back to it's
                   original state, before the earlier apply_mods, but without the
                   attributes it did not want us to apply mods to */
                err = entry_apply_mods(ecc, modsagain);
            }
        }

        if (err != 0) {
            returncode = err;
            returntext[0] = '\0';
            retval = -1;
            goto done;
        }
        break;
    }
    }

    /* We're applying the mods... check that the entry still obeys the schema */
    if (slapi_entry_schema_check(pb, ecc) != 0) {
        char *errtext;

        slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &errtext);
        if (errtext) {
            PL_strncpyz(returntext, errtext, sizeof(returntext));
        }
        returncode = LDAP_OBJECT_CLASS_VIOLATION;
        retval = -1;
        goto done;
    }

    /* Check if the attribute values in the mods obey the syntaxes */
    if (slapi_mods_syntax_check(pb, mods, 0) != 0) {
        char *errtext;

        slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &errtext);
        if (errtext) {
            PL_strncpyz(returntext, errtext, sizeof(returntext));
        }
        returncode = LDAP_INVALID_SYNTAX;
        retval = -1;
        goto done;
    }

    /* Change the entry itself both on disk and in the AVL tree */
    /* dse_replace_entry free's the existing entry. */
    if (dse_replace_entry(pdse, ecc, !dont_write_file, DSE_USE_LOCK) != 0) {
        returncode = LDAP_OPERATIONS_ERROR;
        retval = -1;
        goto done;
    }
    retval = 0;                                                      /* so far, so good */
    slapi_pblock_set(pb, SLAPI_ENTRY_POST_OP, slapi_entry_dup(ecc)); /* JCM - When does this get free'd? */
    /* richm - it is freed in modify.c */
    /* give the dse callbacks the first crack at the modify */
    rc = dse_call_callback(pdse, pb, SLAPI_OPERATION_MODIFY, DSE_FLAG_POSTOP, ec, ecc, &returncode, returntext);
done:
    if (rc != SLAPI_DSE_CALLBACK_DO_NOT_APPLY) {
        /* make sure OPRETURN is set */
        slapi_pblock_get(pb, SLAPI_PLUGIN_OPRETURN, &err);
        if ((retval || returncode) && !err) {
            slapi_pblock_set(pb, SLAPI_PLUGIN_OPRETURN, retval ? &retval : &returncode);
        }
        /* next, give the betxn plugins a crack at it */
        slapi_pblock_set(pb, SLAPI_RESULT_CODE, &returncode);
        slapi_pblock_set(pb, SLAPI_MODIFY_EXISTING_ENTRY, ecc);
        if (need_be_postop) {
            plugin_call_plugins(pb, SLAPI_PLUGIN_BE_TXN_POST_MODIFY_FN);
            if (!returncode) {
                slapi_pblock_get(pb, SLAPI_RESULT_CODE, &returncode);
            }
            if (returncode && !returntext[0]) {
                char *ldap_result_message = NULL;
                slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &ldap_result_message);
                if (ldap_result_message && ldap_result_message[0]) {
                    PL_strncpyz(returntext, ldap_result_message, sizeof(returntext));
                }
            }

            plugin_call_plugins(pb, SLAPI_PLUGIN_BE_POST_MODIFY_FN);
            if (!returncode) {
                slapi_pblock_get(pb, SLAPI_RESULT_CODE, &returncode);
            }
        }
    } else {
        /* It should not happen but just be paranoiac, do not
         * forget to call the postop if needed
         */
        if (need_be_postop) {
            plugin_call_plugins(pb, SLAPI_PLUGIN_BE_TXN_POST_MODIFY_FN);
            if (!returncode) {
                slapi_pblock_get(pb, SLAPI_RESULT_CODE, &returncode);
            }

            plugin_call_plugins(pb, SLAPI_PLUGIN_BE_POST_MODIFY_FN);
            if (!returncode) {
                slapi_pblock_get(pb, SLAPI_RESULT_CODE, &returncode);
            }
        }
    }

    /* time to restore original mods */
    if (original_mods) {
        LDAPMod **mods_from_callback;
        slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &mods_from_callback);
        ldap_mods_free(mods_from_callback, 1 /* Free the Array and the Elements */);
        slapi_pblock_set(pb, SLAPI_MODIFY_MODS, original_mods);
    }
    if (global_lock_owned) {
        global_backend_lock_unlock();
    }
    slapi_send_ldap_result(pb, returncode, NULL, returntext[0] ? returntext : NULL, 0, NULL);

    return dse_modify_return(retval, ec, ecc);
}