2 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please obtain
7 * a copy of the License at http://www.apple.com/publicsource and read it before
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
15 * specific language governing rights and limitations under the License.
23 #include "MetaRecord.h"
24 #include <Security/trackingallocator.h>
26 MetaRecord::MetaRecord(CSSM_DB_RECORDTYPE inRecordType
) :
27 mRecordType(inRecordType
)
31 MetaRecord::MetaRecord(const CSSM_DB_RECORD_ATTRIBUTE_INFO
&inInfo
)
32 : mRecordType(inInfo
.DataRecordType
)
36 setRecordAttributeInfo(inInfo
);
40 for_each_delete(mAttributeVector
.begin(), mAttributeVector
.end());
44 MetaRecord::MetaRecord(CSSM_DB_RECORDTYPE inRelationID
,
45 uint32 inNumberOfAttributes
,
46 const CSSM_DB_SCHEMA_ATTRIBUTE_INFO
*inAttributeInfo
) :
47 mRecordType(inRelationID
)
49 // XXX Is there any particular reason not to allow this?
51 if (inNumberOfAttributes
== 0 || inAttributeInfo
== NULL
)
52 CssmError::throwMe(CSSMERR_DL_UNSUPPORTED_NUM_ATTRIBUTES
);
56 for (uint32 anIndex
= 0; anIndex
< inNumberOfAttributes
; anIndex
++)
59 if (inAttributeInfo
[anIndex
].AttributeName
)
60 aName
= string(inAttributeInfo
[anIndex
].AttributeName
);
62 const CssmData
*aNameID
= NULL
;
63 if (inAttributeInfo
[anIndex
].AttributeNameID
.Length
> 0)
64 aNameID
= &CssmData::overlay(inAttributeInfo
[anIndex
].AttributeNameID
);
66 uint32 aNumber
= inAttributeInfo
[anIndex
].AttributeId
;
68 inAttributeInfo
[anIndex
].AttributeName
? &aName
: NULL
,
70 inAttributeInfo
[anIndex
].DataType
);
75 for_each_delete(mAttributeVector
.begin(), mAttributeVector
.end());
79 MetaRecord::~MetaRecord()
81 for_each_delete(mAttributeVector
.begin(), mAttributeVector
.end());
85 MetaRecord::setRecordAttributeInfo(const CSSM_DB_RECORD_ATTRIBUTE_INFO
&inInfo
)
87 // XXX Is there any particular reason not to allow this?
89 if (inInfo
.NumberOfAttributes
== 0 || inInfo
.AttributeInfo
== NULL
)
90 CssmError::throwMe(CSSMERR_DL_UNSUPPORTED_NUM_ATTRIBUTES
);
93 for (uint32 anIndex
= 0; anIndex
< inInfo
.NumberOfAttributes
; anIndex
++)
95 switch (inInfo
.AttributeInfo
[anIndex
].AttributeNameFormat
)
97 case CSSM_DB_ATTRIBUTE_NAME_AS_STRING
:
99 string
aName(inInfo
.AttributeInfo
[anIndex
].Label
.AttributeName
);
100 createAttribute(&aName
, nil
, anIndex
,
101 inInfo
.AttributeInfo
[anIndex
].AttributeFormat
);
104 case CSSM_DB_ATTRIBUTE_NAME_AS_OID
:
106 const CssmData
&aNameID
= CssmOid::overlay(inInfo
.AttributeInfo
[anIndex
].Label
.AttributeOID
);
107 createAttribute(nil
, &aNameID
, anIndex
,
108 inInfo
.AttributeInfo
[anIndex
].AttributeFormat
);
111 case CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER
:
113 uint32 aNumber
= inInfo
.AttributeInfo
[anIndex
].Label
.AttributeID
;
114 createAttribute(nil
, nil
, aNumber
,
115 inInfo
.AttributeInfo
[anIndex
].AttributeFormat
);
119 CssmError::throwMe(CSSMERR_DL_INVALID_FIELD_NAME
);
126 MetaRecord::createAttribute(const string
*inAttributeName
,
127 const CssmOid
*inAttributeOID
,
128 uint32 inAttributeID
,
129 CSSM_DB_ATTRIBUTE_FORMAT inAttributeFormat
)
131 // Index of new element is current size of vector
132 uint32 anAttributeIndex
= mAttributeVector
.size();
133 bool aInsertedAttributeName
= false;
134 bool aInsertedAttributeOID
= false;
135 bool aInsertedAttributeID
= false;
139 if (!mNameStringMap
.insert(NameStringMap::value_type(*inAttributeName
, anAttributeIndex
)).second
)
140 CssmError::throwMe(CSSMERR_DL_FIELD_SPECIFIED_MULTIPLE
);
141 aInsertedAttributeName
= true;
147 if (!mNameOIDMap
.insert(NameOIDMap::value_type(*inAttributeOID
, anAttributeIndex
)).second
)
148 CssmError::throwMe(CSSMERR_DL_FIELD_SPECIFIED_MULTIPLE
);
149 aInsertedAttributeOID
= true;
152 if (!mNameIntMap
.insert(NameIntMap::value_type(inAttributeID
, anAttributeIndex
)).second
)
153 CssmError::throwMe(CSSMERR_DL_FIELD_SPECIFIED_MULTIPLE
);
154 aInsertedAttributeID
= true;
156 // Note: this no longer throws INVALID_FIELD_NAME since the attribute will always have
157 // an attribute ID by which it is known
159 mAttributeVector
.push_back(MetaAttribute::create(inAttributeFormat
,
160 anAttributeIndex
, inAttributeID
));
164 if (aInsertedAttributeName
)
165 mNameStringMap
.erase(*inAttributeName
);
166 if (aInsertedAttributeOID
)
167 mNameOIDMap
.erase(*inAttributeOID
);
169 mNameIntMap
.erase(inAttributeID
);
176 // Create a packed record from the given inputs.
178 MetaRecord::packRecord(WriteSection
&inWriteSection
,
179 const CSSM_DB_RECORD_ATTRIBUTE_DATA
*inAttributes
,
180 const CssmData
*inData
) const
184 aDataSize
= inData
->Length
;
188 inWriteSection
.put(OffsetDataSize
, aDataSize
);
189 uint32 anOffset
= OffsetAttributeOffsets
+ AtomSize
* mAttributeVector
.size();
191 anOffset
= inWriteSection
.put(anOffset
, aDataSize
, inData
->Data
);
193 vector
<uint32
> aNumValues(mAttributeVector
.size(), ~0UL);
194 vector
<CSSM_DATA_PTR
> aValues(mAttributeVector
.size());
197 if (inAttributes
== NULL
)
198 inWriteSection
.put(OffsetSemanticInformation
, 0);
201 inWriteSection
.put(OffsetSemanticInformation
, inAttributes
->SemanticInformation
);
203 // Put the supplied attribute values into the list of attributes
205 anIndex
= inAttributes
->NumberOfAttributes
;
206 // Make sure that AttributeData is a valid array.
208 Required(inAttributes
->AttributeData
);
210 while (anIndex
-- > 0)
212 CSSM_DB_ATTRIBUTE_DATA
&anAttribute
= inAttributes
->AttributeData
[anIndex
];
213 uint32 anAttributeIndex
= attributeIndex(anAttribute
.Info
);
214 // Make sure that the caller specified the attribute values in the correct format.
215 if (anAttribute
.Info
.AttributeFormat
!= mAttributeVector
[anAttributeIndex
]->attributeFormat())
216 CssmError::throwMe(CSSMERR_DL_INCOMPATIBLE_FIELD_FORMAT
);
218 // If this attribute was specified before, throw.
219 if (aNumValues
[anAttributeIndex
] != ~0UL)
220 CssmError::throwMe(CSSMERR_DL_FIELD_SPECIFIED_MULTIPLE
);
222 aNumValues
[anAttributeIndex
] = anAttribute
.NumberOfValues
;
223 aValues
[anAttributeIndex
] = anAttribute
.Value
;
227 for (anIndex
= 0; anIndex
< mAttributeVector
.size(); ++anIndex
)
229 const MetaAttribute
&aMetaAttribute
= *mAttributeVector
[anIndex
];
230 uint32 aNumberOfValues
= aNumValues
[anIndex
];
231 // Now call the parsingmodule for each attribute that
232 // wasn't explicitly specified and that has a parsingmodule.
233 if (aNumberOfValues
== ~0UL)
234 aNumberOfValues
= aDataSize
== 0 ? 0 : aMetaAttribute
.parse(*inData
, aValues
[anIndex
]);
236 // XXX When do we throw CSSMERR_DL_MISSING_VALUE? Maybe if an
237 // attribute is part of a unique index.
239 // Now we have a valuelist for this attribute. Let's encode it.
240 aMetaAttribute
.packAttribute(inWriteSection
, anOffset
, aNumberOfValues
, aValues
[anIndex
]);
243 inWriteSection
.put(OffsetRecordSize
, anOffset
);
244 inWriteSection
.size(anOffset
);
248 MetaRecord::unpackAttribute(const ReadSection
&inReadSection
,
249 CssmAllocator
&inAllocator
,
250 CSSM_DB_ATTRIBUTE_DATA
&inoutAttribute
) const
252 const MetaAttribute
&aMetaAttribute
= metaAttribute(inoutAttribute
.Info
);
253 // XXX: See ISSUES on whether AttributeFormat should be an outputvalue or not.
254 inoutAttribute
.Info
.AttributeFormat
= aMetaAttribute
.attributeFormat();
255 aMetaAttribute
.unpackAttribute(inReadSection
, inAllocator
,
256 inoutAttribute
.NumberOfValues
,
257 inoutAttribute
.Value
);
261 MetaRecord::unpackRecord(const ReadSection
&inReadSection
,
262 CssmAllocator
&inAllocator
,
263 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes
,
265 CSSM_QUERY_FLAGS inQueryFlags
) const
267 // XXX Use POD wrapper for inoutAttributes here.
268 TrackingAllocator
anAllocator(inAllocator
);
271 // XXX Treat KEY records specially.
273 // If inQueryFlags & CSSM_QUERY_RETURN_DATA is true return the raw
274 // key bits in the CSSM_KEY structure
275 Range aDataRange
= dataRange(inReadSection
);
276 inoutData
->Length
= aDataRange
.mSize
;
277 inoutData
->Data
= inReadSection
.allocCopyRange(aDataRange
, anAllocator
);
282 inoutAttributes
->DataRecordType
= dataRecordType();
283 inoutAttributes
->SemanticInformation
= semanticInformation(inReadSection
);
284 uint32 anIndex
= inoutAttributes
->NumberOfAttributes
;
286 // Make sure that AttributeData is a valid array.
287 if (anIndex
> 0 && inoutAttributes
->AttributeData
== NULL
)
288 CssmError::throwMe(CSSM_ERRCODE_INVALID_POINTER
);
290 while (anIndex
-- > 0)
292 unpackAttribute(inReadSection
, anAllocator
,
293 inoutAttributes
->AttributeData
[anIndex
]);
297 // Don't free anything the trackingAllocator allocated when it is destructed.
298 anAllocator
.commit();
301 // Return the index (0 though NumAttributes - 1) of the attribute
302 // represented by inAttributeInfo
305 #define LOG_NAME_AS_STRING_FAIL
308 MetaRecord::attributeIndex(const CSSM_DB_ATTRIBUTE_INFO
&inAttributeInfo
) const
311 switch (inAttributeInfo
.AttributeNameFormat
)
313 case CSSM_DB_ATTRIBUTE_NAME_AS_STRING
:
315 string
aName(inAttributeInfo
.Label
.AttributeName
);
316 assert(aName
.size() < 500); // MDS leak debug
317 NameStringMap::const_iterator it
= mNameStringMap
.find(aName
);
318 if (it
== mNameStringMap
.end()) {
319 #ifdef LOG_NAME_AS_STRING_FAIL
320 printf("NAME_AS_STRING failure; attrName %s\n",
321 inAttributeInfo
.Label
.AttributeName
);
322 for(it
= mNameStringMap
.begin();
323 it
!= mNameStringMap
.end();
325 printf("name %s val %ul\n", it
->first
.c_str(), it
->second
);
328 CssmError::throwMe(CSSMERR_DL_INVALID_FIELD_NAME
);
330 anIndex
= it
->second
;
333 case CSSM_DB_ATTRIBUTE_NAME_AS_OID
:
335 const CssmOid
&aName
= CssmOid::overlay(inAttributeInfo
.Label
.AttributeOID
);
336 NameOIDMap::const_iterator it
= mNameOIDMap
.find(aName
);
337 if (it
== mNameOIDMap
.end())
338 CssmError::throwMe(CSSMERR_DL_INVALID_FIELD_NAME
);
339 anIndex
= it
->second
;
342 case CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER
:
344 uint32 aName
= inAttributeInfo
.Label
.AttributeID
;
345 NameIntMap::const_iterator it
= mNameIntMap
.find(aName
);
346 if (it
== mNameIntMap
.end())
347 CssmError::throwMe(CSSMERR_DL_INVALID_FIELD_NAME
);
348 anIndex
= it
->second
;
352 CssmError::throwMe(CSSMERR_DL_INVALID_FIELD_NAME
);
359 const MetaAttribute
&
360 MetaRecord::metaAttribute(const CSSM_DB_ATTRIBUTE_INFO
&inAttributeInfo
) const
362 return *mAttributeVector
[attributeIndex(inAttributeInfo
)];
365 // Create a packed record from the given inputs and the old packed record inReadSection.
367 MetaRecord::updateRecord(const ReadSection
&inReadSection
,
368 WriteSection
&inWriteSection
,
369 const CssmDbRecordAttributeData
*inAttributes
,
370 const CssmData
*inData
,
371 CSSM_DB_MODIFY_MODE inModifyMode
) const
373 TrackingAllocator
anAllocator(CssmAllocator::standard());
375 // modify the opaque data associated with the record
378 const uint8
*aDataData
= NULL
;
382 // prepare to write new data
383 aDataSize
= inData
->Length
;
384 aDataData
= inData
->Data
;
388 // prepare to copy old data
389 Range aDataRange
= dataRange(inReadSection
);
390 aDataSize
= aDataRange
.mSize
;
392 aDataData
= inReadSection
.range(aDataRange
);
395 // compute the data offset; this will keep a running total of the record size
396 uint32 anOffset
= OffsetAttributeOffsets
+ AtomSize
* mAttributeVector
.size();
398 // write the appropriate data to the new record
399 inWriteSection
.put(OffsetDataSize
, aDataSize
);
401 anOffset
= inWriteSection
.put(anOffset
, aDataSize
, aDataData
);
403 // unpack the old attributes since some of them may need to be preserved
405 auto_array
<CssmDbAttributeData
> attributeData(mAttributeVector
.size());
407 for (uint32 anAttributeIndex
= mAttributeVector
.size(); anAttributeIndex
-- > 0; )
409 // unpack the old attribute data for this attribute index
410 const MetaAttribute
&attribute
= *mAttributeVector
[anAttributeIndex
];
411 attribute
.unpackAttribute(inReadSection
, anAllocator
,
412 attributeData
[anAttributeIndex
].NumberOfValues
,
413 attributeData
[anAttributeIndex
].Value
);
416 // retrieve the currrent semantic information
418 uint32 oldSemanticInformation
= semanticInformation(inReadSection
);
420 // process each input attribute as necessary, based on the modification mode
422 if (inAttributes
== NULL
)
424 // make sure the modification mode is NONE, otherwise it's an
425 // error accordining to the spec
426 if (inModifyMode
!= CSSM_DB_MODIFY_ATTRIBUTE_NONE
)
427 CssmError::throwMe(CSSMERR_DL_INVALID_MODIFY_MODE
);
432 // modify the semantic information
434 uint32 inSemanticInformation
= inAttributes
? inAttributes
->SemanticInformation
: 0;
436 if (inModifyMode
== CSSM_DB_MODIFY_ATTRIBUTE_ADD
)
437 oldSemanticInformation
|= inSemanticInformation
;
439 else if (inModifyMode
== CSSM_DB_MODIFY_ATTRIBUTE_DELETE
)
440 oldSemanticInformation
&= ~inSemanticInformation
;
442 else if (inModifyMode
== CSSM_DB_MODIFY_ATTRIBUTE_REPLACE
)
443 oldSemanticInformation
= inSemanticInformation
;
445 uint32 anIndex
= inAttributes
->NumberOfAttributes
;
447 Required(inAttributes
->AttributeData
);
449 // modify the attributes
451 while (anIndex
-- > 0) {
453 const CssmDbAttributeData
&anAttribute
= inAttributes
->at(anIndex
);
454 uint32 anAttributeIndex
= attributeIndex(anAttribute
.info());
455 if (anAttribute
.format() != mAttributeVector
[anAttributeIndex
]->attributeFormat())
456 CssmError::throwMe(CSSMERR_DL_INCOMPATIBLE_FIELD_FORMAT
);
458 CssmDbAttributeData
&oldAttribute
= attributeData
[anAttributeIndex
];
460 // if the modify mode is ADD, merge new values with pre-existing values
462 if (inModifyMode
== CSSM_DB_MODIFY_ATTRIBUTE_ADD
)
463 oldAttribute
.add(anAttribute
, anAllocator
);
465 // if the modify mode is DELETE, remove the indicated values, or remove
466 // all values if none are specified
468 else if (inModifyMode
== CSSM_DB_MODIFY_ATTRIBUTE_DELETE
)
470 if (anAttribute
.size() == 0)
471 oldAttribute
.deleteValues(anAllocator
);
473 oldAttribute
.deleteValues(anAttribute
, anAllocator
);
476 // if the modify mode is REPLACE, then replace the specified values, or
477 // delete all values if no values are specified
479 else if (inModifyMode
== CSSM_DB_MODIFY_ATTRIBUTE_REPLACE
)
481 oldAttribute
.deleteValues(anAllocator
);
482 if (anAttribute
.size() > 0)
483 oldAttribute
.add(anAttribute
, anAllocator
);
485 // The spec says "all values are deleted or the the value is replaced
486 // with the default" but doesn't say which. We could call the parsing
487 // module for the attribute here...if they were implemented! But instead
488 // we choose "all values are deleted" and leave it at that.
494 // write the resulting attributes into the new record
496 inWriteSection
.put(OffsetSemanticInformation
, oldSemanticInformation
);
498 for (uint32 anIndex
= 0; anIndex
< mAttributeVector
.size(); ++anIndex
)
500 const MetaAttribute
&metaAttribute
= *mAttributeVector
[anIndex
];
501 metaAttribute
.packAttribute(inWriteSection
, anOffset
,
502 attributeData
[anIndex
].NumberOfValues
,
503 attributeData
[anIndex
].Value
);
506 inWriteSection
.put(OffsetRecordSize
, anOffset
);
507 inWriteSection
.size(anOffset
);