/* * Copyright (c) 2000-2001,2011-2012,2014 Apple Inc. All Rights Reserved. * * The contents of this file constitute Original Code as defined in and are * subject to the Apple Public Source License Version 1.2 (the 'License'). * You may not use this file except in compliance with the License. Please obtain * a copy of the License at http://www.apple.com/publicsource and read it before * using this file. * * This Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the * specific language governing rights and limitations under the License. */ // // MetaRecord.cpp // #include "MetaRecord.h" #include #include MetaRecord::MetaRecord(CSSM_DB_RECORDTYPE inRecordType) : mRecordType(inRecordType) { } MetaRecord::MetaRecord(const CSSM_DB_RECORD_ATTRIBUTE_INFO &inInfo) : mRecordType(inInfo.DataRecordType) { try { setRecordAttributeInfo(inInfo); } catch (...) { for_each_delete(mAttributeVector.begin(), mAttributeVector.end()); } } MetaRecord::MetaRecord(CSSM_DB_RECORDTYPE inRelationID, uint32 inNumberOfAttributes, const CSSM_DB_SCHEMA_ATTRIBUTE_INFO *inAttributeInfo) : mRecordType(inRelationID) { try { for (uint32 anIndex = 0; anIndex < inNumberOfAttributes; anIndex++) { string aName; if (inAttributeInfo[anIndex].AttributeName) aName = string(inAttributeInfo[anIndex].AttributeName); const CssmData *aNameID = NULL; if (inAttributeInfo[anIndex].AttributeNameID.Length > 0) aNameID = &CssmData::overlay(inAttributeInfo[anIndex].AttributeNameID); uint32 aNumber = inAttributeInfo[anIndex].AttributeId; createAttribute( inAttributeInfo[anIndex].AttributeName ? &aName : NULL, aNameID, aNumber, inAttributeInfo[anIndex].DataType); } } catch (...) { for_each_delete(mAttributeVector.begin(), mAttributeVector.end()); } } MetaRecord::~MetaRecord() { // for_each_delete(mAttributeVector.begin(), mAttributeVector.end()); AttributeVector::iterator it = mAttributeVector.begin(); while (it != mAttributeVector.end()) { MetaAttribute* mat = *it++; if (mat != NULL) { delete mat; } } } void MetaRecord::setRecordAttributeInfo(const CSSM_DB_RECORD_ATTRIBUTE_INFO &inInfo) { for (uint32 anIndex = 0; anIndex < inInfo.NumberOfAttributes; anIndex++) { switch (inInfo.AttributeInfo[anIndex].AttributeNameFormat) { case CSSM_DB_ATTRIBUTE_NAME_AS_STRING: { string aName(inInfo.AttributeInfo[anIndex].Label.AttributeName); createAttribute(&aName, nil, anIndex, inInfo.AttributeInfo[anIndex].AttributeFormat); break; } case CSSM_DB_ATTRIBUTE_NAME_AS_OID: { const CssmData &aNameID = CssmOid::overlay(inInfo.AttributeInfo[anIndex].Label.AttributeOID); createAttribute(nil, &aNameID, anIndex, inInfo.AttributeInfo[anIndex].AttributeFormat); break; } case CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER: { uint32 aNumber = inInfo.AttributeInfo[anIndex].Label.AttributeID; createAttribute(nil, nil, aNumber, inInfo.AttributeInfo[anIndex].AttributeFormat); break; } default: CssmError::throwMe(CSSMERR_DL_INVALID_FIELD_NAME); break; } } } void MetaRecord::createAttribute(const string *inAttributeName, const CssmOid *inAttributeOID, uint32 inAttributeID, CSSM_DB_ATTRIBUTE_FORMAT inAttributeFormat) { // Index of new element is current size of vector uint32 anAttributeIndex = (uint32)mAttributeVector.size(); bool aInsertedAttributeName = false; bool aInsertedAttributeOID = false; bool aInsertedAttributeID = false; if (inAttributeName) { if (!mNameStringMap.insert(NameStringMap::value_type(*inAttributeName, anAttributeIndex)).second) CssmError::throwMe(CSSMERR_DL_FIELD_SPECIFIED_MULTIPLE); aInsertedAttributeName = true; } try { if (inAttributeOID) { if (!mNameOIDMap.insert(NameOIDMap::value_type(*inAttributeOID, anAttributeIndex)).second) CssmError::throwMe(CSSMERR_DL_FIELD_SPECIFIED_MULTIPLE); aInsertedAttributeOID = true; } if (!mNameIntMap.insert(NameIntMap::value_type(inAttributeID, anAttributeIndex)).second) CssmError::throwMe(CSSMERR_DL_FIELD_SPECIFIED_MULTIPLE); aInsertedAttributeID = true; // Note: this no longer throws INVALID_FIELD_NAME since the attribute will always have // an attribute ID by which it is known mAttributeVector.push_back(MetaAttribute::create(inAttributeFormat, anAttributeIndex, inAttributeID)); } catch(...) { if (aInsertedAttributeName) mNameStringMap.erase(*inAttributeName); if (aInsertedAttributeOID) mNameOIDMap.erase(*inAttributeOID); if (inAttributeID) mNameIntMap.erase(inAttributeID); throw; } } // Create a packed record from the given inputs. void MetaRecord::packRecord(WriteSection &inWriteSection, const CSSM_DB_RECORD_ATTRIBUTE_DATA *inAttributes, const CssmData *inData) const { uint32 aDataSize; if (inData) aDataSize = (uint32)inData->Length; else aDataSize = 0; inWriteSection.put(OffsetDataSize, aDataSize); uint32 anOffset = (uint32)(OffsetAttributeOffsets + AtomSize * mAttributeVector.size()); if (aDataSize) anOffset = inWriteSection.put(anOffset, aDataSize, inData->Data); vector aNumValues(mAttributeVector.size(), ~(uint32)0); vector aValues(mAttributeVector.size()); uint32 anIndex; if (inAttributes == NULL) inWriteSection.put(OffsetSemanticInformation, 0); else { inWriteSection.put(OffsetSemanticInformation, inAttributes->SemanticInformation); // Put the supplied attribute values into the list of attributes // and values. anIndex = inAttributes->NumberOfAttributes; // Make sure that AttributeData is a valid array. if (anIndex > 0) Required(inAttributes->AttributeData); while (anIndex-- > 0) { CSSM_DB_ATTRIBUTE_DATA &anAttribute = inAttributes->AttributeData[anIndex]; uint32 anAttributeIndex = attributeIndex(anAttribute.Info); // Make sure that the caller specified the attribute values in the correct format. if (anAttribute.Info.AttributeFormat != mAttributeVector[anAttributeIndex]->attributeFormat()) CssmError::throwMe(CSSMERR_DL_INCOMPATIBLE_FIELD_FORMAT); // If this attribute was specified before, throw. if (aNumValues[anAttributeIndex] != ~(uint32)0) CssmError::throwMe(CSSMERR_DL_FIELD_SPECIFIED_MULTIPLE); aNumValues[anAttributeIndex] = anAttribute.NumberOfValues; aValues[anAttributeIndex] = anAttribute.Value; } } for (anIndex = 0; anIndex < mAttributeVector.size(); ++anIndex) { const MetaAttribute &aMetaAttribute = *mAttributeVector[anIndex]; uint32 aNumberOfValues = aNumValues[anIndex]; // Now call the parsingmodule for each attribute that // wasn't explicitly specified and that has a parsingmodule. if (aNumberOfValues == ~(uint32)0) aNumberOfValues = aDataSize == 0 ? 0 : aMetaAttribute.parse(*inData, aValues[anIndex]); // XXX When do we throw CSSMERR_DL_MISSING_VALUE? Maybe if an // attribute is part of a unique index. // Now we have a valuelist for this attribute. Let's encode it. aMetaAttribute.packAttribute(inWriteSection, anOffset, aNumberOfValues, aValues[anIndex]); } inWriteSection.put(OffsetRecordSize, anOffset); inWriteSection.size(anOffset); } inline void MetaRecord::unpackAttribute(const ReadSection &inReadSection, Allocator &inAllocator, CSSM_DB_ATTRIBUTE_DATA &inoutAttribute) const { const MetaAttribute &aMetaAttribute = metaAttribute(inoutAttribute.Info); // XXX: See ISSUES on whether AttributeFormat should be an outputvalue or not. inoutAttribute.Info.AttributeFormat = aMetaAttribute.attributeFormat(); aMetaAttribute.unpackAttribute(inReadSection, inAllocator, inoutAttribute.NumberOfValues, inoutAttribute.Value); } void MetaRecord::unpackRecord(const ReadSection &inReadSection, Allocator &inAllocator, CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes, CssmData *inoutData, CSSM_QUERY_FLAGS inQueryFlags) const { // XXX Use POD wrapper for inoutAttributes here. TrackingAllocator anAllocator(inAllocator); try { if (inoutData) { // XXX Treat KEY records specially. // If inQueryFlags & CSSM_QUERY_RETURN_DATA is true return the raw // key bits in the CSSM_KEY structure Range aDataRange = dataRange(inReadSection); inoutData->Length = aDataRange.mSize; inoutData->Data = inReadSection.allocCopyRange(aDataRange, anAllocator); } if (inoutAttributes) { inoutAttributes->DataRecordType = dataRecordType(); inoutAttributes->SemanticInformation = semanticInformation(inReadSection); uint32 anIndex = inoutAttributes->NumberOfAttributes; // Make sure that AttributeData is a valid array. if (anIndex > 0 && inoutAttributes->AttributeData == NULL) CssmError::throwMe(CSSM_ERRCODE_INVALID_POINTER); while (anIndex-- > 0) { unpackAttribute(inReadSection, anAllocator, inoutAttributes->AttributeData[anIndex]); } } } catch (CssmError e) { if (e.osStatus() != CSSMERR_DL_DATABASE_CORRUPT) { // clear all pointers so that nothing dangles back to the user if (inoutData) { inoutData->Data = NULL; } if (inoutAttributes) { unsigned i; for (i = 0; i < inoutAttributes->NumberOfAttributes; ++i) { CSSM_DB_ATTRIBUTE_DATA& data = inoutAttributes->AttributeData[i]; unsigned j; for (j = 0; j < data.NumberOfValues; ++j) { data.Value[j].Data = NULL; } data.Value = NULL; if (data.Info.AttributeNameFormat == CSSM_DB_ATTRIBUTE_NAME_AS_STRING) { data.Info.Label.AttributeName = NULL; } } } } throw; } catch (...) { // clear all pointers so that nothing dangles back to the user if (inoutData) { inoutData->Data = NULL; } if (inoutAttributes) { unsigned i; for (i = 0; i < inoutAttributes->NumberOfAttributes; ++i) { CSSM_DB_ATTRIBUTE_DATA& data = inoutAttributes->AttributeData[i]; unsigned j; for (j = 0; j < data.NumberOfValues; ++j) { data.Value[j].Data = NULL; } data.Value = NULL; if (data.Info.AttributeNameFormat == CSSM_DB_ATTRIBUTE_NAME_AS_STRING) { data.Info.Label.AttributeName = NULL; } } } throw; } // Don't free anything the trackingAllocator allocated when it is destructed. anAllocator.commit(); } // Return the index (0 though NumAttributes - 1) of the attribute // represented by inAttributeInfo #ifndef NDEBUG #define LOG_NAME_AS_STRING_FAIL #endif uint32 MetaRecord::attributeIndex(const CSSM_DB_ATTRIBUTE_INFO &inAttributeInfo) const { uint32 anIndex; switch (inAttributeInfo.AttributeNameFormat) { case CSSM_DB_ATTRIBUTE_NAME_AS_STRING: { string aName(inAttributeInfo.Label.AttributeName); assert(aName.size() < 500); // MDS leak debug NameStringMap::const_iterator it = mNameStringMap.find(aName); if (it == mNameStringMap.end()) { #ifdef LOG_NAME_AS_STRING_FAIL printf("NAME_AS_STRING failure; attrName %s\n", inAttributeInfo.Label.AttributeName); for(it = mNameStringMap.begin(); it != mNameStringMap.end(); it++) { printf("name %s val %d\n", it->first.c_str(), it->second); } #endif CssmError::throwMe(CSSMERR_DL_INVALID_FIELD_NAME); } anIndex = it->second; break; } case CSSM_DB_ATTRIBUTE_NAME_AS_OID: { const CssmOid &aName = CssmOid::overlay(inAttributeInfo.Label.AttributeOID); NameOIDMap::const_iterator it = mNameOIDMap.find(aName); if (it == mNameOIDMap.end()) CssmError::throwMe(CSSMERR_DL_INVALID_FIELD_NAME); anIndex = it->second; break; } case CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER: { uint32 aName = inAttributeInfo.Label.AttributeID; NameIntMap::const_iterator it = mNameIntMap.find(aName); if (it == mNameIntMap.end()) CssmError::throwMe(CSSMERR_DL_INVALID_FIELD_NAME); anIndex = it->second; break; } default: CssmError::throwMe(CSSMERR_DL_INVALID_FIELD_NAME); break; } return anIndex; } const MetaAttribute & MetaRecord::metaAttribute(const CSSM_DB_ATTRIBUTE_INFO &inAttributeInfo) const { return *mAttributeVector[attributeIndex(inAttributeInfo)]; } // Create a packed record from the given inputs and the old packed record inReadSection. void MetaRecord::updateRecord(const ReadSection &inReadSection, WriteSection &inWriteSection, const CssmDbRecordAttributeData *inAttributes, const CssmData *inData, CSSM_DB_MODIFY_MODE inModifyMode) const { TrackingAllocator anAllocator(Allocator::standard()); // modify the opaque data associated with the record uint32 aDataSize; const uint8 *aDataData = NULL; if (inData) { // prepare to write new data aDataSize = (uint32)inData->Length; aDataData = inData->Data; } else { // prepare to copy old data Range aDataRange = dataRange(inReadSection); aDataSize = aDataRange.mSize; if (aDataSize) aDataData = inReadSection.range(aDataRange); } // compute the data offset; this will keep a running total of the record size uint32 anOffset = (uint32)(OffsetAttributeOffsets + AtomSize * mAttributeVector.size()); // write the appropriate data to the new record inWriteSection.put(OffsetDataSize, aDataSize); if (aDataSize) anOffset = inWriteSection.put(anOffset, aDataSize, aDataData); // unpack the old attributes since some of them may need to be preserved auto_array attributeData(mAttributeVector.size()); for (size_t anAttributeIndex = mAttributeVector.size(); anAttributeIndex-- > 0; ) { // unpack the old attribute data for this attribute index const MetaAttribute &attribute = *mAttributeVector[anAttributeIndex]; attribute.unpackAttribute(inReadSection, anAllocator, attributeData[anAttributeIndex].NumberOfValues, attributeData[anAttributeIndex].Value); } // retrieve the currrent semantic information uint32 oldSemanticInformation = semanticInformation(inReadSection); // process each input attribute as necessary, based on the modification mode if (inAttributes == NULL) { // make sure the modification mode is NONE, otherwise it's an // error accordining to the spec if (inModifyMode != CSSM_DB_MODIFY_ATTRIBUTE_NONE) CssmError::throwMe(CSSMERR_DL_INVALID_MODIFY_MODE); } else { // modify the semantic information uint32 inSemanticInformation = inAttributes ? inAttributes->SemanticInformation : 0; if (inModifyMode == CSSM_DB_MODIFY_ATTRIBUTE_ADD) oldSemanticInformation |= inSemanticInformation; else if (inModifyMode == CSSM_DB_MODIFY_ATTRIBUTE_DELETE) oldSemanticInformation &= ~inSemanticInformation; else if (inModifyMode == CSSM_DB_MODIFY_ATTRIBUTE_REPLACE) oldSemanticInformation = inSemanticInformation; uint32 anIndex = inAttributes->NumberOfAttributes; if (anIndex > 0) Required(inAttributes->AttributeData); // modify the attributes while (anIndex-- > 0) { const CssmDbAttributeData &anAttribute = inAttributes->at(anIndex); uint32 anAttributeIndex = attributeIndex(anAttribute.info()); if (anAttribute.format() != mAttributeVector[anAttributeIndex]->attributeFormat()) CssmError::throwMe(CSSMERR_DL_INCOMPATIBLE_FIELD_FORMAT); CssmDbAttributeData &oldAttribute = attributeData[anAttributeIndex]; // if the modify mode is ADD, merge new values with pre-existing values if (inModifyMode == CSSM_DB_MODIFY_ATTRIBUTE_ADD) oldAttribute.add(anAttribute, anAllocator); // if the modify mode is DELETE, remove the indicated values, or remove // all values if none are specified else if (inModifyMode == CSSM_DB_MODIFY_ATTRIBUTE_DELETE) { if (anAttribute.size() == 0) oldAttribute.deleteValues(anAllocator); else oldAttribute.deleteValues(anAttribute, anAllocator); } // if the modify mode is REPLACE, then replace the specified values, or // delete all values if no values are specified else if (inModifyMode == CSSM_DB_MODIFY_ATTRIBUTE_REPLACE) { oldAttribute.deleteValues(anAllocator); if (anAttribute.size() > 0) oldAttribute.add(anAttribute, anAllocator); else // The spec says "all values are deleted or the the value is replaced // with the default" but doesn't say which. We could call the parsing // module for the attribute here...if they were implemented! But instead // we choose "all values are deleted" and leave it at that. ; } } } // write the resulting attributes into the new record inWriteSection.put(OffsetSemanticInformation, oldSemanticInformation); for (uint32 anIndex = 0; anIndex < mAttributeVector.size(); ++anIndex) { const MetaAttribute &metaAttribute = *mAttributeVector[anIndex]; metaAttribute.packAttribute(inWriteSection, anOffset, attributeData[anIndex].NumberOfValues, attributeData[anIndex].Value); } inWriteSection.put(OffsetRecordSize, anOffset); inWriteSection.size(anOffset); }