/* VmDirInternalDeleteEntry: Interface that can be used "internally" by the server code. One of the main differences between * this function and MLDelete is that this function does not send back an LDAP result to the client. * * Return: VmDir level error code. Also, pOperation->ldapResult content is set. */ int VmDirInternalDeleteEntry( PVDIR_OPERATION pOperation ) { int retVal = LDAP_SUCCESS; VDIR_ENTRY entry = {0}; PVDIR_ENTRY pEntry = NULL; BOOLEAN leafNode = FALSE; DeleteReq* delReq = &(pOperation->request.deleteReq); ModifyReq* modReq = &(pOperation->request.modifyReq); BOOLEAN bIsDomainObject = FALSE; BOOLEAN bHasTxn = FALSE; PSTR pszLocalErrMsg = NULL; PVDIR_OPERATION_ML_METRIC pMLMetrics = NULL; extern DWORD VmDirDeleteRaftPreCommit(PVDIR_SCHEMA_CTX, EntryId, char *, PVDIR_OPERATION); assert(pOperation && pOperation->pBECtx->pBE); pMLMetrics = &pOperation->MLMetrics; VMDIR_COLLECT_TIME(pMLMetrics->iMLStartTime); if (VmDirdState() == VMDIRD_STATE_READ_ONLY) { retVal = VMDIR_ERROR_UNWILLING_TO_PERFORM; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "Server in read-only mode"); } // make sure we have minimum DN length if (delReq->dn.lberbv_len < 3) { retVal = VMDIR_ERROR_INVALID_REQUEST; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "Invalid DN length - (%u)", delReq->dn.lberbv_len); } // Normalize DN retVal = VmDirNormalizeDN(&(delReq->dn), pOperation->pSchemaCtx); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "DN normalization failed - (%u)(%s)", retVal, VDIR_SAFE_STRING(VmDirSchemaCtxGetErrorMsg(pOperation->pSchemaCtx))); VMDIR_COLLECT_TIME(pMLMetrics->iBETxnBeginStartTime); retVal = pOperation->pBEIF->pfnBETxnBegin(pOperation->pBECtx, VDIR_BACKEND_TXN_WRITE, &bHasTxn); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "txn begin (%u)(%s)", retVal, VDIR_SAFE_STRING(pOperation->pBEErrorMsg)); VMDIR_COLLECT_TIME(pMLMetrics->iBETxnBeginEndTime); if (bHasTxn) { retVal = VmDirValidateOp(pOperation, __func__); BAIL_ON_VMDIR_ERROR(retVal); } // Execute pre modify apply Delete plugin logic VMDIR_COLLECT_TIME(pMLMetrics->iPrePluginsStartTime); retVal = VmDirExecutePreModApplyDeletePlugins(pOperation, NULL, retVal); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "PreModApplyDelete plugin failed - (%u)", retVal); VMDIR_COLLECT_TIME(pMLMetrics->iPrePlugunsEndTim); retVal = VmDirNormalizeMods(pOperation->pSchemaCtx, modReq->mods, &pszLocalErrMsg); BAIL_ON_VMDIR_ERROR(retVal); // BUGBUG, need to protect some system entries such as schema,domain....etc? // Read current entry from DB retVal = pOperation->pBEIF->pfnBEDNToEntry( pOperation->pBECtx, pOperation->pSchemaCtx, &(delReq->dn), &entry, VDIR_BACKEND_ENTRY_LOCK_WRITE); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "(%u)(%s)", retVal, VDIR_SAFE_STRING(pOperation->pBEErrorMsg)); pEntry = &entry; // Parse Parent DN retVal = VmDirGetParentDN(&pEntry->dn, &pEntry->pdn); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "Get ParentDn failed - (%u)", retVal); // get parent entry if (pEntry->pdn.lberbv.bv_val) { PVDIR_ENTRY pParentEntry = NULL; retVal = VmDirAllocateMemory(sizeof(*pEntry), (PVOID)&pParentEntry); BAIL_ON_VMDIR_ERROR(retVal); retVal = pOperation->pBEIF->pfnBEDNToEntry( pOperation->pBECtx, pOperation->pSchemaCtx, &pEntry->pdn, pParentEntry, VDIR_BACKEND_ENTRY_LOCK_READ); if (retVal) { VmDirFreeEntryContent(pParentEntry); VMDIR_SAFE_FREE_MEMORY(pParentEntry); switch (retVal) { case VMDIR_ERROR_BACKEND_ENTRY_NOTFOUND: BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "parent (%s) not found, (%s)", pEntry->pdn.lberbv_val, VDIR_SAFE_STRING(pOperation->pBEErrorMsg)); default: BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "parent (%s) lookup failed, (%s)", pEntry->pdn.lberbv_val, VDIR_SAFE_STRING(pOperation->pBEErrorMsg)); } } pEntry->pParentEntry = pParentEntry; // pEntry takes over pParentEntry pParentEntry = NULL; } // // The delete will succeed if the caller either has the explicit right // to delete this object or if they have the right to delete children // of this object's parent. // retVal = VmDirSrvAccessCheck( pOperation, &pOperation->conn->AccessInfo, pEntry, VMDIR_RIGHT_DS_DELETE_OBJECT); if (retVal != ERROR_SUCCESS && pEntry->pParentEntry) { retVal = VmDirSrvAccessCheck( pOperation, &pOperation->conn->AccessInfo, pEntry->pParentEntry, VMDIR_RIGHT_DS_DELETE_CHILD); } BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "VmDirSrvAccessCheck failed - (%u)(%s)", retVal, VMDIR_ACCESS_DENIED_ERROR_MSG); // Make sure it is a leaf node retVal = pOperation->pBEIF->pfnBEChkIsLeafEntry( pOperation->pBECtx, pEntry->eId, &leafNode); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "BEChkIsLeafEntry failed, (%u)(%s)", retVal, VDIR_SAFE_STRING(pOperation->pBEErrorMsg)); if (leafNode == FALSE) { retVal = VMDIR_ERROR_NOT_ALLOWED_ON_NONLEAF; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "Delete of a non-leaf node is not allowed."); } // Retrieve to determine whether it is domain object earlier // before attribute modifications // ('bIsDomainObject' is needed for a domain object deletion) retVal = VmDirIsDomainObjectWithEntry(pEntry, &bIsDomainObject); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "VmDirIsDomainObjectWithEntry failed - (%u)", retVal); retVal = GenerateDeleteAttrsMods(pOperation, pEntry); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "GenerateDeleteAttrsMods failed - (%u)", retVal); // Normalize attribute values in mods retVal = VmDirNormalizeMods(pOperation->pSchemaCtx, modReq->mods, &pszLocalErrMsg); BAIL_ON_VMDIR_ERROR(retVal); // Apply modify operations to the current entry in the DB. retVal = VmDirApplyModsToEntryStruct(pOperation->pSchemaCtx, modReq, pEntry, NULL, &pszLocalErrMsg); BAIL_ON_VMDIR_ERROR(retVal); // Update Entry retVal = pOperation->pBEIF->pfnBEEntryDelete(pOperation->pBECtx, modReq->mods, pEntry); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "BEEntryDelete (%u)(%s)", retVal, VDIR_SAFE_STRING(pOperation->pBEErrorMsg)); retVal = DeleteRefAttributesValue(pOperation, &(pEntry->dn)); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "DeleteRefAttributesValue (%u)(%s)", retVal, VDIR_SAFE_STRING(pOperation->pBEErrorMsg)); // Use normalized DN value if (bIsDomainObject) { retVal = VmDirInternalRemoveOrgConfig(pOperation, BERVAL_NORM_VAL(pEntry->dn)); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "Update domain list entry failed."); } if (pOperation->bNoRaftLog == FALSE) { retVal = VmDirDeleteRaftPreCommit( pOperation->pSchemaCtx, pEntry->eId, BERVAL_NORM_VAL(pEntry->dn), pOperation); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "VmDirDeleteRaftPreCommit error (%u)", retVal); } if (bHasTxn) { VMDIR_COLLECT_TIME(pMLMetrics->iBETxnCommitStartTime); retVal = pOperation->pBEIF->pfnBETxnCommit(pOperation->pBECtx); bHasTxn = FALSE; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "txn commit logIndex %llu (%u)(%s)", pOperation->logIndex, retVal, VDIR_SAFE_STRING(pOperation->pBEErrorMsg)); VMDIR_COLLECT_TIME(pMLMetrics->iBETxnCommitEndTime); } if (!pOperation->bSuppressLogInfo) { VMDIR_LOG_INFO( VMDIR_LOG_MASK_ALL, "Delete Entry (%s) logIndex %llu", VDIR_SAFE_STRING(pEntry->dn.lberbv_val), pOperation->logIndex); } // Post delete entry // TODO, make it into a separate file deletePlugin.c // clean lockout cache record if exists VdirLockoutCacheRemoveRec(pEntry->dn.bvnorm_val); cleanup: if (retVal == 0) { int iPostCommitPluginRtn = 0; VMDIR_COLLECT_TIME(pMLMetrics->iPostPluginsStartTime); // Execute post Delete commit plugin logic iPostCommitPluginRtn = VmDirExecutePostDeleteCommitPlugins(pOperation, pEntry, retVal); if (iPostCommitPluginRtn != LDAP_SUCCESS && iPostCommitPluginRtn != pOperation->ldapResult.errCode) // pass through { VMDIR_LOG_ERROR( LDAP_DEBUG_ANY, "InternalDeleteEntry: VdirExecutePostDeleteCommitPlugins - code(%d)", iPostCommitPluginRtn); } VMDIR_COLLECT_TIME(pMLMetrics->iPostPlugunsEndTime); } // collect metrics VMDIR_COLLECT_TIME(pMLMetrics->iMLEndTime); VmDirInternalMetricsUpdate(pOperation); VmDirInternalMetricsLogInefficientOp(pOperation); if (pOperation->opType != VDIR_OPERATION_TYPE_REPL) { // In case of replication, modReq is owned by the Replication thread/logic DeleteMods(modReq); } VmDirFreeEntryContent(&entry); VMDIR_SAFE_FREE_MEMORY(pszLocalErrMsg); return retVal; error: if (bHasTxn) { pOperation->pBEIF->pfnBETxnAbort(pOperation->pBECtx); } VMDIR_SET_LDAP_RESULT_ERROR(&pOperation->ldapResult, retVal, pszLocalErrMsg); goto cleanup; }
/* VmDirInternalDeleteEntry: Interface that can be used "internally" by the server code. One of the main differences between * this function and MLDelete is that this function does not send back an LDAP result to the client. * * Return: VmDir level error code. Also, pOperation->ldapResult content is set. */ int VmDirInternalDeleteEntry( PVDIR_OPERATION pOperation ) { int retVal = LDAP_SUCCESS; int deadLockRetries = 0; VDIR_ENTRY entry = {0}; PVDIR_ENTRY pEntry = NULL; BOOLEAN leafNode = FALSE; DeleteReq * delReq = &(pOperation->request.deleteReq); ModifyReq * modReq = &(pOperation->request.modifyReq); BOOLEAN bIsDomainObject = FALSE; BOOLEAN bHasTxn = FALSE; PSTR pszLocalErrMsg = NULL; assert(pOperation && pOperation->pBECtx->pBE); if (VmDirdState() == VMDIRD_STATE_READ_ONLY) { retVal = VMDIR_ERROR_UNWILLING_TO_PERFORM; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "Server in read-only mode" ); } // Normalize DN retVal = VmDirNormalizeDN( &(delReq->dn), pOperation->pSchemaCtx ); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "DN normalization failed - (%u)(%s)", retVal, VDIR_SAFE_STRING(VmDirSchemaCtxGetErrorMsg(pOperation->pSchemaCtx)) ); if (pOperation->opType != VDIR_OPERATION_TYPE_REPL) { // Execute pre modify apply Delete plugin logic retVal = VmDirExecutePreModApplyDeletePlugins(pOperation, NULL, retVal); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "PreModApplyDelete plugin failed - (%u)", retVal ); } retVal = VmDirNormalizeMods( pOperation->pSchemaCtx, modReq->mods, &pszLocalErrMsg ); BAIL_ON_VMDIR_ERROR( retVal ); // make sure VDIR_BACKEND_CTX has usn change number by now if ( pOperation->pBECtx->wTxnUSN <= 0 ) { retVal = VMDIR_ERROR_NO_USN; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "BECtx.wTxnUSN not set"); } // BUGBUG, need to protect some system entries such as schema,domain....etc? // ************************************************************************************ // transaction retry loop begin. make sure all function within are retry agnostic. // ************************************************************************************ txnretry: if (bHasTxn) { pOperation->pBEIF->pfnBETxnAbort( pOperation->pBECtx ); bHasTxn = FALSE; } deadLockRetries++; if (deadLockRetries > MAX_DEADLOCK_RETRIES) { retVal = VMDIR_ERROR_LOCK_DEADLOCK; BAIL_ON_VMDIR_ERROR( retVal ); } else { if (pEntry) { VmDirFreeEntryContent(pEntry); memset(pEntry, 0, sizeof(VDIR_ENTRY)); pEntry = NULL; } retVal = pOperation->pBEIF->pfnBETxnBegin( pOperation->pBECtx, VDIR_BACKEND_TXN_WRITE); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "txn begin (%u)(%s)", retVal, VDIR_SAFE_STRING(pOperation->pBEErrorMsg)); bHasTxn = TRUE; // Read current entry from DB retVal = pOperation->pBEIF->pfnBEDNToEntry( pOperation->pBECtx, pOperation->pSchemaCtx, &(delReq->dn), &entry, VDIR_BACKEND_ENTRY_LOCK_WRITE); if (retVal != 0) { switch (retVal) { case VMDIR_ERROR_BACKEND_DEADLOCK: goto txnretry; // Possible retry. default: BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "(%u)(%s)", retVal, VDIR_SAFE_STRING(pOperation->pBEErrorMsg)); } } pEntry = &entry; // Parse Parent DN retVal = VmDirGetParentDN( &pEntry->dn, &pEntry->pdn ); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "Get ParentDn failed - (%u)", retVal ); // get parent entry if (pEntry->pdn.lberbv.bv_val) { PVDIR_ENTRY pParentEntry = NULL; retVal = VmDirAllocateMemory(sizeof(*pEntry), (PVOID)&pParentEntry); BAIL_ON_VMDIR_ERROR(retVal); retVal = pOperation->pBEIF->pfnBEDNToEntry( pOperation->pBECtx, pOperation->pSchemaCtx, &pEntry->pdn, pParentEntry, VDIR_BACKEND_ENTRY_LOCK_READ); if (retVal) { VmDirFreeEntryContent(pParentEntry); VMDIR_SAFE_FREE_MEMORY(pParentEntry); switch (retVal) { case VMDIR_ERROR_BACKEND_DEADLOCK: goto txnretry; // Possible retry. case VMDIR_ERROR_BACKEND_ENTRY_NOTFOUND: BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "parent (%s) not found, (%s)", pEntry->pdn.lberbv_val, VDIR_SAFE_STRING(pOperation->pBEErrorMsg) ); default: BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "parent (%s) lookup failed, (%s)", pEntry->pdn.lberbv_val, VDIR_SAFE_STRING(pOperation->pBEErrorMsg) ); } } pEntry->pParentEntry = pParentEntry; // pEntry takes over pParentEntry pParentEntry = NULL; } // SJ-TBD: Once ACLs are enabled, following check should go in ACLs logic. if (VmDirIsInternalEntry( pEntry ) || VmDirIsProtectedEntry(pEntry)) { retVal = VMDIR_ERROR_UNWILLING_TO_PERFORM; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "An internal entry (%s) can NOT be deleted.", pEntry->dn.lberbv_val ); } // only when there is parent Entry, ACL check is done if (pEntry->pParentEntry) { retVal = VmDirSrvAccessCheck( pOperation, &pOperation->conn->AccessInfo, pEntry->pParentEntry, VMDIR_RIGHT_DS_DELETE_CHILD); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "VmDirSrvAccessCheck failed - (%u)(%s)", retVal, VMDIR_ACCESS_DENIED_ERROR_MSG); } // Make sure it is a leaf node retVal = pOperation->pBEIF->pfnBEChkIsLeafEntry( pOperation->pBECtx, pEntry->eId, &leafNode); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "BEChkIsLeafEntry failed, (%u)(%s)", retVal, VDIR_SAFE_STRING(pOperation->pBEErrorMsg) ); if (leafNode == FALSE) { retVal = VMDIR_ERROR_NOT_ALLOWED_ON_NONLEAF; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "Delete of a non-leaf node is not allowed." ); } // Retrieve to determine whether it is domain object earlier // before attribute modifications // ('bIsDomainObject' is needed for a domain object deletion) retVal = VmDirIsDomainObjectWithEntry(pEntry, &bIsDomainObject); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "VmDirIsDomainObjectWithEntry failed - (%u)", retVal ); if (pOperation->opType != VDIR_OPERATION_TYPE_REPL) { // Generate mods to delete attributes that need not be present in a DELETED entry // Note: in case of executing the deadlock while loop multiple times, same attribute Delete mod be added // multiple times in the modReq, which is expected to work correctly. retVal = GenerateDeleteAttrsMods( pOperation, pEntry ); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "GenerateDeleteAttrsMods failed - (%u)", retVal); // Generate new meta-data for the attributes being updated if ((retVal = VmDirGenerateModsNewMetaData( pOperation, modReq->mods, pEntry->eId )) != 0) { switch (retVal) { case VMDIR_ERROR_LOCK_DEADLOCK: goto txnretry; // Possible retry. BUGBUG, is modReq->mods in above call good for retry? default: BAIL_ON_VMDIR_ERROR( retVal ); } } } // Normalize attribute values in mods retVal = VmDirNormalizeMods( pOperation->pSchemaCtx, modReq->mods, &pszLocalErrMsg ); BAIL_ON_VMDIR_ERROR( retVal ); // Apply modify operations to the current entry in the DB. retVal = VmDirApplyModsToEntryStruct( pOperation->pSchemaCtx, modReq, pEntry, &pszLocalErrMsg ); BAIL_ON_VMDIR_ERROR( retVal ); // Update DBs // Update Entry retVal = pOperation->pBEIF->pfnBEEntryDelete( pOperation->pBECtx, modReq->mods, pEntry ); if (retVal != 0) { switch (retVal) { case VMDIR_ERROR_BACKEND_DEADLOCK: goto txnretry; // Possible retry. default: BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "BEEntryDelete (%u)(%s)", retVal, VDIR_SAFE_STRING(pOperation->pBEErrorMsg)); } } retVal = DeleteRefAttributesValue(pOperation, &(pEntry->dn)); if (retVal != 0) { switch (retVal) { case VMDIR_ERROR_LOCK_DEADLOCK: goto txnretry; // Possible retry. default: BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "BEEntryDelete (%u)(%s)", retVal, VDIR_SAFE_STRING(pOperation->pBEErrorMsg)); } } // Use normalized DN value if (bIsDomainObject) { retVal = VmDirInternalRemoveOrgConfig(pOperation, BERVAL_NORM_VAL(pEntry->dn)); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "Update domain list entry failed." ); } retVal = pOperation->pBEIF->pfnBETxnCommit( pOperation->pBECtx); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "txn commit (%u)(%s)", retVal, VDIR_SAFE_STRING(pOperation->pBEErrorMsg)); bHasTxn = FALSE; } // ************************************************************************************ // transaction retry loop end. // ************************************************************************************ VMDIR_LOG_INFO( VMDIR_LOG_MASK_ALL, "Delete Entry (%s)", VDIR_SAFE_STRING(pEntry->dn.lberbv_val)); // Post delete entry // TODO, make it into a separate file deletePlugin.c // clean lockout cache record if exists VdirLockoutCacheRemoveRec(pEntry->dn.bvnorm_val); cleanup: if (pOperation->opType != VDIR_OPERATION_TYPE_REPL) { if (retVal == LDAP_SUCCESS) { int iPostCommitPluginRtn = 0; // Execute post Delete commit plugin logic iPostCommitPluginRtn = VmDirExecutePostDeleteCommitPlugins(pOperation, pEntry, retVal); if ( iPostCommitPluginRtn != LDAP_SUCCESS && iPostCommitPluginRtn != pOperation->ldapResult.errCode // pass through ) { VmDirLog( LDAP_DEBUG_ANY, "InternalDeleteEntry: VdirExecutePostDeleteCommitPlugins - code(%d)", iPostCommitPluginRtn); } } // In case of replication, modReq is owned by the Replication thread/logic DeleteMods ( modReq ); } VmDirFreeEntryContent ( &entry ); VMDIR_SAFE_FREE_MEMORY(pszLocalErrMsg); return retVal; error: if (bHasTxn) { pOperation->pBEIF->pfnBETxnAbort( pOperation->pBECtx ); } VMDIR_SET_LDAP_RESULT_ERROR( &(pOperation->ldapResult), retVal, pszLocalErrMsg); goto cleanup; }
int VmDirModifyEntryCoreLogic( VDIR_OPERATION * pOperation, /* IN */ ModifyReq * modReq, /* IN */ ENTRYID entryId, /* IN */ BOOLEAN bNoRaftLog, /* IN */ VDIR_ENTRY * pEntry /* OUT */ ) { int retVal = LDAP_SUCCESS; PSTR pszLocalErrMsg = NULL; BOOLEAN bDnModified = FALSE; BOOLEAN bLeafNode = FALSE; PVDIR_ATTRIBUTE pAttrMemberOf = NULL; extern DWORD VmDirModifyRaftPreCommit(PVDIR_SCHEMA_CTX, ENTRYID, char *, PVDIR_MODIFICATION, PVDIR_OPERATION); retVal = pOperation->pBEIF->pfnBEIdToEntry( pOperation->pBECtx, pOperation->pSchemaCtx, entryId, pEntry, VDIR_BACKEND_ENTRY_LOCK_WRITE); BAIL_ON_VMDIR_ERROR(retVal); if (pOperation->pCondWriteCtrl) { retVal = VmDirMatchEntryWithFilter( pOperation, pEntry, pOperation->pCondWriteCtrl->value.condWriteCtrlVal.pszFilter); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "Conditional Write pre-conditions (%s) failed - (%d)", VDIR_SAFE_STRING(pOperation->pCondWriteCtrl->value.condWriteCtrlVal.pszFilter), retVal); } if (modReq->dn.lberbv.bv_val == NULL) // If not already set by the caller { // e.g. delete membership case via index lookup to get EID. retVal = VmDirBervalContentDup(&pEntry->dn, &modReq->dn); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "VmDirBervalContentDup failed - (%d)", retVal); } retVal = VmDirSrvAccessCheck( pOperation, &pOperation->conn->AccessInfo, pEntry, VMDIR_RIGHT_DS_WRITE_PROP); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "VmDirSrvAccessCheck failed - (%u)", retVal); // Apply modify operations to the current entry (in pack format) retVal = VmDirApplyModsToEntryStruct( pOperation->pSchemaCtx, modReq, pEntry, &bDnModified, &pszLocalErrMsg); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "ApplyModsToEntryStruct failed - (%d)(%s)", retVal, pszLocalErrMsg); if (bDnModified) { retVal = pOperation->pBEIF->pfnBEChkIsLeafEntry( pOperation->pBECtx, entryId, &bLeafNode); BAIL_ON_VMDIR_ERROR(retVal); if (bLeafNode == FALSE) { retVal = LDAP_NOT_ALLOWED_ON_NONLEAF; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "Rename of a non-leaf node is not allowed."); } // Verify not a member of any groups retVal = VmDirFindMemberOfAttribute(pEntry, &pAttrMemberOf); if (pAttrMemberOf && pAttrMemberOf->numVals > 0) { retVal = LDAP_UNWILLING_TO_PERFORM; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "Rename of a node with memberships is not allowed."); } } if (pOperation->opType != VDIR_OPERATION_TYPE_REPL) { // Schema check retVal = VmDirSchemaCheck(pEntry); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "Schema check failed - (%u)(%s)", retVal, VDIR_SAFE_STRING(VmDirSchemaCtxGetErrorMsg(pEntry->pSchemaCtx))); // check and read lock dn referenced entries retVal = pOperation->pBEIF->pfnBEChkDNReference(pOperation->pBECtx, pEntry); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "BECheckDnRef, (%u)(%s)", retVal, VDIR_SAFE_STRING(pOperation->pBECtx->pszBEErrorMsg)); } // Execute plugin logic that require final entry image. (Do this for both normal and repl routes) retVal = VmDirExecutePreModifyPlugins(pOperation, pEntry, retVal); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "PreModifyPlugins failed - (%u)", retVal); // Update DB retVal = pOperation->pBEIF->pfnBEEntryModify(pOperation->pBECtx, modReq->mods, pEntry); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "BEEntryModify, (%u)(%s)", retVal, VDIR_SAFE_STRING(pOperation->pBEErrorMsg)); if (bNoRaftLog == FALSE) { // Generate raft log only on the orignal Add/Modify/Delete, but not on the derived operation. // For instance, a delete may cause a Modify on the referenced entry which shouldn't // initiate a raft log generation. retVal = VmDirModifyRaftPreCommit( pEntry->pSchemaCtx, entryId, modReq->dn.bvnorm_val, modReq->mods, pOperation); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "VmDirModifyRaftPreCommit, (%u)(%s)", retVal, VDIR_SAFE_STRING(pOperation->pBEErrorMsg)); } cleanup: VmDirFreeAttribute(pAttrMemberOf); VMDIR_SAFE_FREE_MEMORY(pszLocalErrMsg); return retVal; error: if (retVal == VMDIR_LDAP_ERROR_PRE_CONDITION) { VMDIR_LOG_VERBOSE( LDAP_DEBUG_ANY, "CoreLogicModifyEntry failed, DN = %s, (%u)(%s)", VDIR_SAFE_STRING(modReq->dn.lberbv.bv_val), retVal, VDIR_SAFE_STRING(pszLocalErrMsg)); } else { VMDIR_LOG_ERROR( LDAP_DEBUG_ANY, "CoreLogicModifyEntry failed, DN = %s, (%u)(%s)", VDIR_SAFE_STRING(modReq->dn.lberbv.bv_val), retVal, VDIR_SAFE_STRING(pszLocalErrMsg)); } if (pOperation->ldapResult.pszErrMsg == NULL) { pOperation->ldapResult.pszErrMsg = pszLocalErrMsg; pszLocalErrMsg = NULL; } goto cleanup; }