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