]> git.saurik.com Git - apple/security.git/blob - cdsa/cdsa_utilities/AppleDatabase.cpp
Security-54.1.3.tar.gz
[apple/security.git] / cdsa / cdsa_utilities / AppleDatabase.cpp
1 /*
2 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
3 *
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
8 * using this file.
9 *
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.
16 */
17
18
19 //
20 // AppleDatabase.cpp - Description t.b.d.
21 //
22 #include "AppleDatabase.h"
23 #include <Security/DatabaseSession.h>
24 #include <Security/DbContext.h>
25 #include <Security/cssmdb.h>
26 #include <Security/cssmapple.h>
27 #include <Security/trackingallocator.h>
28 #include <fcntl.h>
29 #include <memory>
30
31 //
32 // Table
33 //
34 Table::Table(const ReadSection &inTableSection) :
35 mMetaRecord(inTableSection[OffsetId]),
36 mTableSection(inTableSection),
37 mRecordsCount(inTableSection[OffsetRecordsCount]),
38 mFreeListHead(inTableSection[OffsetFreeListHead]),
39 mRecordNumbersCount(inTableSection[OffsetRecordNumbersCount])
40 {
41 // can't easily initialize indexes here, since meta record is incomplete
42 // until much later... see DbVersion::open()
43 }
44
45 Table::~Table()
46 {
47 for_each_map_delete(mIndexMap.begin(), mIndexMap.end());
48 }
49
50 void
51 Table::readIndexSection()
52 {
53 uint32 indexSectionOffset = mTableSection.at(OffsetIndexesOffset);
54
55 uint32 numIndexes = mTableSection.at(indexSectionOffset + AtomSize);
56
57 for (uint32 i = 0; i < numIndexes; i++) {
58 uint32 indexOffset = mTableSection.at(indexSectionOffset + (i + 2) * AtomSize);
59 ReadSection indexSection(mTableSection.subsection(indexOffset));
60
61 auto_ptr<DbConstIndex> index(new DbConstIndex(*this, indexSection));
62 mIndexMap.insert(ConstIndexMap::value_type(index->indexId(), index.get()));
63 index.release();
64 }
65 }
66
67 Cursor *
68 Table::createCursor(const CSSM_QUERY *inQuery, const DbVersion &inDbVersion) const
69 {
70 // if an index matches the query, return a cursor which uses the index
71
72 ConstIndexMap::const_iterator it;
73 DbQueryKey *queryKey;
74
75 for (it = mIndexMap.begin(); it != mIndexMap.end(); it++)
76 if (it->second->matchesQuery(*inQuery, queryKey)) {
77 IndexCursor *cursor = new IndexCursor(queryKey, inDbVersion, *this, it->second);
78 return cursor;
79 }
80
81 // otherwise, return a cursor that iterates over all table records
82
83 return new LinearCursor(inQuery, inDbVersion, *this);
84 }
85
86 const ReadSection
87 Table::getRecordSection(uint32 inRecordNumber) const
88 {
89 if (inRecordNumber >= mRecordNumbersCount)
90 CssmError::throwMe(CSSMERR_DL_INVALID_RECORD_UID);
91
92 uint32 aRecordOffset = mTableSection[OffsetRecordNumbers + AtomSize
93 * inRecordNumber];
94
95 // Check if this RecordNumber has been deleted.
96 if (aRecordOffset & 1 || aRecordOffset == 0)
97 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND);
98
99 return MetaRecord::readSection(mTableSection, aRecordOffset);
100 }
101
102 const RecordId
103 Table::getRecord(const RecordId &inRecordId,
104 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes,
105 CssmData *inoutData,
106 CssmAllocator &inAllocator) const
107 {
108 const ReadSection aRecordSection = getRecordSection(inRecordId.mRecordNumber);
109 const RecordId aRecordId = MetaRecord::unpackRecordId(aRecordSection);
110
111 // Make sure the RecordNumber matches that in the RecordId we just retrived.
112 if (aRecordId.mRecordNumber != inRecordId.mRecordNumber)
113 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
114
115 if (aRecordId.mCreateVersion != inRecordId.mCreateVersion)
116 CssmError::throwMe(CSSMERR_DL_INVALID_RECORD_UID);
117
118 // XXX Figure out which value to pass for inQueryFlags (5th) argument
119 mMetaRecord.unpackRecord(aRecordSection, inAllocator, inoutAttributes,
120 inoutData, 0);
121 return aRecordId;
122 }
123
124 uint32
125 Table::popFreeList(uint32 &aFreeListHead) const
126 {
127 assert(aFreeListHead | 1);
128 uint32 anOffset = aFreeListHead ^ 1;
129 uint32 aRecordNumber = (anOffset - OffsetRecordNumbers) / AtomSize;
130 aFreeListHead = mTableSection[anOffset];
131 return aRecordNumber;
132 }
133
134 const ReadSection
135 Table::getRecordsSection() const
136 {
137 return mTableSection.subsection(mTableSection[OffsetRecords]);
138 }
139
140 bool
141 Table::matchesTableId(Id inTableId) const
142 {
143 Id anId = mMetaRecord.dataRecordType();
144 if (inTableId == CSSM_DL_DB_RECORD_ANY) // All non schema tables.
145 return !(CSSM_DB_RECORDTYPE_SCHEMA_START <= anId
146 && anId < CSSM_DB_RECORDTYPE_SCHEMA_END);
147
148 if (inTableId == CSSM_DL_DB_RECORD_ALL_KEYS) // All key tables.
149 return (anId == CSSM_DL_DB_RECORD_PUBLIC_KEY
150 || anId == CSSM_DL_DB_RECORD_PRIVATE_KEY
151 || anId == CSSM_DL_DB_RECORD_SYMMETRIC_KEY);
152
153 return inTableId == anId; // Only if exact match.
154 }
155
156
157 //
158 // ModifiedTable
159 //
160 ModifiedTable::ModifiedTable(const Table *inTable) :
161 mTable(inTable),
162 mNewMetaRecord(nil),
163 mRecordNumberCount(inTable->recordNumberCount()),
164 mFreeListHead(inTable->freeListHead()),
165 mIsModified(false)
166 {
167 }
168
169 ModifiedTable::ModifiedTable(MetaRecord *inMetaRecord) :
170 mTable(nil),
171 mNewMetaRecord(inMetaRecord),
172 mRecordNumberCount(0),
173 mFreeListHead(0),
174 mIsModified(true)
175 {
176 }
177
178 ModifiedTable::~ModifiedTable()
179 {
180 for_each_map_delete(mIndexMap.begin(), mIndexMap.end());
181 for_each_map_delete(mInsertedMap.begin(), mInsertedMap.end());
182
183 delete mNewMetaRecord;
184 }
185
186 void
187 ModifiedTable::deleteRecord(const RecordId &inRecordId)
188 {
189 modifyTable();
190
191 uint32 aRecordNumber = inRecordId.mRecordNumber;
192
193 // remove the record from all the indexes
194 MutableIndexMap::iterator it;
195 for (it = mIndexMap.begin(); it != mIndexMap.end(); it++)
196 it->second->removeRecord(aRecordNumber);
197
198 InsertedMap::iterator anIt = mInsertedMap.find(inRecordId.mRecordNumber);
199 if (anIt == mInsertedMap.end())
200 {
201 // If we have no old table than this record can not exist yet.
202 if (!mTable)
203 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND);
204
205 #if RECORDVERSIONCHECK
206 const RecordId aRecordId = MetaRecord::unpackRecordId(mTable->getRecordSection(aRecordNumber));
207 if (aRecordId.mRecordVersion != inRecordId.mRecordVersion)
208 CssmError::throwMe(CSSMERR_DL_RECORD_MODIFIED);
209 #endif
210
211 // Schedule the record for deletion
212 if (!mDeletedSet.insert(aRecordNumber).second)
213 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND); // It was already deleted
214 }
215 else
216 {
217 const RecordId aRecordId = MetaRecord::unpackRecordId(*anIt->second);
218 if (aRecordId.mCreateVersion != inRecordId.mCreateVersion)
219 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND);
220
221 #if RECORDVERSIONCHECK
222 if (aRecordId.mRecordVersion != inRecordId.mRecordVersion)
223 CssmError::throwMe(CSSMERR_DL_RECORD_MODIFIED);
224 #endif
225
226 // Remove the inserted (but uncommited) record. It should already be in mDeletedSet
227 // if it existed previously in mTable.
228 mInsertedMap.erase(anIt);
229 delete anIt->second;
230 }
231 }
232
233 const RecordId
234 ModifiedTable::insertRecord(AtomicFile::VersionId inVersionId,
235 const CSSM_DB_RECORD_ATTRIBUTE_DATA *inAttributes,
236 const CssmData *inData)
237 {
238 modifyTable();
239
240 auto_ptr<WriteSection> aWriteSection(new WriteSection());
241 getMetaRecord().packRecord(*aWriteSection, inAttributes, inData);
242 uint32 aRecordNumber = nextRecordNumber();
243
244 // add the record to all the indexes; this will throw if the new record
245 // violates a unique index
246 MutableIndexMap::iterator it;
247 for (it = mIndexMap.begin(); it != mIndexMap.end(); it++)
248 it->second->insertRecord(aRecordNumber, *(aWriteSection.get()));
249
250 // schedule the record for insertion
251 RecordId aRecordId(aRecordNumber, inVersionId);
252 MetaRecord::packRecordId(aRecordId, *aWriteSection);
253 mInsertedMap.insert(InsertedMap::value_type(aRecordNumber, aWriteSection.get()));
254
255 aWriteSection.release();
256
257 return aRecordId;
258 }
259
260 const RecordId
261 ModifiedTable::updateRecord(const RecordId &inRecordId,
262 const CSSM_DB_RECORD_ATTRIBUTE_DATA *inAttributes,
263 const CssmData *inData,
264 CSSM_DB_MODIFY_MODE inModifyMode)
265 {
266 modifyTable();
267
268 uint32 aRecordNumber = inRecordId.mRecordNumber;
269 InsertedMap::iterator anIt = mInsertedMap.find(inRecordId.mRecordNumber);
270
271 // aReUpdate is true iff we are updating an already updated record.
272 bool aReUpdate = anIt != mInsertedMap.end();
273
274 // If we are not re-updating and there is no old table than this record does not exist yet.
275 if (!aReUpdate && !mTable)
276 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND);
277
278 const ReadSection &anOldDbRecord = aReUpdate ? *anIt->second : mTable->getRecordSection(aRecordNumber);
279 const RecordId aRecordId = MetaRecord::unpackRecordId(anOldDbRecord);
280
281 // Did someone else delete the record we are trying to update.
282 if (aRecordId.mCreateVersion != inRecordId.mCreateVersion)
283 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND);
284
285 #if RECORDVERSIONCHECK
286 // Is the record we that our update is based on current?
287 if (aRecordId.mRecordVersion != inRecordId.mRecordVersion)
288 CssmError::throwMe(CSSMERR_DL_STALE_UNIQUE_RECORD);
289 #endif
290
291 // Update the actual packed record.
292 auto_ptr<WriteSection> aDbRecord(new WriteSection());
293 getMetaRecord().updateRecord(anOldDbRecord, *aDbRecord,
294 CssmDbRecordAttributeData::overlay(inAttributes), inData, inModifyMode);
295
296
297 // Bump the RecordVersion of this record.
298 RecordId aNewRecordId(aRecordNumber, inRecordId.mCreateVersion, inRecordId.mRecordVersion + 1);
299 // Store the RecordVersion in the packed aDbRecord.
300 MetaRecord::packRecordId(aNewRecordId, *aDbRecord);
301
302 if (!aReUpdate && !mDeletedSet.insert(aRecordNumber).second)
303 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND); // Record was already in mDeletedSet
304
305 try
306 {
307 // remove the original record from all the indexes
308 MutableIndexMap::iterator it;
309 for (it = mIndexMap.begin(); it != mIndexMap.end(); it++)
310 it->second->removeRecord(aRecordNumber);
311
312 // add the updated record to all the indexes; this will throw if the new record
313 // violates a unique index
314 for (it = mIndexMap.begin(); it != mIndexMap.end(); it++)
315 it->second->insertRecord(aRecordNumber, *(aDbRecord.get()));
316
317 mInsertedMap.insert(InsertedMap::value_type(aRecordNumber, aDbRecord.get()));
318 aDbRecord.release();
319 }
320 catch(...)
321 {
322 if (!aReUpdate)
323 mDeletedSet.erase(aRecordNumber);
324 throw;
325 }
326
327 return aNewRecordId;
328 }
329
330 uint32
331 ModifiedTable::nextRecordNumber()
332 {
333 // If we still have unused free records in mTable get the next one.
334 if (mFreeListHead)
335 return mTable->popFreeList(mFreeListHead);
336
337 // Bump up the mRecordNumberCount so we don't reuse the same one.
338 return mRecordNumberCount++;
339 }
340
341 uint32
342 ModifiedTable::recordNumberCount() const
343 {
344 uint32 anOldMax = !mTable ? 0 : mTable->recordNumberCount() - 1;
345 uint32 anInsertedMax = mInsertedMap.empty() ? 0 : mInsertedMap.rbegin()->first;
346
347 DeletedSet::reverse_iterator anIt = mDeletedSet.rbegin();
348 DeletedSet::reverse_iterator anEnd = mDeletedSet.rend();
349 for (; anIt != anEnd; anIt++)
350 {
351 if (*anIt != anOldMax || anOldMax <= anInsertedMax)
352 break;
353 anOldMax--;
354 }
355
356 return max(anOldMax,anInsertedMax) + 1;
357 }
358
359 const MetaRecord &
360 ModifiedTable::getMetaRecord() const
361 {
362 return mNewMetaRecord ? *mNewMetaRecord : mTable->getMetaRecord();
363 }
364
365 // prepare to modify the table
366
367 void
368 ModifiedTable::modifyTable()
369 {
370 if (!mIsModified) {
371 createMutableIndexes();
372 mIsModified = true;
373 }
374 }
375
376 // create mutable indexes from the read-only indexes in the underlying table
377
378 void
379 ModifiedTable::createMutableIndexes()
380 {
381 if (mTable == NULL)
382 return;
383
384 Table::ConstIndexMap::const_iterator it;
385 for (it = mTable->mIndexMap.begin(); it != mTable->mIndexMap.end(); it++) {
386 auto_ptr<DbMutableIndex> mutableIndex(new DbMutableIndex(*it->second));
387 mIndexMap.insert(MutableIndexMap::value_type(it->first, mutableIndex.get()));
388 mutableIndex.release();
389 }
390 }
391
392 // find, and create if needed, an index with the given id
393
394 DbMutableIndex &
395 ModifiedTable::findIndex(uint32 indexId, const MetaRecord &metaRecord, bool isUniqueIndex)
396 {
397 MutableIndexMap::iterator it = mIndexMap.find(indexId);
398
399 if (it == mIndexMap.end()) {
400 // create the new index
401 auto_ptr<DbMutableIndex> index(new DbMutableIndex(metaRecord, indexId, isUniqueIndex));
402 it = mIndexMap.insert(MutableIndexMap::value_type(indexId, index.get())).first;
403 index.release();
404 }
405
406 return *it->second;
407 }
408
409 uint32
410 ModifiedTable::writeIndexSection(WriteSection &tableSection, uint32 offset)
411 {
412 MutableIndexMap::iterator it;
413
414 tableSection.put(Table::OffsetIndexesOffset, offset);
415
416 // leave room for the size, to be written later
417 uint32 indexSectionOffset = offset;
418 offset += AtomSize;
419
420 offset = tableSection.put(offset, mIndexMap.size());
421
422 // leave room for the array of offsets to the indexes
423 uint32 indexOffsetOffset = offset;
424 offset += mIndexMap.size() * AtomSize;
425
426 // write the indexes
427 for (it = mIndexMap.begin(); it != mIndexMap.end(); it++) {
428 indexOffsetOffset = tableSection.put(indexOffsetOffset, offset);
429 offset = it->second->writeIndex(tableSection, offset);
430 }
431
432 // write the total index section size
433 tableSection.put(indexSectionOffset, offset - indexSectionOffset);
434
435 return offset;
436 }
437
438 uint32
439 ModifiedTable::writeTable(AtomicFile &inAtomicFile, uint32 inSectionOffset)
440 {
441 if (mTable && !mIsModified) {
442 // the table has not been modified, so we can just dump the old table
443 // section into the new database
444
445 const ReadSection &tableSection = mTable->getTableSection();
446 uint32 tableSize = tableSection.at(Table::OffsetSize);
447
448 inAtomicFile.write(AtomicFile::FromStart, inSectionOffset,
449 tableSection.range(Range(0, tableSize)), tableSize);
450
451 return inSectionOffset + tableSize;
452 }
453
454 // We should have an old mTable or a mNewMetaRecord but not both.
455 assert(mTable != nil ^ mNewMetaRecord != nil);
456 const MetaRecord &aNewMetaRecord = getMetaRecord();
457
458 uint32 aRecordsCount = 0;
459 uint32 aRecordNumbersCount = recordNumberCount();
460 uint32 aRecordsOffset = Table::OffsetRecordNumbers + AtomSize * aRecordNumbersCount;
461 WriteSection aTableSection(CssmAllocator::standard(), aRecordsOffset);
462 aTableSection.size(aRecordsOffset);
463 aTableSection.put(Table::OffsetId, aNewMetaRecord.dataRecordType());
464 aTableSection.put(Table::OffsetRecords, aRecordsOffset);
465 aTableSection.put(Table::OffsetRecordNumbersCount, aRecordNumbersCount);
466
467 uint32 anOffset = inSectionOffset + aRecordsOffset;
468
469 if (mTable)
470 {
471 // XXX Handle schema changes in the future.
472 assert(mNewMetaRecord == nil);
473
474 // We have a modified old table so copy all non deleted records
475 // The code below is rather elaborate, but this is because it attempts
476 // to copy large ranges of non deleted records with single calls
477 // to AtomicFile::write()
478 uint32 anOldRecordsCount = mTable->getRecordsCount();
479 ReadSection aRecordsSection = mTable->getRecordsSection();
480 uint32 aReadOffset = 0; // Offset of current record
481 uint32 aWriteOffset = aRecordsOffset; // Offset for current write record
482 uint32 aBlockStart = aReadOffset; // Starting point for read
483 uint32 aBlockSize = 0; // Size of block to read
484 for (uint32 aRecord = 0; aRecord < anOldRecordsCount; aRecord++)
485 {
486 ReadSection aRecordSection = MetaRecord::readSection(aRecordsSection, aReadOffset);
487 uint32 aRecordNumber = MetaRecord::unpackRecordNumber(aRecordSection);
488 uint32 aRecordSize = aRecordSection.size();
489 aReadOffset += aRecordSize;
490 if (mDeletedSet.find(aRecordNumber) == mDeletedSet.end())
491 {
492 // This record has not been deleted. Register the offset
493 // at which it will be in the new file in aTableSection.
494 aTableSection.put(Table::OffsetRecordNumbers
495 + AtomSize * aRecordNumber,
496 aWriteOffset);
497 aWriteOffset += aRecordSize;
498 aBlockSize += aRecordSize;
499 aRecordsCount++;
500 // XXX update all indexes being created.
501 }
502 else
503 {
504 // The current record has been deleted. Copy all records up
505 // to but not including the current one to the new file.
506 if (aBlockSize > 0)
507 {
508 inAtomicFile.write(AtomicFile::FromStart, anOffset,
509 aRecordsSection.range(Range(aBlockStart,
510 aBlockSize)),
511 aBlockSize);
512 anOffset += aBlockSize;
513 }
514
515 // Set the start of the next block to the start of the next
516 // record, and the size of the block to 0.
517 aBlockStart = aReadOffset;
518 aBlockSize = 0;
519 } // if (mDeletedSet..)
520 } // for (aRecord...)
521
522 // Copy all records that have not yet been copied to the new file.
523 if (aBlockSize > 0)
524 {
525 inAtomicFile.write(AtomicFile::FromStart, anOffset,
526 aRecordsSection.range(Range(aBlockStart,
527 aBlockSize)),
528 aBlockSize);
529 anOffset += aBlockSize;
530 }
531 } // if (mTable)
532
533 // Now add all inserted records to the table.
534 InsertedMap::const_iterator anIt = mInsertedMap.begin();
535 InsertedMap::const_iterator anEnd = mInsertedMap.end();
536 // Iterate over all inserted objects.
537 for (; anIt != anEnd; anIt++)
538 {
539 // Write out each inserted/modified record
540 const WriteSection &aRecord = *anIt->second;
541 uint32 aRecordNumber = anIt->first;
542 // Put offset relative to start of this table in recordNumber array.
543 aTableSection.put(Table::OffsetRecordNumbers + AtomSize * aRecordNumber,
544 anOffset - inSectionOffset);
545 inAtomicFile.write(AtomicFile::FromStart, anOffset,
546 aRecord.address(), aRecord.size());
547 anOffset += aRecord.size();
548 aRecordsCount++;
549 // XXX update all indexes being created.
550 }
551
552 // Reconstruct the freelist (this is O(N) where N is the number of recordNumbers)
553 // We could implement it faster by using the old freelist and skipping the records
554 // that have been inserted. However building the freelist for the newly used
555 // recordNumbers (not in mTable) would look like the code below anyway (starting
556 // from mTable->recordNumberCount()).
557 // The first part of this would be O(M Log(N)) (where M is the old number of
558 // free records, and N is the number of newly inserted records)
559 // The second part would be O(N) where N is the currently max RecordNumber
560 // in use - the old max RecordNumber in use.
561 uint32 aFreeListHead = 0; // Link to previous free record
562 for (uint32 aRecordNumber = 0; aRecordNumber < aRecordNumbersCount; aRecordNumber++)
563 {
564 // Make the freelist a list of all records with 0 offset (non existing).
565 if (!aTableSection.at(Table::OffsetRecordNumbers + AtomSize * aRecordNumber))
566 {
567 aTableSection.put(Table::OffsetRecordNumbers
568 + AtomSize * aRecordNumber,
569 aFreeListHead);
570 // Make aFreeListHead point to the previous free recordNumber slot in the table.
571 aFreeListHead = (Table::OffsetRecordNumbers + AtomSize * aRecordNumber) | 1;
572 }
573 }
574 aTableSection.put(Table::OffsetFreeListHead, aFreeListHead);
575
576 anOffset -= inSectionOffset;
577
578 // Write out indexes, which are part of the table section
579
580 {
581 uint32 indexOffset = anOffset;
582 anOffset = writeIndexSection(aTableSection, anOffset);
583 inAtomicFile.write(AtomicFile::FromStart, inSectionOffset + indexOffset,
584 aTableSection.address() + indexOffset, anOffset - indexOffset);
585 }
586
587 // Set the section size and recordCount.
588 aTableSection.put(Table::OffsetSize, anOffset);
589 aTableSection.put(Table::OffsetRecordsCount, aRecordsCount);
590
591 // Write out aTableSection header.
592 inAtomicFile.write(AtomicFile::FromStart, inSectionOffset,
593 aTableSection.address(), aTableSection.size());
594
595 return anOffset + inSectionOffset;
596 }
597
598
599
600 //
601 // Metadata
602 //
603
604 // Attribute definitions
605
606 static const CSSM_DB_ATTRIBUTE_INFO RelationID =
607 {
608 CSSM_DB_ATTRIBUTE_NAME_AS_STRING,
609 {"RelationID"},
610 CSSM_DB_ATTRIBUTE_FORMAT_UINT32
611 };
612 static const CSSM_DB_ATTRIBUTE_INFO RelationName =
613 {
614 CSSM_DB_ATTRIBUTE_NAME_AS_STRING,
615 {"RelationName"},
616 CSSM_DB_ATTRIBUTE_FORMAT_STRING
617 };
618 static const CSSM_DB_ATTRIBUTE_INFO AttributeID =
619 {
620 CSSM_DB_ATTRIBUTE_NAME_AS_STRING,
621 {"AttributeID"},
622 CSSM_DB_ATTRIBUTE_FORMAT_UINT32
623 };
624 static const CSSM_DB_ATTRIBUTE_INFO AttributeNameFormat =
625 {
626 CSSM_DB_ATTRIBUTE_NAME_AS_STRING,
627 {"AttributeNameFormat"},
628 CSSM_DB_ATTRIBUTE_FORMAT_UINT32
629 };
630 static const CSSM_DB_ATTRIBUTE_INFO AttributeName =
631 {
632 CSSM_DB_ATTRIBUTE_NAME_AS_STRING,
633 {"AttributeName"},
634 CSSM_DB_ATTRIBUTE_FORMAT_STRING
635 };
636 static const CSSM_DB_ATTRIBUTE_INFO AttributeNameID =
637 {
638 CSSM_DB_ATTRIBUTE_NAME_AS_STRING,
639 {"AttributeNameID"},
640 CSSM_DB_ATTRIBUTE_FORMAT_BLOB
641 };
642 static const CSSM_DB_ATTRIBUTE_INFO AttributeFormat =
643 {
644 CSSM_DB_ATTRIBUTE_NAME_AS_STRING,
645 {"AttributeFormat"},
646 CSSM_DB_ATTRIBUTE_FORMAT_UINT32
647 };
648 static const CSSM_DB_ATTRIBUTE_INFO IndexID =
649 {
650 CSSM_DB_ATTRIBUTE_NAME_AS_STRING,
651 {"IndexID"},
652 CSSM_DB_ATTRIBUTE_FORMAT_UINT32
653 };
654 static const CSSM_DB_ATTRIBUTE_INFO IndexType =
655 {
656 CSSM_DB_ATTRIBUTE_NAME_AS_STRING,
657 {"IndexType"},
658 CSSM_DB_ATTRIBUTE_FORMAT_UINT32
659 };
660 static const CSSM_DB_ATTRIBUTE_INFO IndexedDataLocation =
661 {
662 CSSM_DB_ATTRIBUTE_NAME_AS_STRING,
663 {"IndexedDataLocation"},
664 CSSM_DB_ATTRIBUTE_FORMAT_UINT32
665 };
666 static const CSSM_DB_ATTRIBUTE_INFO ModuleID =
667 {
668 CSSM_DB_ATTRIBUTE_NAME_AS_STRING,
669 {"ModuleID"},
670 CSSM_DB_ATTRIBUTE_FORMAT_BLOB
671 };
672 static const CSSM_DB_ATTRIBUTE_INFO AddinVersion =
673 {
674 CSSM_DB_ATTRIBUTE_NAME_AS_STRING,
675 {"AddinVersion"},
676 CSSM_DB_ATTRIBUTE_FORMAT_STRING
677 };
678 static const CSSM_DB_ATTRIBUTE_INFO SSID =
679 {
680 CSSM_DB_ATTRIBUTE_NAME_AS_STRING,
681 {"SSID"},
682 CSSM_DB_ATTRIBUTE_FORMAT_UINT32
683 };
684 static const CSSM_DB_ATTRIBUTE_INFO SubserviceType =
685 {
686 CSSM_DB_ATTRIBUTE_NAME_AS_STRING,
687 {"SubserviceType"},
688 CSSM_DB_ATTRIBUTE_FORMAT_UINT32
689 };
690
691 #define ATTRIBUTE(type, name) \
692 { CSSM_DB_ATTRIBUTE_NAME_AS_STRING, { #name }, CSSM_DB_ATTRIBUTE_FORMAT_ ## type }
693
694 static const CSSM_DB_ATTRIBUTE_INFO AttrSchemaRelations[] =
695 {
696 //RelationID, RelationName
697 ATTRIBUTE(UINT32, RelationID),
698 ATTRIBUTE(STRING, RelationName)
699 };
700
701 static const CSSM_DB_ATTRIBUTE_INFO AttrSchemaAttributes[] =
702 {
703 //RelationID, AttributeID,
704 //AttributeNameFormat, AttributeName, AttributeNameID,
705 //AttributeFormat
706 ATTRIBUTE(UINT32, RelationID),
707 ATTRIBUTE(UINT32, AttributeID),
708 ATTRIBUTE(UINT32, AttributeNameFormat),
709 ATTRIBUTE(STRING, AttributeName),
710 ATTRIBUTE(BLOB, AttributeNameID),
711 ATTRIBUTE(UINT32, AttributeFormat)
712 };
713
714 static const CSSM_DB_ATTRIBUTE_INFO AttrSchemaIndexes[] =
715 {
716 ATTRIBUTE(UINT32, RelationID),
717 ATTRIBUTE(UINT32, IndexID),
718 ATTRIBUTE(UINT32, AttributeID),
719 ATTRIBUTE(UINT32, IndexType),
720 ATTRIBUTE(UINT32, IndexedDataLocation)
721 //RelationID, IndexID, AttributeID,
722 //IndexType, IndexedDataLocation
723 };
724
725 static const CSSM_DB_ATTRIBUTE_INFO AttrSchemaParsingModule[] =
726 {
727 ATTRIBUTE(UINT32, RelationID),
728 ATTRIBUTE(UINT32, AttributeID),
729 ATTRIBUTE(BLOB, ModuleID),
730 ATTRIBUTE(STRING, AddinVersion),
731 ATTRIBUTE(UINT32, SSID),
732 ATTRIBUTE(UINT32, SubserviceType)
733 //RelationID, AttributeID,
734 //ModuleID, AddinVersion, SSID, SubserviceType
735 };
736
737 #undef ATTRIBUTE
738
739 //
740 // DbVersion
741 //
742 DbVersion::DbVersion(AtomicFile &inDatabaseFile,
743 const AppleDatabase &db) :
744 mDatabase(reinterpret_cast<const uint8 *>(NULL), 0), mDatabaseFile(&inDatabaseFile),
745 mDb(db)
746 {
747 const uint8 *aFileAddress;
748 size_t aLength;
749 mVersionId = mDatabaseFile->enterRead(aFileAddress, aLength);
750 mDatabase = ReadSection(aFileAddress, aLength);
751 open();
752 }
753
754 DbVersion::~DbVersion()
755 {
756 try
757 {
758 for_each_map_delete(mTableMap.begin(), mTableMap.end());
759 if (mDatabaseFile)
760 mDatabaseFile->exitRead(mVersionId);
761 }
762 catch(...) {}
763 }
764
765 bool
766 DbVersion::isDirty() const
767 {
768 if (mDatabaseFile)
769 return mDatabaseFile->isDirty(mVersionId);
770
771 return true;
772 }
773
774 void
775 DbVersion::open()
776 {
777 try
778 {
779 // This is the oposite of DbModifier::commit()
780 const ReadSection aHeaderSection = mDatabase.subsection(HeaderOffset,
781 HeaderSize);
782 if (aHeaderSection.at(OffsetMagic) != HeaderMagic)
783 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
784
785 // We currently only support one version. If we support additional
786 // file format versions in the future fix this.
787 uint32 aVersion = aHeaderSection.at(OffsetVersion);
788 if (aVersion != HeaderVersion)
789 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
790
791 //const ReadSection anAuthSection =
792 // mDatabase.subsection(HeaderOffset + aHeaderSection.at(OffsetAuthOffset));
793 // XXX Do something with anAuthSection.
794
795 uint32 aSchemaOffset = aHeaderSection.at(OffsetSchemaOffset);
796 const ReadSection aSchemaSection =
797 mDatabase.subsection(HeaderOffset + aSchemaOffset);
798
799 uint32 aSchemaSize = aSchemaSection[OffsetSchemaSize];
800 // Make sure that the given range exists.
801 aSchemaSection.subsection(0, aSchemaSize);
802 uint32 aTableCount = aSchemaSection[OffsetTablesCount];
803
804 // Assert that the size of this section is big enough.
805 if (aSchemaSize < OffsetTables + AtomSize * aTableCount)
806 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
807
808 for (uint32 aTableNumber = 0; aTableNumber < aTableCount;
809 aTableNumber++)
810 {
811 uint32 aTableOffset = aSchemaSection.at(OffsetTables + AtomSize
812 * aTableNumber);
813 // XXX Set the size boundary on aTableSection.
814 const ReadSection aTableSection =
815 aSchemaSection.subsection(aTableOffset);
816 auto_ptr<Table> aTable(new Table(aTableSection));
817 Table::Id aTableId = aTable->getMetaRecord().dataRecordType();
818 mTableMap.insert(TableMap::value_type(aTableId, aTable.get()));
819 aTable.release();
820 }
821
822 // Fill in the schema for the meta tables.
823
824 findTable(mDb.schemaRelations.DataRecordType).getMetaRecord().
825 setRecordAttributeInfo(mDb.schemaRelations);
826 findTable(mDb.schemaIndexes.DataRecordType).getMetaRecord().
827 setRecordAttributeInfo(mDb.schemaIndexes);
828 findTable(mDb.schemaParsingModule.DataRecordType).getMetaRecord().
829 setRecordAttributeInfo(mDb.schemaParsingModule);
830
831 // OK, we have created all the tables in the tableMap. Now
832 // lets read the schema and proccess it accordingly.
833 // Iterate over all schema records.
834 Table &aTable = findTable(mDb.schemaAttributes.DataRecordType);
835 aTable.getMetaRecord().setRecordAttributeInfo(mDb.schemaAttributes);
836 uint32 aRecordsCount = aTable.getRecordsCount();
837 ReadSection aRecordsSection = aTable.getRecordsSection();
838 uint32 aReadOffset = 0;
839 const MetaRecord &aMetaRecord = aTable.getMetaRecord();
840
841 CSSM_DB_ATTRIBUTE_DATA aRelationIDData =
842 {
843 RelationID,
844 0,
845 NULL
846 };
847 CSSM_DB_ATTRIBUTE_DATA aAttributeIDData =
848 {
849 AttributeID,
850 0,
851 NULL
852 };
853 CSSM_DB_ATTRIBUTE_DATA aAttributeNameFormatData =
854 {
855 AttributeNameFormat,
856 0,
857 NULL
858 };
859 CSSM_DB_ATTRIBUTE_DATA aAttributeNameData =
860 {
861 AttributeName,
862 0,
863 NULL
864 };
865 CSSM_DB_ATTRIBUTE_DATA aAttributeNameIDData =
866 {
867 AttributeNameID,
868 0,
869 NULL
870 };
871 CSSM_DB_ATTRIBUTE_DATA aAttributeFormatData =
872 {
873 AttributeFormat,
874 0,
875 NULL
876 };
877 CSSM_DB_ATTRIBUTE_DATA aRecordAttributes[] =
878 {
879 aRelationIDData,
880 aAttributeIDData,
881 aAttributeNameFormatData,
882 aAttributeNameData,
883 aAttributeNameIDData,
884 aAttributeFormatData
885 };
886 CSSM_DB_RECORD_ATTRIBUTE_DATA aRecordAttributeData =
887 {
888 aMetaRecord.dataRecordType(),
889 0,
890 sizeof(aRecordAttributes) / sizeof(CSSM_DB_ATTRIBUTE_DATA),
891 aRecordAttributes
892 };
893 CssmDbRecordAttributeData &aRecordData = CssmDbRecordAttributeData::overlay(aRecordAttributeData);
894
895 TrackingAllocator recordAllocator(CssmAllocator::standard());
896 for (uint32 aRecord = 0; aRecord != aRecordsCount; aRecord++)
897 {
898 ReadSection aRecordSection = MetaRecord::readSection(aRecordsSection, aReadOffset);
899 uint32 aRecordSize = aRecordSection.size();
900 aReadOffset += aRecordSize;
901 #if 0
902 try
903 {
904 #endif
905 aMetaRecord.unpackRecord(aRecordSection, recordAllocator,
906 &aRecordAttributeData, NULL, 0);
907 // Create the attribute coresponding to this entry
908 if (aRecordData[0].size() != 1 || aRecordData[0].format() != CSSM_DB_ATTRIBUTE_FORMAT_UINT32)
909 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
910 uint32 aRelationId = aRecordData[0];
911
912 // Skip the schema relations for the meta tables themselves.
913 // FIXME: this hard-wires the meta-table relation IDs to be
914 // within {CSSM_DB_RECORDTYPE_SCHEMA_START...
915 // CSSM_DB_RECORDTYPE_SCHEMA_END} (which is {0..4}).
916 // Bogus - the MDS schema relation IDs start at
917 // CSSM_DB_RELATIONID_MDS_START which is 0x40000000.
918 // Ref. Radar 2817921.
919 if (CSSM_DB_RECORDTYPE_SCHEMA_START <= aRelationId && aRelationId < CSSM_DB_RECORDTYPE_SCHEMA_END)
920 continue;
921
922 // Get the MetaRecord corresponding to the specified RelationId
923 MetaRecord &aMetaRecord = findTable(aRelationId).getMetaRecord();
924
925 if (aRecordData[1].size() != 1
926 || aRecordData[1].format() != CSSM_DB_ATTRIBUTE_FORMAT_UINT32
927 || aRecordData[2].size() != 1
928 || aRecordData[2].format() != CSSM_DB_ATTRIBUTE_FORMAT_UINT32
929 || aRecordData[5].size() != 1
930 || aRecordData[5].format() != CSSM_DB_ATTRIBUTE_FORMAT_UINT32)
931 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
932
933 uint32 anAttributeId = aRecordData[1];
934 uint32 anAttributeNameFormat = aRecordData[2];
935 uint32 anAttributeFormat = aRecordData[5];
936 auto_ptr<string> aName;
937 const CssmData *aNameID = NULL;
938
939 if (aRecordData[3].size() == 1)
940 {
941 if (aRecordData[3].format() != CSSM_DB_ATTRIBUTE_FORMAT_STRING)
942 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
943
944 auto_ptr<string> aName2(new string(static_cast<string>(aRecordData[3])));
945 aName = aName2;
946 }
947
948 if (aRecordData[4].size() == 1)
949 {
950 if (aRecordData[4].format() != CSSM_DB_ATTRIBUTE_FORMAT_BLOB)
951 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
952
953 // @@@ Invoking conversion operator to CssmData & on aRecordData[4]
954 // And taking address of result.
955 aNameID = &static_cast<CssmData &>(aRecordData[4]);
956 }
957
958 // Make sure that the attribute specified by anAttributeNameFormat is present.
959 switch (anAttributeNameFormat)
960 {
961 case CSSM_DB_ATTRIBUTE_NAME_AS_STRING:
962 if (aRecordData[3].size() != 1)
963 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
964 break;
965 case CSSM_DB_ATTRIBUTE_NAME_AS_OID:
966 if (aRecordData[4].size() != 1)
967 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
968 break;
969 case CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER:
970 break;
971 default:
972 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
973 }
974
975 // Create the attribute
976 aMetaRecord.createAttribute(aName.get(), aNameID, anAttributeId, anAttributeFormat);
977
978 #if 0
979 // Free the data.
980 aRecordData.deleteValues(CssmAllocator::standard());
981 }
982 catch(...)
983 {
984 aRecordData.deleteValues(CssmAllocator::standard());
985 throw;
986 }
987 #endif
988 }
989
990 // initialize the indexes associated with each table
991 {
992 TableMap::iterator it;
993 for (it = mTableMap.begin(); it != mTableMap.end(); it++)
994 it->second->readIndexSection();
995 }
996 }
997 catch(...)
998 {
999 for_each_map_delete(mTableMap.begin(), mTableMap.end());
1000 mTableMap.clear();
1001 throw;
1002 }
1003 }
1004
1005 const RecordId
1006 DbVersion::getRecord(Table::Id inTableId, const RecordId &inRecordId,
1007 CSSM_DB_RECORD_ATTRIBUTE_DATA *inoutAttributes,
1008 CssmData *inoutData,
1009 CssmAllocator &inAllocator) const
1010 {
1011 return findTable(inTableId).getRecord(inRecordId, inoutAttributes,
1012 inoutData, inAllocator);
1013 }
1014
1015 Cursor *
1016 DbVersion::createCursor(const CSSM_QUERY *inQuery) const
1017 {
1018 // XXX We should add support for these special query types
1019 // By Creating a Cursor that iterates over multiple tables
1020 if (!inQuery || inQuery->RecordType == CSSM_DL_DB_RECORD_ANY
1021 || inQuery->RecordType == CSSM_DL_DB_RECORD_ALL_KEYS)
1022 {
1023 return new MultiCursor(inQuery, *this);
1024 }
1025
1026 return findTable(inQuery->RecordType).createCursor(inQuery, *this);
1027 }
1028
1029 const Table &
1030 DbVersion::findTable(Table::Id inTableId) const
1031 {
1032 TableMap::const_iterator it = mTableMap.find(inTableId);
1033 if (it == mTableMap.end())
1034 CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE);
1035 return *it->second;
1036 }
1037
1038 Table &
1039 DbVersion::findTable(Table::Id inTableId)
1040 {
1041 TableMap::iterator it = mTableMap.find(inTableId);
1042 if (it == mTableMap.end())
1043 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
1044 return *it->second;
1045 }
1046
1047 //
1048 // Cursor implemetation
1049 //
1050 Cursor::~Cursor()
1051 {
1052 }
1053
1054
1055 //
1056 // LinearCursor implemetation
1057 //
1058 LinearCursor::LinearCursor(const CSSM_QUERY *inQuery, const DbVersion &inDbVersion,
1059 const Table &inTable) :
1060 mDbVersion(&inDbVersion),
1061 mRecordsCount(inTable.getRecordsCount()),
1062 mRecord(0),
1063 mRecordsSection(inTable.getRecordsSection()),
1064 mReadOffset(0),
1065 mMetaRecord(inTable.getMetaRecord())
1066 {
1067 if (inQuery)
1068 {
1069 mConjunctive = inQuery->Conjunctive;
1070 mQueryFlags = inQuery->QueryFlags;
1071 // XXX Do something with inQuery->QueryLimits?
1072 uint32 aPredicatesCount = inQuery->NumSelectionPredicates;
1073 mPredicates.resize(aPredicatesCount);
1074 try
1075 {
1076 for (uint32 anIndex = 0; anIndex < aPredicatesCount; anIndex++)
1077 {
1078 CSSM_SELECTION_PREDICATE &aPredicate = inQuery->SelectionPredicate[anIndex];
1079 mPredicates[anIndex] = new SelectionPredicate(mMetaRecord, aPredicate);
1080 }
1081 }
1082 catch(...)
1083 {
1084 for_each_delete(mPredicates.begin(), mPredicates.end());
1085 throw;
1086 }
1087 }
1088 }
1089
1090 LinearCursor::~LinearCursor()
1091 {
1092 for_each_delete(mPredicates.begin(), mPredicates.end());
1093 }
1094
1095 bool
1096 LinearCursor::next(Table::Id &outTableId,
1097 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes,
1098 CssmData *inoutData, CssmAllocator &inAllocator, RecordId &recordId)
1099 {
1100 while (mRecord++ < mRecordsCount)
1101 {
1102 ReadSection aRecordSection = MetaRecord::readSection(mRecordsSection, mReadOffset);
1103 uint32 aRecordSize = aRecordSection.size();
1104 mReadOffset += aRecordSize;
1105
1106 PredicateVector::const_iterator anIt = mPredicates.begin();
1107 PredicateVector::const_iterator anEnd = mPredicates.end();
1108 bool aMatch;
1109 if (anIt == anEnd)
1110 {
1111 // If there are no predicates we have a match.
1112 aMatch = true;
1113 }
1114 else if (mConjunctive == CSSM_DB_OR)
1115 {
1116 // If mConjunctive is OR, the first predicate that returns
1117 // true indicates a match. Dropthough means no match
1118 aMatch = false;
1119 for (; anIt != anEnd; anIt++)
1120 {
1121 if ((*anIt)->evaluate(aRecordSection))
1122 {
1123 aMatch = true;
1124 break;
1125 }
1126 }
1127 }
1128 else if (mConjunctive == CSSM_DB_AND || mConjunctive == CSSM_DB_NONE)
1129 {
1130 // If mConjunctive is AND (or NONE), the first predicate that returns
1131 // false indicates a mismatch. Dropthough means a match
1132 aMatch = true;
1133 for (; anIt != anEnd; anIt++)
1134 {
1135 if (!(*anIt)->evaluate(aRecordSection))
1136 {
1137 aMatch = false;
1138 break;
1139 }
1140 }
1141 }
1142 else
1143 {
1144 // XXX Should be CSSMERR_DL_INVALID_QUERY (or CSSMERR_DL_INVALID_CONJUNTIVE).
1145 CssmError::throwMe(CSSMERR_DL_UNSUPPORTED_QUERY);
1146 }
1147
1148 if (aMatch)
1149 {
1150 // Get the actual record.
1151 mMetaRecord.unpackRecord(aRecordSection, inAllocator,
1152 inoutAttributes, inoutData,
1153 mQueryFlags);
1154 outTableId = mMetaRecord.dataRecordType();
1155 recordId = MetaRecord::unpackRecordId(aRecordSection);
1156 return true;
1157 }
1158 }
1159
1160 return false;
1161 }
1162
1163 //
1164 // IndexCursor
1165 //
1166
1167 IndexCursor::IndexCursor(DbQueryKey *queryKey, const DbVersion &inDbVersion,
1168 const Table &table, const DbConstIndex *index)
1169 : mQueryKey(queryKey), mDbVersion(inDbVersion), mTable(table), mIndex(index)
1170 {
1171 index->performQuery(*queryKey, mBegin, mEnd);
1172 }
1173
1174 IndexCursor::~IndexCursor()
1175 {
1176 // the query key will be deleted automatically, since it's an auto_ptr
1177 }
1178
1179 bool
1180 IndexCursor::next(Table::Id &outTableId,
1181 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR outAttributes,
1182 CssmData *outData,
1183 CssmAllocator &inAllocator, RecordId &recordId)
1184 {
1185 if (mBegin == mEnd)
1186 return false;
1187
1188 ReadSection rs = mIndex->getRecordSection(mBegin++);
1189 const MetaRecord &metaRecord = mTable.getMetaRecord();
1190
1191 outTableId = metaRecord.dataRecordType();
1192 metaRecord.unpackRecord(rs, inAllocator, outAttributes, outData, 0);
1193
1194 recordId = MetaRecord::unpackRecordId(rs);
1195 return true;
1196 }
1197
1198 //
1199 // MultiCursor
1200 //
1201 MultiCursor::MultiCursor(const CSSM_QUERY *inQuery, const DbVersion &inDbVersion) :
1202 mDbVersion(&inDbVersion), mTableIterator(inDbVersion.begin())
1203 {
1204 if (inQuery)
1205 mQuery.reset(new CssmAutoQuery(*inQuery));
1206 else
1207 {
1208 mQuery.reset(new CssmAutoQuery());
1209 mQuery->recordType(CSSM_DL_DB_RECORD_ANY);
1210 }
1211 }
1212
1213 MultiCursor::~MultiCursor()
1214 {
1215 }
1216
1217 bool
1218 MultiCursor::next(Table::Id &outTableId,
1219 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes,
1220 CssmData *inoutData, CssmAllocator &inAllocator, RecordId &recordId)
1221 {
1222 for (;;)
1223 {
1224 if (!mCursor.get())
1225 {
1226 if (mTableIterator == mDbVersion->end())
1227 return false;
1228
1229 const Table &aTable = *mTableIterator++;
1230 if (!aTable.matchesTableId(mQuery->recordType()))
1231 continue;
1232
1233 mCursor.reset(aTable.createCursor(mQuery.get(), *mDbVersion));
1234 }
1235
1236 if (mCursor->next(outTableId, inoutAttributes, inoutData, inAllocator, recordId))
1237 return true;
1238
1239 mCursor.reset(NULL);
1240 }
1241 }
1242
1243
1244 //
1245 // DbModifier
1246 //
1247 DbModifier::DbModifier(AtomicFile &inAtomicFile, const AppleDatabase &db) :
1248 Metadata(),
1249 mDbVersion(),
1250 mAtomicFile(inAtomicFile),
1251 mWriting(false),
1252 mDb(db)
1253 {
1254 }
1255
1256 DbModifier::~DbModifier()
1257 {
1258 try
1259 {
1260 for_each_map_delete(mModifiedTableMap.begin(), mModifiedTableMap.end());
1261
1262 if (mWriting)
1263 rollback();
1264 }
1265 catch(...) {}
1266 }
1267
1268 const RefPointer<const DbVersion>
1269 DbModifier::getDbVersion()
1270 {
1271 StLock<Mutex> _(mDbVersionLock);
1272 if (mDbVersion && mDbVersion->isDirty())
1273 mDbVersion = NULL;
1274
1275 if (mDbVersion == NULL)
1276 mDbVersion = new DbVersion(mAtomicFile, mDb);
1277
1278 return mDbVersion;
1279 }
1280
1281 void
1282 DbModifier::createDatabase(const CSSM_DBINFO &inDbInfo,
1283 const CSSM_ACL_ENTRY_INPUT *inInitialAclEntry)
1284 {
1285 // XXX This needs better locking. There is a possible race condition between
1286 // two concurrent creators. Or a writer/creator or a close/create etc.
1287 if (mWriting || !mModifiedTableMap.empty())
1288 CssmError::throwMe(CSSMERR_DL_DATASTORE_ALREADY_EXISTS);
1289
1290 mVersionId = mAtomicFile.enterCreate(mFileRef);
1291 mWriting = true;
1292
1293 // we need to create the meta tables first, because inserting tables
1294 // (including the meta tables themselves) relies on them being there
1295 createTable(new MetaRecord(mDb.schemaRelations));
1296 createTable(new MetaRecord(mDb.schemaAttributes));
1297 createTable(new MetaRecord(mDb.schemaIndexes));
1298 createTable(new MetaRecord(mDb.schemaParsingModule));
1299
1300 // now add the meta-tables' schema to the meta tables themselves
1301 insertTableSchema(mDb.schemaRelations);
1302 insertTableSchema(mDb.schemaAttributes);
1303 insertTableSchema(mDb.schemaIndexes);
1304 insertTableSchema(mDb.schemaParsingModule);
1305
1306 if (inInitialAclEntry != NULL)
1307 {
1308 //createACL(*inInitialAclEntry);
1309 }
1310
1311 if (inDbInfo.NumberOfRecordTypes == 0)
1312 return;
1313 if (inDbInfo.RecordAttributeNames == NULL)
1314 CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE);
1315 if (inDbInfo.RecordIndexes == NULL)
1316 CssmError::throwMe(CSSMERR_DL_INVALID_RECORD_INDEX);
1317 if (inDbInfo.DefaultParsingModules == NULL)
1318 CssmError::throwMe(CSSMERR_DL_INVALID_PARSING_MODULE);
1319
1320 for (uint32 anIndex = 0; anIndex < inDbInfo.NumberOfRecordTypes; anIndex++)
1321 {
1322 insertTable(CssmDbRecordAttributeInfo::overlay(inDbInfo.RecordAttributeNames[anIndex]),
1323 &inDbInfo.RecordIndexes[anIndex],
1324 &inDbInfo.DefaultParsingModules[anIndex]);
1325 }
1326 }
1327
1328 void DbModifier::openDatabase()
1329 {
1330 commit(); // XXX Requires write lock.
1331 getDbVersion();
1332 }
1333
1334 void DbModifier::closeDatabase()
1335 {
1336 commit(); // XXX Requires write lock.
1337 StLock<Mutex> _(mDbVersionLock);
1338 mDbVersion = NULL;
1339 }
1340
1341 void DbModifier::deleteDatabase()
1342 {
1343 rollback(); // XXX Requires write lock. Also if autoCommit was disabled
1344 // this will incorrectly cause the performDelete to throw CSSMERR_DB_DOES_NOT_EXIST.
1345 StLock<Mutex> _(mDbVersionLock);
1346
1347 // Clean up mModifiedTableMap in case this object gets reused again for
1348 // a new create.
1349 for_each_map_delete(mModifiedTableMap.begin(), mModifiedTableMap.end());
1350 mModifiedTableMap.clear();
1351
1352 mDbVersion = NULL;
1353 mAtomicFile.performDelete();
1354 }
1355
1356 void
1357 DbModifier::modifyDatabase()
1358 {
1359 if (mWriting)
1360 return;
1361
1362 try
1363 {
1364 const uint8 *aFileAddress;
1365 size_t aLength;
1366 mVersionId = mAtomicFile.enterWrite(aFileAddress, aLength, mFileRef);
1367 mWriting = true;
1368 {
1369 // Aquire the mutex protecting mDbVersion
1370 StLock<Mutex> _l(mDbVersionLock);
1371 if (mDbVersion == nil || mDbVersion->getVersionId() != mVersionId)
1372 {
1373 // This will call enterRead(). Now that we hold the write
1374 // lock on the file this ensures we get the same verison
1375 // enterWrite just returned.
1376 mDbVersion = new DbVersion(mAtomicFile, mDb);
1377 }
1378 }
1379
1380 // Remove all old modified tables
1381 for_each_map_delete(mModifiedTableMap.begin(), mModifiedTableMap.end());
1382 mModifiedTableMap.clear();
1383
1384 // Setup the new tables
1385 DbVersion::TableMap::const_iterator anIt =
1386 mDbVersion->mTableMap.begin();
1387 DbVersion::TableMap::const_iterator anEnd =
1388 mDbVersion->mTableMap.end();
1389 for (; anIt != anEnd; ++anIt)
1390 {
1391 auto_ptr<ModifiedTable> aTable(new ModifiedTable(anIt->second));
1392 mModifiedTableMap.insert(ModifiedTableMap::value_type(anIt->first,
1393 aTable.get()));
1394 aTable.release();
1395 }
1396 }
1397 catch(...)
1398 {
1399 for_each_map_delete(mModifiedTableMap.begin(), mModifiedTableMap.end());
1400 mModifiedTableMap.clear();
1401 rollback();
1402 throw;
1403 }
1404 }
1405
1406 void
1407 DbModifier::deleteRecord(Table::Id inTableId, const RecordId &inRecordId)
1408 {
1409 modifyDatabase();
1410 findTable(inTableId).deleteRecord(inRecordId);
1411 }
1412
1413 const RecordId
1414 DbModifier::insertRecord(Table::Id inTableId,
1415 const CSSM_DB_RECORD_ATTRIBUTE_DATA *inAttributes,
1416 const CssmData *inData)
1417 {
1418 modifyDatabase();
1419 return findTable(inTableId).insertRecord(mVersionId, inAttributes, inData);
1420 }
1421
1422 const RecordId
1423 DbModifier::updateRecord(Table::Id inTableId, const RecordId &inRecordId,
1424 const CSSM_DB_RECORD_ATTRIBUTE_DATA *inAttributes,
1425 const CssmData *inData,
1426 CSSM_DB_MODIFY_MODE inModifyMode)
1427 {
1428 commit(); // XXX this is not thread safe, but what is?
1429 modifyDatabase();
1430 return findTable(inTableId).updateRecord(inRecordId, inAttributes, inData, inModifyMode);
1431 }
1432
1433 // Create a table associated with a given metarecord, and add the table
1434 // to the database.
1435
1436 ModifiedTable *
1437 DbModifier::createTable(MetaRecord *inMetaRecord)
1438 {
1439 auto_ptr<MetaRecord> aMetaRecord(inMetaRecord);
1440 auto_ptr<ModifiedTable> aModifiedTable(new ModifiedTable(inMetaRecord));
1441 // Now that aModifiedTable is fully constructed it owns inMetaRecord
1442 aMetaRecord.release();
1443
1444 if (!mModifiedTableMap.insert
1445 (ModifiedTableMap::value_type(inMetaRecord->dataRecordType(),
1446 aModifiedTable.get())).second)
1447 {
1448 // XXX Should be CSSMERR_DL_DUPLICATE_RECORDTYPE. Since that
1449 // doesn't exist we report that the metatable's unique index would
1450 // no longer be valid
1451 CssmError::throwMe(CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA);
1452 }
1453
1454 return aModifiedTable.release();
1455 }
1456
1457 void
1458 DbModifier::deleteTable(Table::Id inTableId)
1459 {
1460 modifyDatabase();
1461 // Can't delete schema tables.
1462 if (CSSM_DB_RECORDTYPE_SCHEMA_START <= inTableId
1463 && inTableId < CSSM_DB_RECORDTYPE_SCHEMA_END)
1464 CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE);
1465
1466 // Find the ModifiedTable and delete it
1467 ModifiedTableMap::iterator it = mModifiedTableMap.find(inTableId);
1468 if (it == mModifiedTableMap.end())
1469 CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE);
1470
1471 delete it->second;
1472 mModifiedTableMap.erase(it);
1473 }
1474
1475 uint32
1476 DbModifier::writeAuthSection(uint32 inSectionOffset)
1477 {
1478 WriteSection anAuthSection;
1479
1480 // XXX Put real data into the authsection.
1481 uint32 anOffset = anAuthSection.put(0, 0);
1482 anAuthSection.size(anOffset);
1483
1484 mAtomicFile.write(AtomicFile::FromStart, inSectionOffset,
1485 anAuthSection.address(), anAuthSection.size());
1486 return inSectionOffset + anOffset;
1487 }
1488
1489 uint32
1490 DbModifier::writeSchemaSection(uint32 inSectionOffset)
1491 {
1492 uint32 aTableCount = mModifiedTableMap.size();
1493 WriteSection aTableSection(CssmAllocator::standard(),
1494 OffsetTables + AtomSize * aTableCount);
1495 // Set aTableSection to the correct size.
1496 aTableSection.size(OffsetTables + AtomSize * aTableCount);
1497 aTableSection.put(OffsetTablesCount, aTableCount);
1498
1499 uint32 anOffset = inSectionOffset + OffsetTables + AtomSize * aTableCount;
1500 ModifiedTableMap::const_iterator anIt = mModifiedTableMap.begin();
1501 ModifiedTableMap::const_iterator anEnd = mModifiedTableMap.end();
1502 for (uint32 aTableNumber = 0; anIt != anEnd; anIt++, aTableNumber++)
1503 {
1504 // Put the offset to the current table relative to the start of
1505 // this section into the tables array
1506 aTableSection.put(OffsetTables + AtomSize * aTableNumber,
1507 anOffset - inSectionOffset);
1508 anOffset = anIt->second->writeTable(mAtomicFile, anOffset);
1509 }
1510
1511 aTableSection.put(OffsetSchemaSize, anOffset - inSectionOffset);
1512 mAtomicFile.write(AtomicFile::FromStart, inSectionOffset,
1513 aTableSection.address(), aTableSection.size());
1514
1515 return anOffset;
1516 }
1517
1518 void
1519 DbModifier::commit()
1520 {
1521 if (!mWriting)
1522 return;
1523 try
1524 {
1525 WriteSection aHeaderSection(CssmAllocator::standard(), size_t(HeaderSize));
1526 // Set aHeaderSection to the correct size.
1527 aHeaderSection.size(HeaderSize);
1528
1529 // Start writing sections after the header
1530 uint32 anOffset = HeaderOffset + HeaderSize;
1531
1532 // Write auth section
1533 aHeaderSection.put(OffsetAuthOffset, anOffset);
1534 anOffset = writeAuthSection(anOffset);
1535 // Write schema section
1536 aHeaderSection.put(OffsetSchemaOffset, anOffset);
1537 anOffset = writeSchemaSection(anOffset);
1538
1539 // Write out the file header.
1540 aHeaderSection.put(OffsetMagic, HeaderMagic);
1541 aHeaderSection.put(OffsetVersion, HeaderVersion);
1542 mAtomicFile.write(AtomicFile::FromStart, HeaderOffset,
1543 aHeaderSection.address(), aHeaderSection.size());
1544 }
1545 catch(...)
1546 {
1547 try
1548 {
1549 rollback(); // Sets mWriting to false;
1550 }
1551 catch(...) {}
1552 throw;
1553 }
1554
1555 mWriting = false;
1556 mAtomicFile.commit();
1557 }
1558
1559 void
1560 DbModifier::rollback()
1561 {
1562 if (mWriting)
1563 {
1564 mWriting = false;
1565 mAtomicFile.rollback();
1566 }
1567 }
1568
1569 const RecordId
1570 DbModifier::getRecord(Table::Id inTableId, const RecordId &inRecordId,
1571 CSSM_DB_RECORD_ATTRIBUTE_DATA *inoutAttributes,
1572 CssmData *inoutData, CssmAllocator &inAllocator)
1573 {
1574 // XXX never call commit(), rather search our own record tables.
1575 commit(); // XXX Requires write lock.
1576 return getDbVersion()->getRecord(inTableId, inRecordId,
1577 inoutAttributes, inoutData, inAllocator);
1578 }
1579
1580 Cursor *
1581 DbModifier::createCursor(const CSSM_QUERY *inQuery)
1582 {
1583 // XXX Be smarter as to when we must call commit (i.e. don't
1584 // force commit if the table being queried has not been modified).
1585 commit(); // XXX Requires write lock.
1586 return getDbVersion()->createCursor(inQuery);
1587 }
1588
1589 // Insert schema records for a new table into the metatables of the database. This gets
1590 // called while a database is being created.
1591
1592 void
1593 DbModifier::insertTableSchema(const CssmDbRecordAttributeInfo &inInfo,
1594 const CSSM_DB_RECORD_INDEX_INFO *inIndexInfo /* = NULL */)
1595 {
1596 ModifiedTable &aTable = findTable(inInfo.DataRecordType);
1597 const MetaRecord &aMetaRecord = aTable.getMetaRecord();
1598
1599 CssmAutoDbRecordAttributeData aRecordBuilder(5); // Set capacity to 5 so we don't need to grow
1600
1601 // Create the entry for the SchemaRelations table.
1602 aRecordBuilder.add(RelationID, inInfo.recordType());
1603 aRecordBuilder.add(RelationName, mDb.recordName(inInfo.recordType()));
1604
1605 // Insert the record into the SchemaRelations ModifiedTable
1606 findTable(mDb.schemaRelations.DataRecordType).insertRecord(mVersionId,
1607 &aRecordBuilder, NULL);
1608
1609 ModifiedTable &anAttributeTable = findTable(mDb.schemaAttributes.DataRecordType);
1610 for (uint32 anIndex = 0; anIndex < inInfo.size(); anIndex++)
1611 {
1612 // Create an entry for the SchemaAttributes table.
1613 aRecordBuilder.clear();
1614 aRecordBuilder.add(RelationID, inInfo.recordType());
1615 aRecordBuilder.add(AttributeNameFormat, inInfo.at(anIndex).nameFormat());
1616
1617 uint32 attributeId = aMetaRecord.metaAttribute(inInfo.at(anIndex)).attributeId();
1618
1619 switch (inInfo.at(anIndex).nameFormat())
1620 {
1621 case CSSM_DB_ATTRIBUTE_NAME_AS_STRING:
1622 aRecordBuilder.add(AttributeName, inInfo.at(anIndex).Label.AttributeName);
1623 break;
1624 case CSSM_DB_ATTRIBUTE_NAME_AS_OID:
1625 aRecordBuilder.add(AttributeNameID, inInfo.at(anIndex).Label.AttributeOID);
1626 break;
1627 case CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER:
1628 break;
1629 default:
1630 CssmError::throwMe(CSSMERR_DL_INVALID_FIELD_NAME);
1631 }
1632
1633 aRecordBuilder.add(AttributeID, attributeId);
1634 aRecordBuilder.add(AttributeFormat, inInfo.at(anIndex).format());
1635
1636 // Insert the record into the SchemaAttributes ModifiedTable
1637 anAttributeTable.insertRecord(mVersionId, &aRecordBuilder, NULL);
1638 }
1639
1640 if (inIndexInfo != NULL) {
1641
1642 if (inIndexInfo->DataRecordType != inInfo.DataRecordType &&
1643 inIndexInfo->NumberOfIndexes > 0)
1644 CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE);
1645
1646 ModifiedTable &indexMetaTable = findTable(mDb.schemaIndexes.DataRecordType);
1647 uint32 aNumberOfIndexes = inIndexInfo->NumberOfIndexes;
1648
1649 for (uint32 anIndex = 0; anIndex < aNumberOfIndexes; anIndex++)
1650 {
1651 const CssmDbIndexInfo &thisIndex = CssmDbIndexInfo::overlay(inIndexInfo->IndexInfo[anIndex]);
1652
1653 // make sure the index is supported
1654 if (thisIndex.dataLocation() != CSSM_DB_INDEX_ON_ATTRIBUTE)
1655 CssmError::throwMe(CSSMERR_DL_INVALID_INDEX_INFO);
1656
1657 // assign an index ID: the unique index is ID 0, all others are ID > 0
1658 uint32 indexId;
1659 if (thisIndex.IndexType == CSSM_DB_INDEX_UNIQUE)
1660 indexId = 0;
1661 else
1662 indexId = anIndex + 1;
1663
1664 // figure out the attribute ID
1665 uint32 attributeId =
1666 aMetaRecord.metaAttribute(thisIndex.Info).attributeId();
1667
1668 // Create an entry for the SchemaIndexes table.
1669 aRecordBuilder.clear();
1670 aRecordBuilder.add(RelationID, inInfo.DataRecordType);
1671 aRecordBuilder.add(IndexID, indexId);
1672 aRecordBuilder.add(AttributeID, attributeId);
1673 aRecordBuilder.add(IndexType, thisIndex.IndexType);
1674 aRecordBuilder.add(IndexedDataLocation, thisIndex.IndexedDataLocation);
1675
1676 // Insert the record into the SchemaIndexes ModifiedTable
1677 indexMetaTable.insertRecord(mVersionId, &aRecordBuilder, NULL);
1678
1679 // update the table's index objects
1680 DbMutableIndex &index = aTable.findIndex(indexId, aMetaRecord, indexId == 0);
1681 index.appendAttribute(attributeId);
1682 }
1683 }
1684 }
1685
1686 // Insert a new table. The attribute info is required; the index and parsing module
1687 // descriptions are optional. This version gets called during the creation of a
1688 // database.
1689
1690 void
1691 DbModifier::insertTable(const CssmDbRecordAttributeInfo &inInfo,
1692 const CSSM_DB_RECORD_INDEX_INFO *inIndexInfo /* = NULL */,
1693 const CSSM_DB_PARSING_MODULE_INFO *inParsingModule /* = NULL */)
1694 {
1695 modifyDatabase();
1696 createTable(new MetaRecord(inInfo));
1697 insertTableSchema(inInfo, inIndexInfo);
1698 }
1699
1700 // Insert a new table. This is the version that gets called when a table is added
1701 // after a database has been created.
1702
1703 void
1704 DbModifier::insertTable(Table::Id inTableId, const string &inTableName,
1705 uint32 inNumberOfAttributes,
1706 const CSSM_DB_SCHEMA_ATTRIBUTE_INFO *inAttributeInfo,
1707 uint32 inNumberOfIndexes,
1708 const CSSM_DB_SCHEMA_INDEX_INFO *inIndexInfo)
1709 {
1710 modifyDatabase();
1711 ModifiedTable *aTable = createTable(new MetaRecord(inTableId, inNumberOfAttributes, inAttributeInfo));
1712
1713 CssmAutoDbRecordAttributeData aRecordBuilder(6); // Set capacity to 6 so we don't need to grow
1714
1715 // Create the entry for the SchemaRelations table.
1716 aRecordBuilder.add(RelationID, inTableId);
1717 aRecordBuilder.add(RelationName, inTableName);
1718
1719 // Insert the record into the SchemaRelations ModifiedTable
1720 findTable(mDb.schemaRelations.DataRecordType).insertRecord(mVersionId,
1721 &aRecordBuilder, NULL);
1722
1723 ModifiedTable &anAttributeTable = findTable(mDb.schemaAttributes.DataRecordType);
1724 for (uint32 anIndex = 0; anIndex < inNumberOfAttributes; anIndex++)
1725 {
1726 // Create an entry for the SchemaAttributes table.
1727 aRecordBuilder.clear();
1728 aRecordBuilder.add(RelationID, inTableId);
1729 // XXX What should this be? We set it to CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER for now
1730 // since the AttributeID is always valid.
1731 aRecordBuilder.add(AttributeNameFormat, uint32(CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER));
1732 aRecordBuilder.add(AttributeID, inAttributeInfo[anIndex].AttributeId);
1733 if (inAttributeInfo[anIndex].AttributeName)
1734 aRecordBuilder.add(AttributeName, inAttributeInfo[anIndex].AttributeName);
1735 if (inAttributeInfo[anIndex].AttributeNameID.Length > 0)
1736 aRecordBuilder.add(AttributeNameID, inAttributeInfo[anIndex].AttributeNameID);
1737 aRecordBuilder.add(AttributeFormat, inAttributeInfo[anIndex].DataType);
1738
1739 // Insert the record into the SchemaAttributes ModifiedTable
1740 anAttributeTable.insertRecord(mVersionId, &aRecordBuilder, NULL);
1741 }
1742
1743 ModifiedTable &anIndexTable = findTable(mDb.schemaIndexes.DataRecordType);
1744 for (uint32 anIndex = 0; anIndex < inNumberOfIndexes; anIndex++)
1745 {
1746 // Create an entry for the SchemaIndexes table.
1747 aRecordBuilder.clear();
1748 aRecordBuilder.add(RelationID, inTableId);
1749 aRecordBuilder.add(IndexID, inIndexInfo[anIndex].IndexId);
1750 aRecordBuilder.add(AttributeID, inIndexInfo[anIndex].AttributeId);
1751 aRecordBuilder.add(IndexType, inIndexInfo[anIndex].IndexType);
1752 aRecordBuilder.add(IndexedDataLocation, inIndexInfo[anIndex].IndexedDataLocation);
1753
1754 // Insert the record into the SchemaIndexes ModifiedTable
1755 anIndexTable.insertRecord(mVersionId, &aRecordBuilder, NULL);
1756
1757 // update the table's index objects
1758 DbMutableIndex &index = aTable->findIndex(inIndexInfo[anIndex].IndexId,
1759 aTable->getMetaRecord(), inIndexInfo[anIndex].IndexType == CSSM_DB_INDEX_UNIQUE);
1760 index.appendAttribute(inIndexInfo[anIndex].AttributeId);
1761 }
1762 }
1763
1764 ModifiedTable &
1765 DbModifier::findTable(Table::Id inTableId)
1766 {
1767 ModifiedTableMap::iterator it = mModifiedTableMap.find(inTableId);
1768 if (it == mModifiedTableMap.end())
1769 CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE);
1770 return *it->second;
1771 }
1772
1773
1774 //
1775 // AppleDatabaseManager implementation
1776 //
1777
1778 AppleDatabaseManager::AppleDatabaseManager(const AppleDatabaseTableName *tableNames)
1779 : DatabaseManager(),
1780 mTableNames(tableNames)
1781 {
1782 // make sure that a proper set of table ids and names has been provided
1783
1784 if (!mTableNames)
1785 CssmError::throwMe(CSSMERR_DL_INTERNAL_ERROR);
1786 else {
1787 uint32 i;
1788 for (i = 0; mTableNames[i].mTableName; i++) {}
1789 if (i < AppleDatabaseTableName::kNumRequiredTableNames)
1790 CssmError::throwMe(CSSMERR_DL_INTERNAL_ERROR);
1791 }
1792 }
1793
1794 Database *
1795 AppleDatabaseManager::make(const DbName &inDbName)
1796 {
1797 return new AppleDatabase(inDbName, mTableNames);
1798 }
1799
1800 //
1801 // AppleDbContext implementation
1802 //
1803 AppleDbContext::AppleDbContext(Database &inDatabase,
1804 DatabaseSession &inDatabaseSession,
1805 CSSM_DB_ACCESS_TYPE inAccessRequest,
1806 const AccessCredentials *inAccessCred,
1807 const void *inOpenParameters) :
1808 DbContext(inDatabase, inDatabaseSession, inAccessRequest, inAccessCred)
1809 {
1810 const CSSM_APPLEDL_OPEN_PARAMETERS *anOpenParameters =
1811 reinterpret_cast<const CSSM_APPLEDL_OPEN_PARAMETERS *>(inOpenParameters);
1812 if (anOpenParameters)
1813 {
1814 if (anOpenParameters->length < sizeof(CSSM_APPLEDL_OPEN_PARAMETERS)
1815 || anOpenParameters->version != 0)
1816 CssmError::throwMe(CSSMERR_APPLEDL_INVALID_OPEN_PARAMETERS);
1817
1818 mAutoCommit = anOpenParameters->autoCommit == CSSM_FALSE ? false : true;
1819 }
1820 else
1821 mAutoCommit = true;
1822 }
1823
1824 AppleDbContext::~AppleDbContext()
1825 {
1826 }
1827
1828 //
1829 // AppleDatabase implementation
1830 //
1831 AppleDatabase::AppleDatabase(const DbName &inDbName, const AppleDatabaseTableName *tableNames) :
1832 Database(inDbName),
1833 schemaRelations(tableNames[AppleDatabaseTableName::kSchemaInfo].mTableId,
1834 sizeof(AttrSchemaRelations) / sizeof(CSSM_DB_ATTRIBUTE_INFO),
1835 const_cast<CSSM_DB_ATTRIBUTE_INFO_PTR>(AttrSchemaRelations)),
1836 schemaAttributes(tableNames[AppleDatabaseTableName::kSchemaAttributes].mTableId,
1837 sizeof(AttrSchemaAttributes) / sizeof(CSSM_DB_ATTRIBUTE_INFO),
1838 const_cast<CSSM_DB_ATTRIBUTE_INFO_PTR>(AttrSchemaAttributes)),
1839 schemaIndexes(tableNames[AppleDatabaseTableName::kSchemaIndexes].mTableId,
1840 sizeof(AttrSchemaIndexes) / sizeof(CSSM_DB_ATTRIBUTE_INFO),
1841 const_cast<CSSM_DB_ATTRIBUTE_INFO_PTR>(AttrSchemaIndexes)),
1842 schemaParsingModule(tableNames[AppleDatabaseTableName::kSchemaParsingModule].mTableId,
1843 sizeof(AttrSchemaParsingModule) / sizeof(CSSM_DB_ATTRIBUTE_INFO),
1844 const_cast<CSSM_DB_ATTRIBUTE_INFO_PTR>(AttrSchemaParsingModule)),
1845 mAtomicFile(mDbName),
1846 mDbModifier(mAtomicFile, *this),
1847 mTableNames(tableNames)
1848 {
1849 }
1850
1851 AppleDatabase::~AppleDatabase()
1852 {
1853 }
1854
1855 // Return the name of a record type. This uses a table that maps record types
1856 // to record names. The table is provided when the database is created.
1857
1858 const char *AppleDatabase::recordName(CSSM_DB_RECORDTYPE inRecordType) const
1859 {
1860 if (inRecordType == CSSM_DL_DB_RECORD_ANY || inRecordType == CSSM_DL_DB_RECORD_ALL_KEYS)
1861 CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE);
1862
1863 for (uint32 i = 0; mTableNames[i].mTableName; i++)
1864 if (mTableNames[i].mTableId == inRecordType)
1865 return mTableNames[i].mTableName;
1866
1867 return "";
1868 }
1869
1870 DbContext *
1871 AppleDatabase::makeDbContext(DatabaseSession &inDatabaseSession,
1872 CSSM_DB_ACCESS_TYPE inAccessRequest,
1873 const AccessCredentials *inAccessCred,
1874 const void *inOpenParameters)
1875 {
1876 return new AppleDbContext(*this, inDatabaseSession, inAccessRequest,
1877 inAccessCred, inOpenParameters);
1878 }
1879
1880 void
1881 AppleDatabase::dbCreate(DbContext &inDbContext, const CSSM_DBINFO &inDBInfo,
1882 const CSSM_ACL_ENTRY_INPUT *inInitialAclEntry)
1883 {
1884 try
1885 {
1886 StLock<Mutex> _(mWriteLock);
1887 mDbModifier.createDatabase(inDBInfo, inInitialAclEntry);
1888 }
1889 catch(...)
1890 {
1891 mDbModifier.rollback();
1892 throw;
1893 }
1894 if (safer_cast<AppleDbContext &>(inDbContext).autoCommit())
1895 mDbModifier.commit();
1896 }
1897
1898 void
1899 AppleDatabase::dbOpen(DbContext &inDbContext)
1900 {
1901 mDbModifier.openDatabase();
1902 }
1903
1904 void
1905 AppleDatabase::dbClose()
1906 {
1907 StLock<Mutex> _(mWriteLock);
1908 mDbModifier.closeDatabase();
1909 }
1910
1911 void
1912 AppleDatabase::dbDelete(DatabaseSession &inDatabaseSession,
1913 const AccessCredentials *inAccessCred)
1914 {
1915 StLock<Mutex> _(mWriteLock);
1916 // XXX Check callers credentials.
1917 mDbModifier.deleteDatabase();
1918 }
1919
1920 void
1921 AppleDatabase::createRelation(DbContext &inDbContext,
1922 CSSM_DB_RECORDTYPE inRelationID,
1923 const char *inRelationName,
1924 uint32 inNumberOfAttributes,
1925 const CSSM_DB_SCHEMA_ATTRIBUTE_INFO &inAttributeInfo,
1926 uint32 inNumberOfIndexes,
1927 const CSSM_DB_SCHEMA_INDEX_INFO &inIndexInfo)
1928 {
1929 try
1930 {
1931 StLock<Mutex> _(mWriteLock);
1932 // XXX Fix the refs here.
1933 mDbModifier.insertTable(inRelationID, inRelationName,
1934 inNumberOfAttributes, &inAttributeInfo,
1935 inNumberOfIndexes, &inIndexInfo);
1936 }
1937 catch(...)
1938 {
1939 mDbModifier.rollback();
1940 throw;
1941 }
1942 if (safer_cast<AppleDbContext &>(inDbContext).autoCommit())
1943 mDbModifier.commit();
1944 }
1945
1946 void
1947 AppleDatabase::destroyRelation(DbContext &inDbContext,
1948 CSSM_DB_RECORDTYPE inRelationID)
1949 {
1950 try
1951 {
1952 StLock<Mutex> _(mWriteLock);
1953 mDbModifier.deleteTable(inRelationID);
1954 }
1955 catch(...)
1956 {
1957 mDbModifier.rollback();
1958 throw;
1959 }
1960 if (safer_cast<AppleDbContext &>(inDbContext).autoCommit())
1961 mDbModifier.commit();
1962 }
1963
1964 void
1965 AppleDatabase::authenticate(DbContext &inDbContext,
1966 CSSM_DB_ACCESS_TYPE inAccessRequest,
1967 const AccessCredentials &inAccessCred)
1968 {
1969 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED);
1970 }
1971
1972 void
1973 AppleDatabase::getDbAcl(DbContext &inDbContext,
1974 const CSSM_STRING *inSelectionTag,
1975 uint32 &outNumberOfAclInfos,
1976 CSSM_ACL_ENTRY_INFO_PTR &outAclInfos)
1977 {
1978 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED);
1979 }
1980
1981 void
1982 AppleDatabase::changeDbAcl(DbContext &inDbContext,
1983 const AccessCredentials &inAccessCred,
1984 const CSSM_ACL_EDIT &inAclEdit)
1985 {
1986 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED);
1987 }
1988
1989 void
1990 AppleDatabase::getDbOwner(DbContext &inDbContext,
1991 CSSM_ACL_OWNER_PROTOTYPE &outOwner)
1992 {
1993 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED);
1994 }
1995
1996 void
1997 AppleDatabase::changeDbOwner(DbContext &inDbContext,
1998 const AccessCredentials &inAccessCred,
1999 const CSSM_ACL_OWNER_PROTOTYPE &inNewOwner)
2000 {
2001 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED);
2002 }
2003
2004 char *
2005 AppleDatabase::getDbNameFromHandle(const DbContext &inDbContext) const
2006 {
2007 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED);
2008 }
2009
2010 CSSM_DB_UNIQUE_RECORD_PTR
2011 AppleDatabase::dataInsert(DbContext &inDbContext,
2012 CSSM_DB_RECORDTYPE inRecordType,
2013 const CSSM_DB_RECORD_ATTRIBUTE_DATA *inAttributes,
2014 const CssmData *inData)
2015 {
2016 CSSM_DB_UNIQUE_RECORD_PTR anUniqueRecordPtr = NULL;
2017 try
2018 {
2019 StLock<Mutex> _(mWriteLock);
2020 const RecordId aRecordId =
2021 mDbModifier.insertRecord(inRecordType, inAttributes, inData);
2022
2023 anUniqueRecordPtr = createUniqueRecord(inDbContext, inRecordType,
2024 aRecordId);
2025 if (safer_cast<AppleDbContext &>(inDbContext).autoCommit())
2026 mDbModifier.commit();
2027 }
2028 catch(...)
2029 {
2030 if (anUniqueRecordPtr != NULL)
2031 freeUniqueRecord(inDbContext, *anUniqueRecordPtr);
2032
2033 mDbModifier.rollback();
2034 throw;
2035 }
2036
2037 return anUniqueRecordPtr;
2038 }
2039
2040 void
2041 AppleDatabase::dataDelete(DbContext &inDbContext,
2042 const CSSM_DB_UNIQUE_RECORD &inUniqueRecord)
2043 {
2044 try
2045 {
2046 StLock<Mutex> _(mWriteLock);
2047 Table::Id aTableId;
2048 const RecordId aRecordId(parseUniqueRecord(inUniqueRecord, aTableId));
2049 mDbModifier.deleteRecord(aTableId, aRecordId);
2050 }
2051 catch(...)
2052 {
2053 mDbModifier.rollback();
2054 throw;
2055 }
2056
2057 if (safer_cast<AppleDbContext &>(inDbContext).autoCommit())
2058 mDbModifier.commit();
2059 }
2060
2061 void
2062 AppleDatabase::dataModify(DbContext &inDbContext,
2063 CSSM_DB_RECORDTYPE inRecordType,
2064 CSSM_DB_UNIQUE_RECORD &inoutUniqueRecord,
2065 const CSSM_DB_RECORD_ATTRIBUTE_DATA *inAttributesToBeModified,
2066 const CssmData *inDataToBeModified,
2067 CSSM_DB_MODIFY_MODE inModifyMode)
2068 {
2069 try
2070 {
2071 StLock<Mutex> _(mWriteLock);
2072 Table::Id aTableId;
2073 const RecordId aRecordId =
2074 mDbModifier.updateRecord(aTableId,
2075 parseUniqueRecord(inoutUniqueRecord, aTableId),
2076 inAttributesToBeModified,
2077 inDataToBeModified,
2078 inModifyMode);
2079 updateUniqueRecord(inDbContext, inRecordType, aRecordId, inoutUniqueRecord);
2080 }
2081 catch(...)
2082 {
2083 mDbModifier.rollback();
2084 throw;
2085 }
2086
2087 if (safer_cast<AppleDbContext &>(inDbContext).autoCommit())
2088 mDbModifier.commit();
2089 }
2090
2091 CSSM_HANDLE
2092 AppleDatabase::dataGetFirst(DbContext &inDbContext,
2093 const DLQuery *inQuery,
2094 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes,
2095 CssmData *inoutData,
2096 CSSM_DB_UNIQUE_RECORD_PTR &outUniqueRecord)
2097 {
2098 // XXX: register Cursor with DbContext and have DbContext call
2099 // dataAbortQuery for all outstanding Query objects on close.
2100 auto_ptr<Cursor> aCursor(mDbModifier.createCursor(inQuery));
2101 Table::Id aTableId;
2102 RecordId aRecordId;
2103
2104 if (!aCursor->next(aTableId, inoutAttributes, inoutData,
2105 inDbContext.mDatabaseSession, aRecordId))
2106 // return a NULL handle, and implicitly delete the cursor
2107 return CSSM_INVALID_HANDLE;
2108
2109 outUniqueRecord = createUniqueRecord(inDbContext, aTableId, aRecordId);
2110 return aCursor.release()->handle(); // We didn't throw so keep the Cursor around.
2111 }
2112
2113 bool
2114 AppleDatabase::dataGetNext(DbContext &inDbContext,
2115 CSSM_HANDLE inResultsHandle,
2116 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes,
2117 CssmData *inoutData,
2118 CSSM_DB_UNIQUE_RECORD_PTR &outUniqueRecord)
2119 {
2120 auto_ptr<Cursor> aCursor(&findHandle<Cursor>(inResultsHandle, CSSMERR_DL_INVALID_RESULTS_HANDLE));
2121 Table::Id aTableId;
2122 RecordId aRecordId;
2123
2124 if (!aCursor->next(aTableId, inoutAttributes, inoutData, inDbContext.mDatabaseSession, aRecordId))
2125 return false;
2126
2127 outUniqueRecord = createUniqueRecord(inDbContext, aTableId, aRecordId);
2128
2129 aCursor.release();
2130 return true;
2131 }
2132
2133 void
2134 AppleDatabase::dataAbortQuery(DbContext &inDbContext,
2135 CSSM_HANDLE inResultsHandle)
2136 {
2137 delete &findHandle<Cursor>(inResultsHandle, CSSMERR_DL_INVALID_RESULTS_HANDLE);
2138 }
2139
2140 void
2141 AppleDatabase::dataGetFromUniqueRecordId(DbContext &inDbContext,
2142 const CSSM_DB_UNIQUE_RECORD &inUniqueRecord,
2143 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes,
2144 CssmData *inoutData)
2145 {
2146 Table::Id aTableId;
2147 const RecordId aRecordId(parseUniqueRecord(inUniqueRecord, aTableId));
2148 // XXX Change CDSA spec to use new RecordId returned by this function
2149 mDbModifier.getRecord(aTableId, aRecordId, inoutAttributes, inoutData,
2150 inDbContext.mDatabaseSession);
2151 }
2152
2153 void
2154 AppleDatabase::freeUniqueRecord(DbContext &inDbContext,
2155 CSSM_DB_UNIQUE_RECORD &inUniqueRecord)
2156 {
2157 if (inUniqueRecord.RecordIdentifier.Length != 0
2158 && inUniqueRecord.RecordIdentifier.Data != NULL)
2159 {
2160 inUniqueRecord.RecordIdentifier.Length = 0;
2161 inDbContext.mDatabaseSession.free(inUniqueRecord.RecordIdentifier.Data);
2162 }
2163 inDbContext.mDatabaseSession.free(&inUniqueRecord);
2164 }
2165
2166 void
2167 AppleDatabase::updateUniqueRecord(DbContext &inDbContext,
2168 CSSM_DB_RECORDTYPE inTableId,
2169 const RecordId &inRecordId,
2170 CSSM_DB_UNIQUE_RECORD &inoutUniqueRecord)
2171 {
2172 uint32 *aBuffer = reinterpret_cast<uint32 *>(inoutUniqueRecord.RecordIdentifier.Data);
2173 aBuffer[0] = inTableId;
2174 aBuffer[1] = inRecordId.mRecordNumber;
2175 aBuffer[2] = inRecordId.mCreateVersion;
2176 aBuffer[3] = inRecordId.mRecordVersion;
2177 }
2178
2179 CSSM_DB_UNIQUE_RECORD_PTR
2180 AppleDatabase::createUniqueRecord(DbContext &inDbContext,
2181 CSSM_DB_RECORDTYPE inTableId,
2182 const RecordId &inRecordId)
2183 {
2184 CSSM_DB_UNIQUE_RECORD_PTR aUniqueRecord =
2185 inDbContext.mDatabaseSession.alloc<CSSM_DB_UNIQUE_RECORD>();
2186 memset(aUniqueRecord, 0, sizeof(*aUniqueRecord));
2187 aUniqueRecord->RecordIdentifier.Length = sizeof(uint32) * 4;
2188 try
2189 {
2190 aUniqueRecord->RecordIdentifier.Data =
2191 inDbContext.mDatabaseSession.alloc<uint8>(sizeof(uint32) * 4);
2192 updateUniqueRecord(inDbContext, inTableId, inRecordId, *aUniqueRecord);
2193 }
2194 catch(...)
2195 {
2196 inDbContext.mDatabaseSession.free(aUniqueRecord);
2197 throw;
2198 }
2199
2200 return aUniqueRecord;
2201 }
2202
2203 const RecordId
2204 AppleDatabase::parseUniqueRecord(const CSSM_DB_UNIQUE_RECORD &inUniqueRecord,
2205 CSSM_DB_RECORDTYPE &outTableId)
2206 {
2207 if (inUniqueRecord.RecordIdentifier.Length != sizeof(uint32) * 4)
2208 CssmError::throwMe(CSSMERR_DL_INVALID_RECORD_UID);
2209
2210 uint32 *aBuffer = reinterpret_cast<uint32 *>(inUniqueRecord.RecordIdentifier.Data);
2211 outTableId = aBuffer[0];
2212 return RecordId(aBuffer[1], aBuffer[2], aBuffer[3]);
2213 }
2214
2215 void
2216 AppleDatabase::passThrough(DbContext &dbContext,
2217 uint32 passThroughId,
2218 const void *inputParams,
2219 void **outputParams)
2220 {
2221 switch (passThroughId)
2222 {
2223 case CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT:
2224 {
2225 CSSM_BOOL on = reinterpret_cast<CSSM_BOOL>(inputParams);
2226 safer_cast<AppleDbContext &>(dbContext).autoCommit(on);
2227 }
2228 break;
2229
2230 case CSSM_APPLEFILEDL_COMMIT:
2231 mDbModifier.commit();
2232 break;
2233
2234 case CSSM_APPLEFILEDL_ROLLBACK:
2235 mDbModifier.rollback();
2236 break;
2237
2238 default:
2239 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED);
2240 break;
2241 }
2242 }
2243
2244