int VmDirMLDelete( PVDIR_OPERATION pOperation ) { DWORD dwError = 0; PSTR pszLocalErrMsg = NULL; pOperation->pBECtx->pBE = VmDirBackendSelect(pOperation->reqDn.lberbv.bv_val); assert(pOperation->pBECtx->pBE); // AnonymousBind Or in case of a failed bind, do not grant delete access if (pOperation->conn->bIsAnonymousBind || VmDirIsFailedAccessInfo(&pOperation->conn->AccessInfo)) { dwError = LDAP_INSUFFICIENT_ACCESS; BAIL_ON_VMDIR_ERROR_WITH_MSG( dwError, pszLocalErrMsg, "Not bind/authenticate yet" ); } dwError = VmDirInternalDeleteEntry( pOperation ); BAIL_ON_VMDIR_ERROR( dwError ); cleanup: VmDirSendLdapResult( pOperation ); VMDIR_SAFE_FREE_MEMORY( pszLocalErrMsg ); return pOperation->ldapResult.errCode; error: VMDIR_SET_LDAP_RESULT_ERROR( &(pOperation->ldapResult), dwError, pszLocalErrMsg); goto cleanup; }
int VmDirMLModify( PVDIR_OPERATION pOperation ) { DWORD dwError = 0; PSTR pszLocalErrMsg = NULL; pOperation->pBECtx->pBE = VmDirBackendSelect(pOperation->request.modifyReq.dn.lberbv.bv_val); assert(pOperation->pBECtx->pBE); // AnonymousBind Or in case of a failed bind, do not grant modify access if (pOperation->conn->bIsAnonymousBind || VmDirIsFailedAccessInfo(&pOperation->conn->AccessInfo)) { dwError = LDAP_INSUFFICIENT_ACCESS; BAIL_ON_VMDIR_ERROR_WITH_MSG( dwError, pszLocalErrMsg, "Not bind/authenticate yet"); } if (!VmDirValidTxnState(pOperation->pBECtx, pOperation->reqCode)) { dwError = LDAP_UNWILLING_TO_PERFORM; BAIL_ON_VMDIR_ERROR_WITH_MSG(dwError, pszLocalErrMsg, "%s: invaid request for transaction state", __func__); } // Mod request sanity check dwError = _VmDirExternalModsSanityCheck(pOperation, pOperation->request.modifyReq.mods); BAIL_ON_VMDIR_ERROR(dwError); dwError = VmDirInternalModifyEntry(pOperation); BAIL_ON_VMDIR_ERROR(dwError); cleanup: VMDIR_SAFE_FREE_MEMORY(pszLocalErrMsg); return pOperation->ldapResult.errCode; error: VMDIR_SET_LDAP_RESULT_ERROR(&(pOperation->ldapResult), dwError, pszLocalErrMsg); goto cleanup; }
int VmDirMLDelete( PVDIR_OPERATION pOperation ) { DWORD dwError = 0; PSTR pszLocalErrMsg = NULL; pOperation->pBECtx->pBE = VmDirBackendSelect(pOperation->reqDn.lberbv.bv_val); assert(pOperation->pBECtx->pBE); // AnonymousBind Or in case of a failed bind, do not grant delete access if (pOperation->conn->bIsAnonymousBind || VmDirIsFailedAccessInfo(&pOperation->conn->AccessInfo)) { dwError = LDAP_INSUFFICIENT_ACCESS; BAIL_ON_VMDIR_ERROR_WITH_MSG( dwError, pszLocalErrMsg, "Not bind/authenticate yet" ); } if (VmDirRaftDisallowUpdates("Delete")) { dwError = VMDIR_ERROR_UNWILLING_TO_PERFORM; BAIL_ON_VMDIR_ERROR( dwError ); } dwError = VmDirInternalDeleteEntry( pOperation ); BAIL_ON_VMDIR_ERROR( dwError ); if (pOperation->opType == VDIR_OPERATION_TYPE_EXTERNAL) { pOperation->pBEIF->pfnBESetMaxOriginatingUSN(pOperation->pBECtx, pOperation->pBECtx->wTxnUSN); } cleanup: VMDIR_SAFE_FREE_MEMORY( pszLocalErrMsg ); return pOperation->ldapResult.errCode; error: VMDIR_SET_LDAP_RESULT_ERROR( &(pOperation->ldapResult), dwError, pszLocalErrMsg); goto cleanup; }
int VmDirMLSearch( PVDIR_OPERATION pOperation ) { int retVal = 0; PSTR pszLocalErrMsg = NULL; pOperation->pBEIF = VmDirBackendSelect(pOperation->reqDn.lberbv.bv_val); assert(pOperation->pBEIF); retVal = VmDirInternalSearch(pOperation); BAIL_ON_VMDIR_ERROR(retVal); cleanup: VMDIR_SAFE_FREE_MEMORY(pszLocalErrMsg); return pOperation->ldapResult.errCode; error: VMDIR_SET_LDAP_RESULT_ERROR( &(pOperation->ldapResult), retVal, pszLocalErrMsg); goto cleanup; }
int VmDirMLSearch( PVDIR_OPERATION pOperation ) { int retVal = 0; PSTR pszLocalErrMsg = NULL; pOperation->pBEIF = VmDirBackendSelect(pOperation->reqDn.lberbv.bv_val); assert(pOperation->pBEIF); if (pOperation->conn->bIsAnonymousBind && !VmDirIsSearchForDseRootEntry( pOperation )) { retVal = LDAP_INSUFFICIENT_ACCESS; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "Not bind/authenticate yet" ); } // AnonymousBind is handled when retrieving search candidate result // DSE_ROOT_DN and PERSISTED_DSE_ROOT_DN, SCHEMA_NAMING_CONTEXT_DN // SUB_SCHEMA_SUB_ENTRY_DN should allow anonymous bind READ retVal = VmDirInternalSearch( pOperation); BAIL_ON_VMDIR_ERROR(retVal); cleanup: VmDirSendLdapResult( pOperation ); VMDIR_SAFE_FREE_MEMORY(pszLocalErrMsg); return pOperation->ldapResult.errCode; error: VMDIR_SET_LDAP_RESULT_ERROR( &(pOperation->ldapResult), retVal, pszLocalErrMsg); goto cleanup; }
/* * Before modify schema cache, make sure new schema is valid. * 1. schema of pEntry must be live one * 2. create new schema instance via pEntry * 3. check active and new schema compatibility * NOT compatible - reject this operation * Compatible but NO semantic chnage - update schema entry * Compatible and has semantic chnage - update schema entry and cache * 4. make new instance pending in gVdirSchemaGlobals */ DWORD VmDirSchemaCacheModifyPrepare( PVDIR_OPERATION pOperation, VDIR_MODIFICATION* pMods, PVDIR_ENTRY pSchemaEntry ) { DWORD dwError = 0; BOOLEAN bInLock = FALSE; PSTR pszLocalErrMsg = NULL; BOOLEAN bOwnNewInstance = FALSE; BOOLEAN bCompatible = FALSE; // schema compatible BOOLEAN bNeedCachePatch = FALSE; // schema semantic/cache change PVDIR_SCHEMA_INSTANCE pNewInstance = NULL; // DO NOT free, pEntry should take over it. PVDIR_SCHEMA_INSTANCE pEntryOrgSchemaInstance = NULL; if ( !pMods || !pSchemaEntry || !pOperation ) { dwError = ERROR_INVALID_PARAMETER; BAIL_ON_VMDIR_ERROR(dwError); } { PVDIR_MODIFICATION pLocalMods = NULL; PCSTR immutableList[] = VDIR_IMMUTABLE_SCHEMA_ELEMENT_INITIALIZER; int iImmutableSize = sizeof(immutableList)/sizeof(immutableList[0]); for (pLocalMods = pMods; pLocalMods; pLocalMods = pLocalMods->next) { BOOLEAN bImmutableElement = _VmDirIsNameInCaseIgnoreList( pLocalMods->attr.pATDesc->pszName, immutableList, iImmutableSize); if ( bImmutableElement ) { dwError = ERROR_OPERATION_NOT_PERMITTED; BAIL_ON_VMDIR_ERROR_WITH_MSG(dwError, pszLocalErrMsg, "modify (%s) not allowed", pLocalMods->attr.pATDesc->pszName); } } } // make sure pEntry uses live schema if (! vdirIsLiveSchema(pSchemaEntry->pSchemaCtx->pSchema)) { dwError = LDAP_UNWILLING_TO_PERFORM; BAIL_ON_VMDIR_ERROR_WITH_MSG(dwError, pszLocalErrMsg, "Out dated schema"); } pEntryOrgSchemaInstance = pSchemaEntry->pSchemaCtx->pSchema; // instantiate a schema cache - pNewInstance // If this call succeed, do NOT free pNewInstance. pEntry->pSchemaCtx takes it over. dwError = VdirSchemaInstanceInitViaEntry( pSchemaEntry, &pNewInstance); if ( dwError != 0 && pSchemaEntry->pSchemaCtx->pSchema != pNewInstance ) { // we still own pNewInstance and need to free it. bOwnNewInstance = TRUE; } BAIL_ON_VMDIR_ERROR_WITH_MSG(dwError, pszLocalErrMsg, "Entry to schema instance failed (%d)", dwError); // check if two instances are compatible and if schema patching is needed dwError = VmDirSchemaInstancePatchCheck( pEntryOrgSchemaInstance, pNewInstance, &bCompatible, &bNeedCachePatch); BAIL_ON_VMDIR_ERROR_WITH_MSG(dwError, pszLocalErrMsg, "VmDirSchemaInstancePatch (%d)", dwError); if ( !bCompatible ) { dwError = LDAP_UNWILLING_TO_PERFORM; BAIL_ON_VMDIR_ERROR_WITH_MSG(dwError, pszLocalErrMsg, "Schema NOT compatible (%d)", dwError); } if ( !bNeedCachePatch ) { // no semantic change, just update schema entry VMDIR_LOG_INFO( VMDIR_LOG_MASK_ALL, "Prepare schema entry update"); } else { // need schema entry and cache update VMDIR_LOG_INFO( VMDIR_LOG_MASK_ALL, "Prepare schema entry and instance update (%p)", pNewInstance); } VMDIR_LOCK_MUTEX(bInLock, gVdirSchemaGlobals.mutex); if ( bNeedCachePatch ) { gVdirSchemaGlobals.bHasPendingChange = TRUE; } VMDIR_UNLOCK_MUTEX(bInLock, gVdirSchemaGlobals.mutex); cleanup: VMDIR_UNLOCK_MUTEX(bInLock, gVdirSchemaGlobals.mutex); VMDIR_SAFE_FREE_MEMORY(pszLocalErrMsg); return dwError; error: if ( bOwnNewInstance ) { VdirSchemaInstanceFree( pNewInstance ); } VMDIR_SET_LDAP_RESULT_ERROR( &pOperation->ldapResult, dwError, pszLocalErrMsg ); goto cleanup; }
/* * Add a ModifyRequest to pOperation with- * 1. modOp * 2. create attribute with values from pBerValue * * i.e. this function call is equivalent of a modify section in LDIF file * changetype: modify * add/delete/replace: attributeXYZ <<<<<<<<<<<<<<<< modOp * attributeXYZ: value1 <<<<<<<<<<<<<<<<<<<<<<<<<<<<< pBervalue * attributeXYZ: valueN <<<<<<<<<<<<<<<<<<<<<<<<<<<<< iBerValueSize */ DWORD VmDirOperationAddModReq( PVDIR_OPERATION pOperation, int modOp, char * pszAttrName, PVDIR_BERVALUE pBerValue, size_t iBerValueSize ) { DWORD dwError = 0; PSTR pszLocalErrMsg = NULL; VDIR_MODIFICATION * pMod = NULL; ModifyReq * pModReq = &(pOperation->request.modifyReq); size_t iCnt = 0; if ( !pOperation || !pszAttrName || !pBerValue ) { dwError = ERROR_INVALID_PARAMETER; BAIL_ON_VMDIR_ERROR(dwError); } dwError = VmDirAllocateMemory( sizeof( *pMod ), (PVOID *)&(pMod) ); BAIL_ON_VMDIR_ERROR( dwError ); pMod->operation = modOp; pMod->attr.next = NULL; pMod->attr.pATDesc = VmDirSchemaAttrNameToDesc( pOperation->pSchemaCtx, pszAttrName); if ( !pMod->attr.pATDesc ) { dwError = ERROR_NO_SUCH_ATTRIBUTE; BAIL_ON_VMDIR_ERROR_WITH_MSG( dwError, pszLocalErrMsg, "attibute (%s) not defined", pszAttrName ); } // attribute.type.lberbv use (schema cache) inplace memory (we don't fee it). pMod->attr.type.lberbv.bv_val = pMod->attr.pATDesc->pszName; pMod->attr.type.lberbv.bv_len = VmDirStringLenA(pMod->attr.pATDesc->pszName); dwError = VmDirAllocateMemory( sizeof( VDIR_BERVALUE ) * (iBerValueSize + 1 ), (PVOID *)&(pMod->attr.vals) ); BAIL_ON_VMDIR_ERROR( dwError ); for (iCnt = 0; iCnt < iBerValueSize; iCnt++) { dwError = VmDirAllocateAndCopyMemory( pBerValue[iCnt].lberbv_val, pBerValue[iCnt].lberbv_len, (PVOID *)&(pMod->attr.vals[iCnt].lberbv.bv_val)); BAIL_ON_VMDIR_ERROR( dwError ); pMod->attr.vals[iCnt].lberbv.bv_len = pBerValue[iCnt].lberbv_len; pMod->attr.vals[iCnt].bOwnBvVal = TRUE; pMod->attr.numVals++; } pMod->next = pModReq->mods; pModReq->mods = pMod; // pOpeartion->request.modifyRequest takes over pMod pModReq->numMods++; cleanup: VMDIR_SAFE_FREE_MEMORY(pszLocalErrMsg); return dwError; error: if (pMod) { VmDirModificationFree( pMod ); } VMDIR_SET_LDAP_RESULT_ERROR( &(pOperation->ldapResult), dwError, pszLocalErrMsg ); goto cleanup; }
/* InternalSearch: Interface that can be used "internally" by the server code. One of the main differences between * this function and MLSearch 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 VmDirInternalSearch( PVDIR_OPERATION pOperation ) { int retVal = LDAP_SUCCESS; DWORD dwEntryCount = 0; BOOLEAN bHasTxn = FALSE; PSTR pszLocalErrMsg = NULL; ENTRYID eId = 0; ENTRYID* pValidatedEntries = NULL; PVDIR_LDAP_RESULT pResult = &(pOperation->ldapResult); PVDIR_OPERATION_ML_METRIC pMLMetrics = NULL; assert(pOperation && pOperation->pBEIF); pMLMetrics = &pOperation->MLMetrics; VMDIR_COLLECT_TIME(pMLMetrics->iMLStartTime); // compute required access for this search ComputeRequiredAccess(&pOperation->request.searchReq); // Normalize (base) DN retVal = VmDirNormalizeDN( &(pOperation->reqDn), pOperation->pSchemaCtx ); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "DN normalization failed - (%u)(%s)", retVal, VDIR_SAFE_STRING(VmDirSchemaCtxGetErrorMsg(pOperation->pSchemaCtx)) ); if (VmDirHandleSpecialSearch( pOperation, pResult )) // TODO, add &pszLocalErrMsg { retVal = pResult->errCode ? pResult->errCode : pResult->vmdirErrCode; BAIL_ON_VMDIR_ERROR_WITH_MSG(retVal, pszLocalErrMsg, "Special search failed - (%u)", retVal); goto cleanup; // done special search } if (pOperation->dbCopyCtrl) { retVal = VmDirExecDbCopyCtrl(pOperation); BAIL_ON_VMDIR_ERROR(retVal); } if (pOperation->syncReqCtrl != NULL) // Replication { pOperation->opType = VDIR_OPERATION_TYPE_REPL; } if (pOperation->pReplAgrDisableCtrl) { retVal = VmDirExecReplAgrEnableDisableCtrl(BERVAL_NORM_VAL(pOperation->reqDn), FALSE); BAIL_ON_VMDIR_ERROR(retVal); } else if (pOperation->pReplAgrEnableCtrl) { retVal = VmDirExecReplAgrEnableDisableCtrl(BERVAL_NORM_VAL(pOperation->reqDn), TRUE); BAIL_ON_VMDIR_ERROR(retVal); } // If base is not ROOT, read lock the base object (DnToEntryId index entry) to make sure it exists, and it does // not get deleted during this search processing. if (pOperation->reqDn.lberbv.bv_len != 0) { VMDIR_COLLECT_TIME(pMLMetrics->iBETxnBeginStartTime); retVal = pOperation->pBEIF->pfnBETxnBegin( pOperation->pBECtx, VDIR_BACKEND_TXN_READ ); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "txn begin (%u)(%s)", retVal, VDIR_SAFE_STRING(pOperation->pBEErrorMsg)); bHasTxn = TRUE; VMDIR_COLLECT_TIME(pMLMetrics->iBETxnBeginEndTime); // Lookup in the DN index. retVal = pOperation->pBEIF->pfnBEDNToEntryId( pOperation->pBECtx, &(pOperation->reqDn), &eId ); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "DNToEID (%u)(%s)", retVal, VDIR_SAFE_STRING(pOperation->pBEErrorMsg)); } // start txn if not has one already. if (! pOperation->pBECtx->pBEPrivate) { retVal = pOperation->pBEIF->pfnBETxnBegin( pOperation->pBECtx, VDIR_BACKEND_TXN_READ ); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "txn begin (%u)(%s)", retVal, VDIR_SAFE_STRING(pOperation->pBEErrorMsg)); bHasTxn = TRUE; } retVal = AppendDNFilter( pOperation ); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "Appending DN filter failed."); /* * TODO cleanup During background thread implementation of page search */ if (gVmdirGlobals.bPagedSearchReadAhead) { if (pOperation->showPagedResultsCtrl != NULL && !IsNullOrEmptyString(pOperation->showPagedResultsCtrl->value.pagedResultCtrlVal.cookie)) { retVal = VmDirPagedSearchCacheRead( pOperation->showPagedResultsCtrl->value.pagedResultCtrlVal.cookie, &pValidatedEntries, &dwEntryCount); BAIL_ON_VMDIR_ERROR(retVal); } } if (pOperation->showPagedResultsCtrl == NULL) { retVal = _VmDirInternalNormalSearch(pOperation); BAIL_ON_VMDIR_ERROR(retVal); } else { retVal = _VmDirInternalPagedSearch(pOperation); BAIL_ON_VMDIR_ERROR(retVal); } /* * TODO cleanup During background thread implementation of page search */ if (pValidatedEntries != NULL) { retVal = ProcessPreValidatedEntries( pOperation, dwEntryCount, pValidatedEntries); BAIL_ON_VMDIR_ERROR(retVal); } VMDIR_COLLECT_TIME(pMLMetrics->iBETxnCommitStartTime); 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; VMDIR_COLLECT_TIME(pMLMetrics->iBETxnCommitEndTime); cleanup: // collect metrics VMDIR_COLLECT_TIME(pMLMetrics->iMLEndTime); VmDirInternalMetricsUpdate(pOperation); VmDirInternalMetricsLogInefficientOp(pOperation); VMDIR_SAFE_FREE_MEMORY(pValidatedEntries); 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; }
/* 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; }
/* * Mod request sanity check * 1. modify attribute is defined * 2. attribute can be modified (we only enforce this for external operation) */ static int _VmDirExternalModsSanityCheck( PVDIR_OPERATION pOp, PVDIR_MODIFICATION pMods ) { int retVal = LDAP_SUCCESS; PSTR pszLocalErrMsg = NULL; PVDIR_MODIFICATION pLocalMod = NULL; for (pLocalMod = pMods; pLocalMod != NULL; pLocalMod = pLocalMod->next) { if (pLocalMod->attr.pATDesc == NULL && (pLocalMod->attr.pATDesc = VmDirSchemaAttrNameToDesc(pOp->pSchemaCtx, pLocalMod->attr.type.lberbv.bv_val)) == NULL ) { retVal = VMDIR_ERROR_UNDEFINED_TYPE; BAIL_ON_VMDIR_ERROR_WITH_MSG(retVal, (pszLocalErrMsg), "Undefined attribute (%s)", VDIR_SAFE_STRING(pLocalMod->attr.type.lberbv.bv_val)); } // Make sure attribute can be modified if (pLocalMod->attr.pATDesc->bNoUserModifiable == TRUE && pOp->conn->AccessInfo.bindEID != DEFAULT_ADMINISTRATOR_ENTRY_ID // exempt default administrator ) { retVal = VMDIR_ERROR_DATA_CONSTRAINT_VIOLATION; BAIL_ON_VMDIR_ERROR_WITH_MSG(retVal, (pszLocalErrMsg), "attribute (%s) can not be modified", VDIR_SAFE_STRING(pLocalMod->attr.pATDesc->pszName)); } if (pLocalMod->operation == MOD_OP_ADD || pLocalMod->operation == MOD_OP_REPLACE) { // ADD or REPLACE principal name, validate its syntax. if (VmDirStringCompareA(pLocalMod->attr.type.lberbv_val, ATTR_KRB_UPN, FALSE) == 0 || VmDirStringCompareA(pLocalMod->attr.type.lberbv_val, ATTR_KRB_SPN, FALSE) == 0) { retVal = VmDirValidatePrincipalName(&(pLocalMod->attr), &pszLocalErrMsg); BAIL_ON_VMDIR_ERROR(retVal); } else if (VmDirStringCompareA(pLocalMod->attr.type.lberbv_val, ATTR_OBJECT_SECURITY_DESCRIPTOR, FALSE) == 0) { BOOLEAN bReturn = FALSE; bReturn = VmDirValidRelativeSecurityDescriptor( (PSECURITY_DESCRIPTOR_RELATIVE)pLocalMod->attr.vals[0].lberbv_val, (ULONG)pLocalMod->attr.vals[0].lberbv_len, OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION); if (!bReturn) { retVal = VMDIR_ERROR_BAD_ATTRIBUTE_DATA; BAIL_ON_VMDIR_ERROR(retVal); } } } else if (pLocalMod->operation == MOD_OP_DELETE) { if (VmDirStringCompareA(pLocalMod->attr.type.lberbv_val, ATTR_OBJECT_SECURITY_DESCRIPTOR, FALSE) == 0) { retVal = VMDIR_ERROR_DATA_CONSTRAINT_VIOLATION; BAIL_ON_VMDIR_ERROR(retVal); } } } cleanup: VMDIR_SAFE_FREE_MEMORY(pszLocalErrMsg); return retVal; error: VMDIR_SET_LDAP_RESULT_ERROR(&(pOp->ldapResult), retVal, pszLocalErrMsg); goto cleanup; }
/* VmDirInternalModifyEntry: Interface that can be used "internally" by the server code, e.g. to modify schema, indices, * config etc. entries in the BDB store. One of the main differences between this function and MLModify 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 VmDirInternalModifyEntry( PVDIR_OPERATION pOperation ) { int retVal = LDAP_SUCCESS; VDIR_ENTRY entry = {0}; PVDIR_ENTRY pEntry = NULL; ModifyReq* modReq = NULL; ENTRYID entryId = 0; BOOLEAN bHasTxn = FALSE; PSTR pszLocalErrMsg = NULL; PVDIR_OPERATION_ML_METRIC pMLMetrics = NULL; assert(pOperation && pOperation->pBEIF); 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"); } modReq = &(pOperation->request.modifyReq); // make sure we have minimum DN length if (modReq->dn.lberbv_len < 3) { retVal = VMDIR_ERROR_INVALID_REQUEST; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "Invalid DN length - (%u)", modReq->dn.lberbv_len); } // Normalize DN retVal = VmDirNormalizeDN(&(modReq->dn), pOperation->pSchemaCtx); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "DN normalization failed - (%u)(%s)", retVal, VDIR_SAFE_STRING(VmDirSchemaCtxGetErrorMsg(pOperation->pSchemaCtx))); // Acquire schema modification mutex retVal = VmDirSchemaModMutexAcquire(pOperation); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "Failed to lock schema mod mutex", retVal); if (pOperation->opType != VDIR_OPERATION_TYPE_REPL) { // Generate mods based on MODN request retVal = VmDirGenerateRenameAttrsMods(pOperation); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "GenerateDeleteAttrsMods failed - (%u)", retVal); } 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 plugin logic VMDIR_COLLECT_TIME(pMLMetrics->iPrePluginsStartTime); retVal = VmDirExecutePreModApplyModifyPlugins(pOperation, NULL, retVal); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "PreModApplyModify plugin failed - (%u)", retVal); VMDIR_COLLECT_TIME(pMLMetrics->iPrePlugunsEndTim); // Normalize attribute values in mods retVal = VmDirNormalizeMods(pOperation->pSchemaCtx, modReq->mods, &pszLocalErrMsg); BAIL_ON_VMDIR_ERROR(retVal); // Read current entry from DB retVal = pOperation->pBEIF->pfnBEDNToEntryId(pOperation->pBECtx, &(modReq->dn), &entryId); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "BEEntryModify (%u)(%s)", retVal, VDIR_SAFE_STRING(pOperation->pBEErrorMsg)); pEntry = &entry; retVal = VmDirModifyEntryCoreLogic( pOperation, &pOperation->request.modifyReq, entryId, pOperation->bNoRaftLog, pEntry); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "CoreLogicModifyEntry failed. (%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, "Modify Entry (%s) blob size %d logIndex %llu", VDIR_SAFE_STRING(pEntry->dn.lberbv_val), pEntry->encodedSize, pOperation->logIndex); } cleanup: if (retVal == 0) { int iPostCommitPluginRtn = 0; VMDIR_COLLECT_TIME(pMLMetrics->iPostPluginsStartTime); // Execute post modify plugin logic iPostCommitPluginRtn = VmDirExecutePostModifyCommitPlugins(pOperation, &entry, retVal); if (iPostCommitPluginRtn != LDAP_SUCCESS && iPostCommitPluginRtn != pOperation->ldapResult.errCode) // pass through { VMDIR_LOG_ERROR( LDAP_DEBUG_ANY, "InternalModifyEntry: VdirExecutePostModifyCommitPlugins - code(%d)", iPostCommitPluginRtn); } VMDIR_COLLECT_TIME(pMLMetrics->iPostPlugunsEndTime); } // Release schema modification mutex (VOID)VmDirSchemaModMutexRelease(pOperation); // collect metrics VMDIR_COLLECT_TIME(pMLMetrics->iMLEndTime); VmDirInternalMetricsUpdate(pOperation); VmDirInternalMetricsLogInefficientOp(pOperation); 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 VmDirPerformDelete( PVDIR_OPERATION pOperation ) { int retVal = LDAP_SUCCESS; DeleteReq * dr = &(pOperation->request.deleteReq); BOOLEAN bRefSent = FALSE; PSTR pszRefStr = NULL; PSTR pszLocalErrMsg = NULL; // Get entry DN. 'm' => pOperation->reqDn.lberbv.bv_val points to DN within (in-place) ber if ( ber_scanf( pOperation->ber, "m", &(pOperation->reqDn.lberbv) ) == LBER_ERROR ) { VMDIR_LOG_ERROR( LDAP_DEBUG_ARGS, "PerformDelete: ber_scanf failed" ); retVal = LDAP_NOTICE_OF_DISCONNECT; BAIL_ON_VMDIR_ERROR( retVal ); } VMDIR_LOG_INFO( LDAP_DEBUG_ARGS, "Delete Request: dn (%s)", pOperation->reqDn.lberbv.bv_val ); memset( dr, 0, sizeof( DeleteReq )); // NOTE: pOperation->reqDn.lberbv.bv_val is NULL terminated (TODO, verify this) dr->dn.lberbv.bv_val = pOperation->reqDn.lberbv.bv_val; dr->dn.lberbv.bv_len = pOperation->reqDn.lberbv.bv_len; retVal = ParseRequestControls(pOperation, &pOperation->ldapResult); BAIL_ON_VMDIR_ERROR(retVal); if (pOperation->manageDsaITCtrl == NULL && (gVmdirGlobals.dwEnableRaftReferral & VMDIR_RAFT_ENABLE_UPDATE_REFERRAL) && VmDirRaftNeedReferral(pOperation->reqDn.lberbv.bv_val)) { retVal = VmDirAllocateStringPrintf(&pszRefStr, "%s", pOperation->reqDn.lberbv.bv_len > 0 ? pOperation->reqDn.lberbv.bv_val:""); BAIL_ON_VMDIR_ERROR(retVal); VmDirSendLdapReferralResult(pOperation, pszRefStr, &bRefSent); if (bRefSent) { goto cleanup; } // Referral is not sent because the raft state might have changed. Go throughput normal procedure. } retVal = VmDirMLDelete( pOperation ); BAIL_ON_VMDIR_ERROR(retVal); cleanup: if (retVal != LDAP_NOTICE_OF_DISCONNECT && bRefSent == FALSE) { VmDirSendLdapResult( pOperation ); } VMDIR_SAFE_FREE_MEMORY(pszRefStr); VMDIR_SAFE_FREE_MEMORY(pszLocalErrMsg); return retVal; error: VMDIR_SET_LDAP_RESULT_ERROR(&pOperation->ldapResult, retVal, pszLocalErrMsg); goto cleanup; }
int VmDirInternalSearch( PVDIR_OPERATION pOperation ) { int retVal = LDAP_SUCCESS; ENTRYID eId = 0; int deadLockRetries = 0; BOOLEAN bHasTxn = FALSE; PSTR pszLocalErrMsg = NULL; PVDIR_LDAP_RESULT pResult = &(pOperation->ldapResult); assert(pOperation && pOperation->pBEIF); // Normalize (base) DN retVal = VmDirNormalizeDN( &(pOperation->reqDn), pOperation->pSchemaCtx ); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "DN normalization failed - (%u)(%s)", retVal, VDIR_SAFE_STRING(VmDirSchemaCtxGetErrorMsg(pOperation->pSchemaCtx)) ); if (VmDirHandleSpecialSearch( pOperation, pResult )) // TODO, add &pszLocalErrMsg { retVal = pResult->errCode; BAIL_ON_VMDIR_ERROR_WITH_MSG(retVal, pszLocalErrMsg, "Special search failed - (%u)", retVal); goto cleanup; // done special search } if (pOperation->syncReqCtrl != NULL) // Replication { pOperation->lowestPendingUncommittedUsn = pOperation->pBEIF->pfnBEGetLeastOutstandingUSN( pOperation->pBECtx, FALSE ); VmDirLog( LDAP_DEBUG_REPL, "Replication request USN (%u)", pOperation->lowestPendingUncommittedUsn ); } // If base is not ROOT, read lock the base object (DnToEntryId index entry) to make sure it exists, and it does // not get deleted during this search processing. if (pOperation->reqDn.lberbv.bv_len != 0) { // ************************************************************************************ // 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 { retVal = pOperation->pBEIF->pfnBETxnBegin( pOperation->pBECtx, VDIR_BACKEND_TXN_READ ); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "txn begin (%u)(%s)", retVal, VDIR_SAFE_STRING(pOperation->pBEErrorMsg)); bHasTxn = TRUE; // Lookup in the DN index. retVal = pOperation->pBEIF->pfnBEDNToEntryId( pOperation->pBECtx, &(pOperation->reqDn), &eId ); if (retVal != 0) { switch (retVal) { case VMDIR_ERROR_BACKEND_DEADLOCK: goto txnretry; // Possible retry. default: BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "DNToEID (%u)(%s)", retVal, VDIR_SAFE_STRING(pOperation->pBEErrorMsg)); } } } // ************************************************************************************ // transaction retry loop end. // ************************************************************************************ } // start txn if not has one already. if (! pOperation->pBECtx->pBEPrivate) { retVal = pOperation->pBEIF->pfnBETxnBegin( pOperation->pBECtx, VDIR_BACKEND_TXN_READ ); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "txn begin (%u)(%s)", retVal, VDIR_SAFE_STRING(pOperation->pBEErrorMsg)); bHasTxn = TRUE; } retVal = AppendDNFilter( pOperation ); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "Appending DN filter failed."); retVal = BuildCandidateList( pOperation, pOperation->request.searchReq.filter); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "BuildCandidateList failed."); if (pOperation->request.searchReq.filter->computeResult == FILTER_RES_TRUE) { retVal = VMDIR_ERROR_UNWILLING_TO_PERFORM; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "Full scan of Entry DB is required. Refine your search."); } retVal = ProcessCandidateList( pOperation ); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "ProcessCandidateList failed. (%u)(%s)", retVal, VDIR_SAFE_STRING( pOperation->ldapResult.pszErrMsg) ); 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; cleanup: 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; }