void VmDirFreeEntry( PVDIR_ENTRY pEntry ) { VmDirFreeEntryContent(pEntry); VMDIR_SAFE_FREE_MEMORY(pEntry); }
static BOOLEAN _VmDirIsBenignReplConflict( PVDIR_ENTRY pEntry, PVDIR_ATTRIBUTE pAttr ) { int i = 0; CHAR excludeAttrs[] = {ATTR_USN_CHANGED}; DWORD dwError = 0; BOOLEAN bIsBenign = FALSE; VDIR_ENTRY consumerEntry = {0}; if (pEntry->eId) { if (!consumerEntry.dn.lberbv_val) { PVDIR_BACKEND_INTERFACE pBE = NULL; pBE = VmDirBackendSelect(NULL); assert(pBE); dwError = pBE->pfnBESimpleIdToEntry(pEntry->eId, &consumerEntry); BAIL_ON_VMDIR_ERROR(dwError); } for (i=0; i< VMDIR_ARRAY_SIZE(excludeAttrs); i++) { if (VmDirStringCompareA(pAttr->type.lberbv_val, &excludeAttrs[i], FALSE) == 0) { bIsBenign = TRUE; goto cleanup; } } bIsBenign = VmDirIsSameConsumerSupplierEntryAttr(pAttr, pEntry, &consumerEntry); } cleanup: VmDirFreeEntryContent(&consumerEntry); return bIsBenign; error: VMDIR_LOG_ERROR(VMDIR_LOG_MASK_ALL, "failed, error (%d)", dwError); goto cleanup; }
void VmDirFreeEntryArrayContent( PVDIR_ENTRY_ARRAY pEntryAry ) { size_t iCnt = 0; if ( pEntryAry ) { for (iCnt = 0; iCnt < pEntryAry->iSize; iCnt++) { VmDirFreeEntryContent((pEntryAry->pEntry)+iCnt); } VMDIR_SAFE_FREE_MEMORY(pEntryAry->pEntry); pEntryAry->iSize = 0; } }
static BOOLEAN _VmDirPagedSearchProcessEntry( PVDIR_OPERATION pOperation, ENTRYID eId ) { DWORD dwError = 0; VDIR_ENTRY srEntry = {0}; BOOLEAN bInclude = FALSE; dwError = pOperation->pBEIF->pfnBESimpleIdToEntry(eId, &srEntry); if (dwError == 0) { if (CheckIfEntryPassesFilter(pOperation, &srEntry, pOperation->request.searchReq.filter) == FILTER_RES_TRUE) { bInclude = TRUE; } VmDirFreeEntryContent(&srEntry); } return bInclude; }
/* MdbEIdToEntry: For a given entry ID, reads an entry from the entry DB. * * Returns: BE error - BACKEND_ERROR, BACKEND OPERATIONS, BACKEND_ENTRY_NOTFOUND * */ DWORD VmDirMDBEIdToEntry( PVDIR_BACKEND_CTX pBECtx, PVDIR_SCHEMA_CTX pSchemaCtx, ENTRYID eId, PVDIR_ENTRY pEntry, VDIR_BACKEND_ENTRY_LOCKTYPE entryLockType) { DWORD dwError = 0; VDIR_DB mdbDBi = 0; PVDIR_DB_TXN pTxn = NULL; VDIR_DB_DBT key = {0}; VDIR_DB_DBT value = {0}; unsigned char eIdBytes[sizeof( ENTRYID )] = {0}; unsigned char* pszBlob = NULL; assert(pBECtx && pBECtx->pBEPrivate && pSchemaCtx && pEntry); pTxn = (PVDIR_DB_TXN)pBECtx->pBEPrivate; mdbDBi = gVdirMdbGlobals.mdbEntryDB.pMdbDataFiles[0].mdbDBi; // Set key key.mv_data = &eIdBytes[0]; MDBEntryIdToDBT(eId, &key); if ((dwError = mdb_get(pTxn, mdbDBi, &key, &value) ) != 0) { dwError = MDBToBackendError(dwError, MDB_NOTFOUND, ERROR_BACKEND_ENTRY_NOTFOUND, pBECtx, "EIDToEntry"); BAIL_ON_VMDIR_ERROR( dwError ); } if ((dwError = VmDirAllocateMemory( value.mv_size, (PVOID *)&pszBlob)) != 0) { dwError = ERROR_BACKEND_OPERATIONS; BAIL_ON_VMDIR_ERROR( dwError ); } if ((dwError = VmDirCopyMemory(pszBlob, value.mv_size, value.mv_data, value.mv_size)) != 0) { dwError = ERROR_BACKEND_OPERATIONS; BAIL_ON_VMDIR_ERROR( dwError ); } // encodedEntry takes over pszBlob pEntry->encodedEntry = pszBlob; pszBlob = NULL; pEntry->eId = eId; dwError = VmDirDecodeEntry(pSchemaCtx, pEntry ); BAIL_ON_VMDIR_ERROR(dwError); cleanup: return dwError; error: VMDIR_LOG_ERROR( LDAP_DEBUG_BACKEND, "VmDirMDBEIdToEntry, eid(%u) failed (%u)", eId, dwError); VMDIR_SAFE_FREE_MEMORY(pszBlob); VmDirFreeEntryContent( pEntry ); VMDIR_SET_BACKEND_ERROR(dwError); // if dwError no in BE space, set to ERROR_BACKEND_ERROR goto cleanup; }
static DWORD ProcessPreValidatedEntries( PVDIR_OPERATION pOperation, DWORD dwEntryCount, ENTRYID *pValidatedEntries ) { DWORD i = 0; DWORD dwError = 0; DWORD dwSentEntries = 0; BOOLEAN bInternalSearch = FALSE; BOOLEAN bStoreRsltInMem = FALSE; VDIR_ENTRY srEntry = {0}; PVDIR_ENTRY pSrEntry = NULL; if (dwEntryCount == 0) { goto cleanup; } bInternalSearch = pOperation->opType == VDIR_OPERATION_TYPE_INTERNAL; bStoreRsltInMem = pOperation->request.searchReq.bStoreRsltInMem; if (bInternalSearch || bStoreRsltInMem) { VmDirFreeEntryArrayContent(&pOperation->internalSearchEntryArray); dwError = VmDirAllocateMemory( sizeof(VDIR_ENTRY) * (dwEntryCount + 1), (PVOID*)&pOperation->internalSearchEntryArray.pEntry); BAIL_ON_VMDIR_ERROR(dwError); } for (; i < dwEntryCount; ++i) { pSrEntry = bInternalSearch || bStoreRsltInMem ? (pOperation->internalSearchEntryArray.pEntry + pOperation->internalSearchEntryArray.iSize) : &srEntry; dwError = pOperation->pBEIF->pfnBESimpleIdToEntry( pValidatedEntries[i], pSrEntry); if (dwError != 0) { // Ignore errors resolving ENTRYIDs. VMDIR_LOG_WARNING( VMDIR_LOG_MASK_ALL, "%s pfnBESimpleIdToEntry EID(%u), error (%u)", __FUNCTION__, pValidatedEntries[i], dwError); continue; } dwError = VmDirBuildComputedAttribute(pOperation, pSrEntry); BAIL_ON_VMDIR_ERROR(dwError); dwError = VmDirSendSearchEntry(pOperation, pSrEntry); if (dwError == VMDIR_ERROR_INSUFFICIENT_ACCESS) { VMDIR_LOG_WARNING( VMDIR_LOG_MASK_ALL, "Access deny on search entry result [%s,%d] (bindedDN-%s) (targetDn-%s)", __FILE__, __LINE__, pOperation->conn->AccessInfo.pszBindedDn, pSrEntry->dn.lberbv.bv_val); // make sure search continues dwError = 0; } BAIL_ON_VMDIR_ERROR(dwError); if (pSrEntry->bSearchEntrySent) { dwSentEntries++; if (bInternalSearch || bStoreRsltInMem) { pOperation->internalSearchEntryArray.iSize++; pSrEntry = NULL; // EntryArray takes over *pSrEntry content } } VmDirFreeEntryContent(pSrEntry); pSrEntry = NULL; } dwError = SetPagedSearchCookie( pOperation, pValidatedEntries[dwEntryCount - 1], 0); BAIL_ON_VMDIR_ERROR(dwError); cleanup: pOperation->dwSentEntries = dwSentEntries; VmDirFreeEntryContent(pSrEntry); return dwError; error: goto cleanup; }
static int VmDirProcessCandidateList( VDIR_OPERATION * pOperation ) { int retVal = LDAP_SUCCESS; int i = 0; VDIR_CANDIDATES * cl = pOperation->request.searchReq.filter->candidates; VDIR_ENTRY srEntry = {0}; VDIR_ENTRY * pSrEntry = NULL; int numSentEntries = 0; BOOLEAN bExternalSearch = FALSE; BOOLEAN bInternalSearch = FALSE; BOOLEAN bStoreRsltInMem = FALSE; BOOLEAN bPageResultsCtrl = FALSE; DWORD dwPageSize = 0; ENTRYID lastEID = 0; /* * If the page size is greater than or equal to the sizeLimit value, * the server should ignore the control as the request can be satisfied in a single page. */ if (pOperation->showPagedResultsCtrl && (pOperation->request.searchReq.sizeLimit == 0 || pOperation->showPagedResultsCtrl->value.pagedResultCtrlVal.pageSize < (DWORD)pOperation->request.searchReq.sizeLimit)) { VmDirLog( LDAP_DEBUG_TRACE, "showPagedResultsCtrl applies to this query." ); bPageResultsCtrl = TRUE; dwPageSize = pOperation->showPagedResultsCtrl->value.pagedResultCtrlVal.pageSize; lastEID = atoll(pOperation->showPagedResultsCtrl->value.pagedResultCtrlVal.cookie); pOperation->showPagedResultsCtrl->value.pagedResultCtrlVal.cookie[0] = '\0'; VmDirSortCandidateList(cl); // sort candidate list if not yet sorted } bExternalSearch = pOperation->opType == VDIR_OPERATION_TYPE_EXTERNAL; bInternalSearch = pOperation->opType == VDIR_OPERATION_TYPE_INTERNAL; bStoreRsltInMem = pOperation->request.searchReq.bStoreRsltInMem; if (cl && cl->size > 0) { if (bInternalSearch || bStoreRsltInMem) { //TODO, we should have a hard limit on the cl->size we handle VmDirFreeEntryArrayContent(&pOperation->internalSearchEntryArray); retVal = VmDirAllocateMemory( sizeof(VDIR_ENTRY) * cl->size, (PVOID*)&pOperation->internalSearchEntryArray.pEntry); BAIL_ON_VMDIR_ERROR(retVal); } for (i = 0, numSentEntries = 0; (i < cl->size) && (pOperation->request.searchReq.sizeLimit == 0 /* unlimited */ || numSentEntries < pOperation->request.searchReq.sizeLimit); i++) { if (bExternalSearch && VmDirdState() == VMDIRD_STATE_SHUTDOWN && pOperation->syncReqCtrl == NULL) { retVal = LDAP_UNAVAILABLE; // stop all external search ops, except replication pull goto cleanup; } if (!gVmdirGlobals.bPagedSearchReadAhead) { //skip entries we sent before in sorted cl->eIds. if (bPageResultsCtrl && cl->eIds[i] <= lastEID) { continue; } } pSrEntry = bInternalSearch || bStoreRsltInMem ? (pOperation->internalSearchEntryArray.pEntry + pOperation->internalSearchEntryArray.iSize) : &srEntry; retVal = pOperation->pBEIF->pfnBEIdToEntry( pOperation->pBECtx, pOperation->pSchemaCtx, cl->eIds[i], pSrEntry, VDIR_BACKEND_ENTRY_LOCK_READ); if (retVal) { // Ignore BdbEIdToEntry errors. VMDIR_LOG_WARNING( VMDIR_LOG_MASK_ALL, "ProcessCandiateList BEIdToEntry EID(%u), error (%u)", cl->eIds[i], retVal); continue; } if (CheckIfEntryPassesFilter(pOperation, pSrEntry, pOperation->request.searchReq.filter) == FILTER_RES_TRUE) { BOOLEAN bSendEntry = TRUE; CHAR sha1Digest[SHA_DIGEST_LENGTH] = {0}; retVal = VmDirBuildComputedAttribute( pOperation, pSrEntry ); BAIL_ON_VMDIR_ERROR( retVal ); if (pOperation->digestCtrl) { retVal = VmDirEntrySHA1Digest(pSrEntry, sha1Digest); BAIL_ON_VMDIR_ERROR(retVal); if (memcmp(sha1Digest, pOperation->digestCtrl->value.digestCtrlVal.sha1Digest, SHA_DIGEST_LENGTH) == 0) { bSendEntry = FALSE; VMDIR_LOG_VERBOSE( VMDIR_LOG_MASK_ALL,"%s digest match %s", __FUNCTION__, pSrEntry->dn.lberbv.bv_val); } else { VMDIR_LOG_VERBOSE( VMDIR_LOG_MASK_ALL,"%s digest mismatch %s", __FUNCTION__, pSrEntry->dn.lberbv.bv_val); } } if (bSendEntry) { retVal = VmDirSendSearchEntry(pOperation, pSrEntry); if (retVal == VMDIR_ERROR_INSUFFICIENT_ACCESS) { VMDIR_LOG_WARNING( VMDIR_LOG_MASK_ALL, "Access deny on search entry result [%s,%d] (bindedDN-%s) (targetDn-%s)\n", __FILE__, __LINE__, pOperation->conn->AccessInfo.pszBindedDn, pSrEntry->dn.lberbv.bv_val); // make sure search continues retVal = 0; } BAIL_ON_VMDIR_ERROR( retVal ); if (pSrEntry->bSearchEntrySent) { numSentEntries++; if (bInternalSearch || bStoreRsltInMem) { pOperation->internalSearchEntryArray.iSize++; pSrEntry = NULL; // EntryArray takes over *pSrEntry content if (pOperation->internalSearchEntryArray.iSize > gVmdirServerGlobals.dwMaxInternalSearchLimit) { BAIL_WITH_VMDIR_ERROR(retVal, VMDIR_ERROR_INTERNAL_SEARCH_LIMIT); } } } } } //We have sent one page size of entries, so we can break here if (bPageResultsCtrl && numSentEntries == dwPageSize) { retVal = SetPagedSearchCookie(pOperation, cl->eIds[i], i); BAIL_ON_VMDIR_ERROR(retVal); break; } VmDirFreeEntryContent( pSrEntry ); pSrEntry = NULL; // Reset to NULL so that DeleteEntry is no-op. } VMDIR_LOG_VERBOSE( LDAP_DEBUG_FILTER, "(%d) candiates processed and (%d) entries sent", cl->size, numSentEntries); } if ( pOperation->request.searchReq.sizeLimit && numSentEntries < pOperation->request.searchReq.sizeLimit && pOperation->pBECtx->iPartialCandidates) { retVal = LDAP_UNWILLING_TO_PERFORM; VMDIR_LOG_ERROR(VMDIR_LOG_MASK_ALL, "ProcessCandiateList may return none or paritial requested entries with sizelimit %d", pOperation->request.searchReq.sizeLimit); } #ifndef REPLICATION_V2 retVal = VmDirUpdateSyncDoneCtl( pOperation, numSentEntries); BAIL_ON_VMDIR_ERROR(retVal); #endif cleanup: pOperation->dwSentEntries = numSentEntries; VmDirFreeEntryContent(pSrEntry); return retVal; error: VMDIR_LOG_ERROR(VMDIR_LOG_MASK_ALL, "ProcessCandiateList failed. (%u)", retVal); goto cleanup; }
void VmDirFreeEntryContent( VDIR_ENTRY * e ) { VmDirLog( LDAP_DEBUG_TRACE, "DeleteEntry: Begin" ); if ( e ) { VmDirLog( LDAP_DEBUG_TRACE, "DeleteEntry: DN = %s", e ? (e->dn.lberbv.bv_val ? e->dn.lberbv.bv_val : "") : "" ); if (e->pParentEntry) { VmDirFreeEntryContent(e->pParentEntry); VMDIR_SAFE_FREE_MEMORY(e->pParentEntry); } VmDirFreeLDAPDNContent( &(e->ldapDN) ); VmDirFreeBervalContent( &(e->dn) ); VmDirFreeBervalContent( &(e->pdn) ); VmDirFreeBervalContent( &(e->newpdn) ); if (e->allocType == ENTRY_STORAGE_FORMAT_PACK) { PVDIR_ATTRIBUTE pCurrAttr = NULL; PVDIR_ATTRIBUTE pTempAttr = NULL; pCurrAttr = e->attrs; while(pCurrAttr != NULL) { pTempAttr = pCurrAttr->next; VmDirFreeMetaData(pCurrAttr->pMetaData); pCurrAttr = pTempAttr; } VMDIR_SAFE_FREE_MEMORY( e->encodedEntry ); VmDirFreeBervalArrayContent(e->bvs, e->usNumBVs); VMDIR_SAFE_FREE_MEMORY( e->bvs ); VMDIR_SAFE_FREE_MEMORY( e->savedAttrsPtr ); } else if (e->allocType == ENTRY_STORAGE_FORMAT_NORMAL) { VDIR_ATTRIBUTE * currAttr = NULL; VDIR_ATTRIBUTE * tmpAttr = NULL; VMDIR_SAFE_FREE_MEMORY( e->encodedEntry ); for (currAttr = e->attrs; currAttr != NULL; ) { tmpAttr = currAttr->next; VmDirFreeAttribute(currAttr); currAttr = tmpAttr; } } if (e->pComputedAttrs) { VDIR_ATTRIBUTE * currAttr = NULL; VDIR_ATTRIBUTE * tmpAttr = NULL; for (currAttr = e->pComputedAttrs; currAttr != NULL; ) { tmpAttr = currAttr->next; VmDirFreeAttribute(currAttr); currAttr = tmpAttr; } } VmDirSchemaCtxRelease(e->pSchemaCtx); VmDirAclCtxContentFree(e->pAclCtx); VMDIR_SAFE_FREE_MEMORY(e->pAclCtx); VMDIR_SAFE_FREE_MEMORY(e->pszGuid); memset(e, 0, sizeof(*e)); } VmDirLog( LDAP_DEBUG_TRACE, "DeleteEntry: End" ); }
/* * Convert entry allocType from FORMAT_PACK to FORMAT_NORMAL */ DWORD VmDirEntryUnpack( PVDIR_ENTRY pEntry ) { DWORD dwError = 0; PVDIR_ATTRIBUTE pAttr = NULL; PVDIR_ATTRIBUTE pDupAttr = NULL; VDIR_ENTRY newEntry = {0}; assert(pEntry); if (pEntry->allocType != ENTRY_STORAGE_FORMAT_PACK) { return 0; } newEntry.allocType = ENTRY_STORAGE_FORMAT_NORMAL; newEntry.eId = pEntry->eId; dwError = VmDirBervalContentDup(&pEntry->dn, &newEntry.dn); BAIL_ON_VMDIR_ERROR(dwError); // pdn.lberbv.bv_val is always in-place of dn.lberbv.bv_val (compatible with DeleteEntry) if (pEntry->pdn.lberbv.bv_val && pEntry->pdn.lberbv.bv_len > 0) { newEntry.pdn.lberbv.bv_val = newEntry.dn.lberbv.bv_val + (pEntry->pdn.lberbv.bv_val - pEntry->dn.lberbv.bv_val); } // pdn.bvnorm_val is ok to heap allocated if (pEntry->pdn.bvnorm_val && pEntry->pdn.bvnorm_len > 0) { dwError = VmDirAllocateStringA( pEntry->pdn.bvnorm_val, &newEntry.pdn.bvnorm_val); BAIL_ON_VMDIR_ERROR(dwError); newEntry.pdn.bvnorm_len = VmDirStringLenA(newEntry.pdn.bvnorm_val); } // copy attribute for (pAttr = pEntry->attrs, pDupAttr = NULL; pAttr; pAttr = pAttr->next) { dwError = VmDirAttributeDup(pAttr, &pDupAttr); BAIL_ON_VMDIR_ERROR(dwError); dwError = VmDirEntryAddAttribute(&newEntry, pDupAttr); BAIL_ON_VMDIR_ERROR(dwError); pDupAttr = NULL; } ///////////////////////////////////////////////////////////////////// // should never fail from here to the end ///////////////////////////////////////////////////////////////////// // takes over schema ctx (we copy attributes) newEntry.pSchemaCtx = pEntry->pSchemaCtx; assert(newEntry.pSchemaCtx); pEntry->pSchemaCtx = NULL; // TODO, other fields? // takes over pParentEntry newEntry.pParentEntry = pEntry->pParentEntry; pEntry->pParentEntry = NULL; // fee resources VmDirFreeEntryContent(pEntry); // takes over newEntry content VmDirCopyMemory(pEntry, sizeof(VDIR_ENTRY), &newEntry, sizeof(newEntry)); cleanup: return dwError; error: if (pDupAttr) { VmDirFreeAttribute(pDupAttr); } VmDirFreeEntryContent(&newEntry); goto cleanup; }
/* * Simple (but not efficient) function to convert list of attribute,value * into an ADD_REQUEST type entry. * (e.g. ppszAttrList = { "objectclass","person", * "objectclass", "masterdiver", * "cn","my common name", * NULL}; */ static DWORD AttrListToEntry( PVDIR_SCHEMA_CTX pSchemaCtx, PSTR pszDN, PSTR* ppszAttrList, PVDIR_ENTRY pEntry) { DWORD dwError = 0; VDIR_ATTRIBUTE* pAttr = NULL; PSTR* ppszNext = NULL; assert(pSchemaCtx && pszDN && ppszAttrList && pEntry); pEntry->allocType = ENTRY_STORAGE_FORMAT_NORMAL; // establish entry schema context association pEntry->pSchemaCtx = VmDirSchemaCtxClone(pSchemaCtx); assert(pEntry->pSchemaCtx); dwError = VmDirAllocateStringA( pszDN, &pEntry->dn.lberbv.bv_val); BAIL_ON_VMDIR_ERROR(dwError); pEntry->dn.bOwnBvVal = TRUE; pEntry->dn.lberbv.bv_len = VmDirStringLenA(pEntry->dn.lberbv.bv_val); dwError = VmDirNormalizeDN( &(pEntry->dn), pSchemaCtx ); BAIL_ON_VMDIR_ERROR(dwError); for (ppszNext = ppszAttrList; *ppszNext && *(ppszNext+1); ppszNext += 2) { assert (!pAttr); if ( (*(ppszNext+1))[0] != '\0') // skip attribute with empty value { dwError = VmDirAttributeAllocate( *ppszNext, 1, pSchemaCtx, &pAttr); BAIL_ON_VMDIR_ERROR(dwError); dwError = VmDirAllocateStringA( *(ppszNext+1), &pAttr->vals[0].lberbv.bv_val); BAIL_ON_VMDIR_ERROR(dwError); pAttr->vals[0].bOwnBvVal = TRUE; pAttr->vals[0].lberbv.bv_len = VmDirStringLenA(pAttr->vals[0].lberbv.bv_val); dwError = VmDirEntryAddAttribute( pEntry, pAttr); BAIL_ON_VMDIR_ERROR(dwError); pAttr = NULL; // pEntry takes over pAttr } } cleanup: return dwError; error: if (pAttr) { VmDirFreeAttribute(pAttr); } VmDirFreeEntryContent(pEntry); memset(pEntry, 0, sizeof(*pEntry)); 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; }
static int DeleteRefAttributesValue( VDIR_OPERATION * pOperation, VDIR_BERVALUE * dn ) { int retVal = LDAP_SUCCESS; VDIR_FILTER * f = NULL; VDIR_CANDIDATES * cl = NULL; VDIR_ENTRY groupEntry = {0}; VDIR_ENTRY * pGroupEntry = NULL; int i = 0; VDIR_MODIFICATION mod = {0}; ModifyReq mr; VDIR_BERVALUE delVals[2]; PSTR pszLocalErrorMsg = NULL; assert( pOperation != NULL && pOperation->pBEIF != NULL && dn != NULL); retVal = VmDirNormalizeDN( dn, pOperation->pSchemaCtx ); BAIL_ON_VMDIR_ERROR( retVal ); // Set filter retVal = VmDirAllocateMemory( sizeof( VDIR_FILTER ), (PVOID *)&f); BAIL_ON_VMDIR_ERROR( retVal ); f->choice = LDAP_FILTER_EQUALITY; f->filtComp.ava.type.lberbv.bv_val = ATTR_MEMBER; f->filtComp.ava.type.lberbv.bv_len = ATTR_MEMBER_LEN; f->filtComp.ava.value = *dn; if ((f->filtComp.ava.pATDesc = VmDirSchemaAttrNameToDesc( pOperation->pSchemaCtx, ATTR_MEMBER)) == NULL) { retVal = VMDIR_ERROR_NO_SUCH_ATTRIBUTE; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, (pszLocalErrorMsg), "undefined attribute (%s)", VDIR_SAFE_STRING(ATTR_MEMBER)); } // Set ModifyReq structure memset(&mr, 0, sizeof(ModifyReq)); mod.operation = MOD_OP_DELETE; mod.attr.type.lberbv.bv_val = ATTR_MEMBER; mod.attr.type.lberbv.bv_len = ATTR_MEMBER_LEN; mod.attr.pATDesc = f->filtComp.ava.pATDesc; mod.attr.next = NULL; delVals[0] = *dn; memset(&(delVals[1]), 0, sizeof(VDIR_BERVALUE)); mod.attr.vals = delVals; mod.attr.numVals = 1; mod.next = NULL; mr.mods = &mod; mr.numMods = 1; retVal = pOperation->pBEIF->pfnBEGetCandidates( pOperation->pBECtx, f); if ( retVal != 0 ) { if (retVal == VMDIR_ERROR_BACKEND_ENTRY_NOTFOUND) { retVal = LDAP_SUCCESS; // no member refer to this DN. return ok/0 } else { retVal = VMDIR_ERROR_GENERIC; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, (pszLocalErrorMsg), "DeleteRefAttributesValue: Building group list (BdbGetCandidates()) failed."); } } else { cl = f->candidates; for (i = 0; i < cl->size; i++) { pGroupEntry = &groupEntry; if ((retVal = VmDirModifyEntryCoreLogic( pOperation, &mr, cl->eIds[i], pGroupEntry)) != 0) { switch (retVal) { case VMDIR_ERROR_BACKEND_PARENT_NOTFOUND: case VMDIR_ERROR_BACKEND_ENTRY_NOTFOUND: case VMDIR_ERROR_ENTRY_NOT_FOUND: continue; default: // Including LDAP_LOCK_DEADLOCK, which is handled by the caller BAIL_ON_VMDIR_ERROR( retVal ); } } VmDirFreeBervalContent( &(mr.dn) ); // VmDirModifyEntryCoreLogic fill in DN if not exists VmDirFreeEntryContent( pGroupEntry ); pGroupEntry = NULL; // Reset to NULL so that DeleteEntry is no-op. } } cleanup: memset(&(f->filtComp.ava.value), 0, sizeof(VDIR_BERVALUE)); // Since ava.value is NOT owned by filter. DeleteFilter( f ); VmDirFreeEntryContent( pGroupEntry ); VMDIR_SAFE_FREE_MEMORY(pszLocalErrorMsg); return retVal; error: VMDIR_APPEND_ERROR_MSG(pOperation->ldapResult.pszErrMsg, pszLocalErrorMsg); 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; }
/* 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; }
static int ProcessCandidateList( VDIR_OPERATION * pOperation ) { int retVal = LDAP_SUCCESS; int i = 0; VDIR_CANDIDATES * cl = pOperation->request.searchReq.filter->candidates; VDIR_ENTRY srEntry = {0}; VDIR_ENTRY * pSrEntry = NULL; int numSentEntries = 0; BOOLEAN bInternalSearch = FALSE; BOOLEAN bPageResultsCtrl = FALSE; DWORD dwPageSize = 0; ENTRYID lastEID = 0; /* * If the page size is greater than or equal to the sizeLimit value, * the server should ignore the control as the request can be satisfied in a single page. */ if (pOperation->showPagedResultsCtrl && (pOperation->request.searchReq.sizeLimit == 0 || pOperation->showPagedResultsCtrl->value.pagedResultCtrlVal.pageSize < (DWORD)pOperation->request.searchReq.sizeLimit)) { VmDirLog( LDAP_DEBUG_TRACE, "showPagedResultsCtrl applies to this query." ); bPageResultsCtrl = TRUE; dwPageSize = pOperation->showPagedResultsCtrl->value.pagedResultCtrlVal.pageSize; lastEID = atoi(pOperation->showPagedResultsCtrl->value.pagedResultCtrlVal.cookie); pOperation->showPagedResultsCtrl->value.pagedResultCtrlVal.cookie[0] = '\0'; } if (cl && cl->size > 0) { if (pOperation->opType == VDIR_OPERATION_TYPE_INTERNAL) { //TODO, we should have a hard limit on the cl->size we handle bInternalSearch = TRUE; VmDirFreeEntryArrayContent(&pOperation->internalSearchEntryArray); retVal = VmDirAllocateMemory( sizeof(VDIR_ENTRY) * cl->size, (PVOID*)&pOperation->internalSearchEntryArray.pEntry); BAIL_ON_VMDIR_ERROR(retVal); } for (i = 0, numSentEntries = 0; (i < cl->size) && VmDirdState() != VMDIRD_STATE_SHUTDOWN && (pOperation->request.searchReq.sizeLimit == 0 /* unlimited */ || numSentEntries < pOperation->request.searchReq.sizeLimit); i++) { //skip entries we sent before if (bPageResultsCtrl && lastEID > 0) { if (cl->eIds[i] == lastEID) { lastEID = 0; } continue; } VMDIR_LOG_DEBUG( LDAP_DEBUG_FILTER, "ProcessCandidateList EID(%u)", cl->eIds[i]); pSrEntry = bInternalSearch ? (pOperation->internalSearchEntryArray.pEntry + pOperation->internalSearchEntryArray.iSize) : &srEntry; retVal = pOperation->pBEIF->pfnBEIdToEntry( pOperation->pBECtx, pOperation->pSchemaCtx, cl->eIds[i], pSrEntry, VDIR_BACKEND_ENTRY_LOCK_READ); if (retVal == 0) { if (CheckIfEntryPassesFilter( pOperation, pSrEntry, pOperation->request.searchReq.filter) == FILTER_RES_TRUE) { retVal = VmDirBuildComputedAttribute( pOperation, pSrEntry ); BAIL_ON_VMDIR_ERROR( retVal ); if (bInternalSearch) { pOperation->internalSearchEntryArray.iSize++; pSrEntry = NULL; // EntryArray takes over *pSrEntry content } else { retVal = VmDirSendSearchEntry( pOperation, pSrEntry ); if (retVal == VMDIR_ERROR_INSUFFICIENT_ACCESS) { VMDIR_LOG_WARNING( VMDIR_LOG_MASK_ALL, "Access deny on search entry result [%s,%d] (bindedDN-%s) (targetDn-%s)\n", __FILE__, __LINE__, pOperation->conn->AccessInfo.pszBindedDn, pSrEntry->dn.lberbv.bv_val); // make sure search continues retVal = 0; } BAIL_ON_VMDIR_ERROR( retVal ); if (pSrEntry->bSearchEntrySent) { numSentEntries++; } } } //We have sent one page size of entries, so we can break here if (bPageResultsCtrl && numSentEntries == dwPageSize){ retVal = VmDirStringPrintFA( pOperation->showPagedResultsCtrl->value.pagedResultCtrlVal.cookie, VMDIR_MAX_I64_ASCII_STR_LEN, "%u", pSrEntry->eId); BAIL_ON_VMDIR_ERROR( retVal ); break; } VmDirFreeEntryContent( pSrEntry ); pSrEntry = NULL; // Reset to NULL so that DeleteEntry is no-op. } else { // Ignore BdbEIdToEntry errors. VMDIR_LOG_WARNING( VMDIR_LOG_MASK_ALL, "ProcessCandiateList BEIdToEntry EID(%u), error (%u)", cl->eIds[i], retVal); retVal = 0; } } VMDIR_LOG_VERBOSE( LDAP_DEBUG_FILTER, "(%d) candiates processed and (%d) entries sent", cl->size, numSentEntries); } if ( pOperation->request.searchReq.sizeLimit && numSentEntries < pOperation->request.searchReq.sizeLimit && pOperation->pBECtx->iPartialCandidates) { retVal = LDAP_UNWILLING_TO_PERFORM; VMDIR_LOG_ERROR(VMDIR_LOG_MASK_ALL, "ProcessCandiateList may return none or paritial requested entries with sizelimit %d", pOperation->request.searchReq.sizeLimit); } cleanup: pOperation->dwSentEntries = numSentEntries; VmDirFreeEntryContent( pSrEntry ); return retVal; error: VMDIR_LOG_ERROR( VMDIR_LOG_MASK_ALL, "ProcessCandiateList failed. (%u)", retVal); goto cleanup; }