]> git.saurik.com Git - apple/security.git/blob - cdsa/cdsa_utilities/AppleDatabase.cpp
Security-163.tar.gz
[apple/security.git] / cdsa / cdsa_utilities / AppleDatabase.cpp
1 /*
2 * Copyright (c) 2000-2001, 2003 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(uint32 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(AtomicTempFile &inAtomicTempFile, 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 inAtomicTempFile.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 inAtomicTempFile.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 inAtomicTempFile.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 inAtomicTempFile.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 inAtomicTempFile.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 inAtomicTempFile.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(const AppleDatabase &db, const RefPointer <AtomicBufferedFile> &inAtomicBufferedFile) :
743 mDatabase(reinterpret_cast<const uint8 *>(NULL), 0),
744 mDb(db),
745 mBufferedFile(inAtomicBufferedFile)
746 {
747 off_t aLength = mBufferedFile->length();
748 off_t bytesRead = 0;
749 const uint8 *ptr = mBufferedFile->read(0, aLength, bytesRead);
750 mBufferedFile->close();
751 mDatabase = ReadSection(ptr, bytesRead);
752 open();
753 }
754
755 DbVersion::~DbVersion()
756 {
757 try
758 {
759 for_each_map_delete(mTableMap.begin(), mTableMap.end());
760 }
761 catch(...) {}
762 }
763
764 void
765 DbVersion::open()
766 {
767 try
768 {
769 // This is the oposite of DbModifier::commit()
770 mVersionId = mDatabase[mDatabase.size() - AtomSize];
771
772 const ReadSection aHeaderSection = mDatabase.subsection(HeaderOffset,
773 HeaderSize);
774 if (aHeaderSection.at(OffsetMagic) != HeaderMagic)
775 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
776
777 // We currently only support one version. If we support additional
778 // file format versions in the future fix this.
779 uint32 aVersion = aHeaderSection.at(OffsetVersion);
780 if (aVersion != HeaderVersion)
781 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
782
783 //const ReadSection anAuthSection =
784 // mDatabase.subsection(HeaderOffset + aHeaderSection.at(OffsetAuthOffset));
785 // XXX Do something with anAuthSection.
786
787 uint32 aSchemaOffset = aHeaderSection.at(OffsetSchemaOffset);
788 const ReadSection aSchemaSection =
789 mDatabase.subsection(HeaderOffset + aSchemaOffset);
790
791 uint32 aSchemaSize = aSchemaSection[OffsetSchemaSize];
792 // Make sure that the given range exists.
793 aSchemaSection.subsection(0, aSchemaSize);
794 uint32 aTableCount = aSchemaSection[OffsetTablesCount];
795
796 // Assert that the size of this section is big enough.
797 if (aSchemaSize < OffsetTables + AtomSize * aTableCount)
798 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
799
800 for (uint32 aTableNumber = 0; aTableNumber < aTableCount;
801 aTableNumber++)
802 {
803 uint32 aTableOffset = aSchemaSection.at(OffsetTables + AtomSize
804 * aTableNumber);
805 // XXX Set the size boundary on aTableSection.
806 const ReadSection aTableSection =
807 aSchemaSection.subsection(aTableOffset);
808 auto_ptr<Table> aTable(new Table(aTableSection));
809 Table::Id aTableId = aTable->getMetaRecord().dataRecordType();
810 mTableMap.insert(TableMap::value_type(aTableId, aTable.get()));
811 aTable.release();
812 }
813
814 // Fill in the schema for the meta tables.
815
816 findTable(mDb.schemaRelations.DataRecordType).getMetaRecord().
817 setRecordAttributeInfo(mDb.schemaRelations);
818 findTable(mDb.schemaIndexes.DataRecordType).getMetaRecord().
819 setRecordAttributeInfo(mDb.schemaIndexes);
820 findTable(mDb.schemaParsingModule.DataRecordType).getMetaRecord().
821 setRecordAttributeInfo(mDb.schemaParsingModule);
822
823 // OK, we have created all the tables in the tableMap. Now
824 // lets read the schema and proccess it accordingly.
825 // Iterate over all schema records.
826 Table &aTable = findTable(mDb.schemaAttributes.DataRecordType);
827 aTable.getMetaRecord().setRecordAttributeInfo(mDb.schemaAttributes);
828 uint32 aRecordsCount = aTable.getRecordsCount();
829 ReadSection aRecordsSection = aTable.getRecordsSection();
830 uint32 aReadOffset = 0;
831 const MetaRecord &aMetaRecord = aTable.getMetaRecord();
832
833 CSSM_DB_ATTRIBUTE_DATA aRelationIDData =
834 {
835 RelationID,
836 0,
837 NULL
838 };
839 CSSM_DB_ATTRIBUTE_DATA aAttributeIDData =
840 {
841 AttributeID,
842 0,
843 NULL
844 };
845 CSSM_DB_ATTRIBUTE_DATA aAttributeNameFormatData =
846 {
847 AttributeNameFormat,
848 0,
849 NULL
850 };
851 CSSM_DB_ATTRIBUTE_DATA aAttributeNameData =
852 {
853 AttributeName,
854 0,
855 NULL
856 };
857 CSSM_DB_ATTRIBUTE_DATA aAttributeNameIDData =
858 {
859 AttributeNameID,
860 0,
861 NULL
862 };
863 CSSM_DB_ATTRIBUTE_DATA aAttributeFormatData =
864 {
865 AttributeFormat,
866 0,
867 NULL
868 };
869 CSSM_DB_ATTRIBUTE_DATA aRecordAttributes[] =
870 {
871 aRelationIDData,
872 aAttributeIDData,
873 aAttributeNameFormatData,
874 aAttributeNameData,
875 aAttributeNameIDData,
876 aAttributeFormatData
877 };
878 CSSM_DB_RECORD_ATTRIBUTE_DATA aRecordAttributeData =
879 {
880 aMetaRecord.dataRecordType(),
881 0,
882 sizeof(aRecordAttributes) / sizeof(CSSM_DB_ATTRIBUTE_DATA),
883 aRecordAttributes
884 };
885 CssmDbRecordAttributeData &aRecordData = CssmDbRecordAttributeData::overlay(aRecordAttributeData);
886
887 TrackingAllocator recordAllocator(CssmAllocator::standard());
888 for (uint32 aRecord = 0; aRecord != aRecordsCount; aRecord++)
889 {
890 ReadSection aRecordSection = MetaRecord::readSection(aRecordsSection, aReadOffset);
891 uint32 aRecordSize = aRecordSection.size();
892 aReadOffset += aRecordSize;
893 aMetaRecord.unpackRecord(aRecordSection, recordAllocator,
894 &aRecordAttributeData, NULL, 0);
895 // Create the attribute coresponding to this entry
896 if (aRecordData[0].size() != 1 || aRecordData[0].format() != CSSM_DB_ATTRIBUTE_FORMAT_UINT32)
897 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
898 uint32 aRelationId = aRecordData[0];
899
900 // Skip the schema relations for the meta tables themselves.
901 // FIXME: this hard-wires the meta-table relation IDs to be
902 // within {CSSM_DB_RECORDTYPE_SCHEMA_START...
903 // CSSM_DB_RECORDTYPE_SCHEMA_END} (which is {0..4}).
904 // Bogus - the MDS schema relation IDs start at
905 // CSSM_DB_RELATIONID_MDS_START which is 0x40000000.
906 // Ref. Radar 2817921.
907 if (CSSM_DB_RECORDTYPE_SCHEMA_START <= aRelationId && aRelationId < CSSM_DB_RECORDTYPE_SCHEMA_END)
908 continue;
909
910 // Get the MetaRecord corresponding to the specified RelationId
911 MetaRecord &aMetaRecord = findTable(aRelationId).getMetaRecord();
912
913 if (aRecordData[1].size() != 1
914 || aRecordData[1].format() != CSSM_DB_ATTRIBUTE_FORMAT_UINT32
915 || aRecordData[2].size() != 1
916 || aRecordData[2].format() != CSSM_DB_ATTRIBUTE_FORMAT_UINT32
917 || aRecordData[5].size() != 1
918 || aRecordData[5].format() != CSSM_DB_ATTRIBUTE_FORMAT_UINT32)
919 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
920
921 uint32 anAttributeId = aRecordData[1];
922 uint32 anAttributeNameFormat = aRecordData[2];
923 uint32 anAttributeFormat = aRecordData[5];
924 auto_ptr<string> aName;
925 const CssmData *aNameID = NULL;
926
927 if (aRecordData[3].size() == 1)
928 {
929 if (aRecordData[3].format() != CSSM_DB_ATTRIBUTE_FORMAT_STRING)
930 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
931
932 auto_ptr<string> aName2(new string(static_cast<string>(aRecordData[3])));
933 aName = aName2;
934 }
935
936 if (aRecordData[4].size() == 1)
937 {
938 if (aRecordData[4].format() != CSSM_DB_ATTRIBUTE_FORMAT_BLOB)
939 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
940
941 // @@@ Invoking conversion operator to CssmData & on aRecordData[4]
942 // And taking address of result.
943 aNameID = &static_cast<CssmData &>(aRecordData[4]);
944 }
945
946 // Make sure that the attribute specified by anAttributeNameFormat is present.
947 switch (anAttributeNameFormat)
948 {
949 case CSSM_DB_ATTRIBUTE_NAME_AS_STRING:
950 if (aRecordData[3].size() != 1)
951 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
952 break;
953 case CSSM_DB_ATTRIBUTE_NAME_AS_OID:
954 if (aRecordData[4].size() != 1)
955 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
956 break;
957 case CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER:
958 break;
959 default:
960 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
961 }
962
963 // Create the attribute
964 aMetaRecord.createAttribute(aName.get(), aNameID, anAttributeId, anAttributeFormat);
965 }
966
967 // initialize the indexes associated with each table
968 {
969 TableMap::iterator it;
970 for (it = mTableMap.begin(); it != mTableMap.end(); it++)
971 it->second->readIndexSection();
972 }
973 }
974 catch(...)
975 {
976 for_each_map_delete(mTableMap.begin(), mTableMap.end());
977 mTableMap.clear();
978 throw;
979 }
980 }
981
982 const RecordId
983 DbVersion::getRecord(Table::Id inTableId, const RecordId &inRecordId,
984 CSSM_DB_RECORD_ATTRIBUTE_DATA *inoutAttributes,
985 CssmData *inoutData,
986 CssmAllocator &inAllocator) const
987 {
988 return findTable(inTableId).getRecord(inRecordId, inoutAttributes,
989 inoutData, inAllocator);
990 }
991
992 Cursor *
993 DbVersion::createCursor(const CSSM_QUERY *inQuery) const
994 {
995 // XXX We should add support for these special query types
996 // By Creating a Cursor that iterates over multiple tables
997 if (!inQuery || inQuery->RecordType == CSSM_DL_DB_RECORD_ANY
998 || inQuery->RecordType == CSSM_DL_DB_RECORD_ALL_KEYS)
999 {
1000 return new MultiCursor(inQuery, *this);
1001 }
1002
1003 return findTable(inQuery->RecordType).createCursor(inQuery, *this);
1004 }
1005
1006 const Table &
1007 DbVersion::findTable(Table::Id inTableId) const
1008 {
1009 TableMap::const_iterator it = mTableMap.find(inTableId);
1010 if (it == mTableMap.end())
1011 CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE);
1012 return *it->second;
1013 }
1014
1015 Table &
1016 DbVersion::findTable(Table::Id inTableId)
1017 {
1018 TableMap::iterator it = mTableMap.find(inTableId);
1019 if (it == mTableMap.end())
1020 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
1021 return *it->second;
1022 }
1023
1024 //
1025 // Cursor implemetation
1026 //
1027 Cursor::~Cursor()
1028 {
1029 }
1030
1031
1032 //
1033 // LinearCursor implemetation
1034 //
1035 LinearCursor::LinearCursor(const CSSM_QUERY *inQuery, const DbVersion &inDbVersion,
1036 const Table &inTable) :
1037 mDbVersion(&inDbVersion),
1038 mRecordsCount(inTable.getRecordsCount()),
1039 mRecord(0),
1040 mRecordsSection(inTable.getRecordsSection()),
1041 mReadOffset(0),
1042 mMetaRecord(inTable.getMetaRecord())
1043 {
1044 if (inQuery)
1045 {
1046 mConjunctive = inQuery->Conjunctive;
1047 mQueryFlags = inQuery->QueryFlags;
1048 // XXX Do something with inQuery->QueryLimits?
1049 uint32 aPredicatesCount = inQuery->NumSelectionPredicates;
1050 mPredicates.resize(aPredicatesCount);
1051 try
1052 {
1053 for (uint32 anIndex = 0; anIndex < aPredicatesCount; anIndex++)
1054 {
1055 CSSM_SELECTION_PREDICATE &aPredicate = inQuery->SelectionPredicate[anIndex];
1056 mPredicates[anIndex] = new SelectionPredicate(mMetaRecord, aPredicate);
1057 }
1058 }
1059 catch(...)
1060 {
1061 for_each_delete(mPredicates.begin(), mPredicates.end());
1062 throw;
1063 }
1064 }
1065 }
1066
1067 LinearCursor::~LinearCursor()
1068 {
1069 for_each_delete(mPredicates.begin(), mPredicates.end());
1070 }
1071
1072 bool
1073 LinearCursor::next(Table::Id &outTableId,
1074 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes,
1075 CssmData *inoutData, CssmAllocator &inAllocator, RecordId &recordId)
1076 {
1077 while (mRecord++ < mRecordsCount)
1078 {
1079 ReadSection aRecordSection = MetaRecord::readSection(mRecordsSection, mReadOffset);
1080 uint32 aRecordSize = aRecordSection.size();
1081 mReadOffset += aRecordSize;
1082
1083 PredicateVector::const_iterator anIt = mPredicates.begin();
1084 PredicateVector::const_iterator anEnd = mPredicates.end();
1085 bool aMatch;
1086 if (anIt == anEnd)
1087 {
1088 // If there are no predicates we have a match.
1089 aMatch = true;
1090 }
1091 else if (mConjunctive == CSSM_DB_OR)
1092 {
1093 // If mConjunctive is OR, the first predicate that returns
1094 // true indicates a match. Dropthough means no match
1095 aMatch = false;
1096 for (; anIt != anEnd; anIt++)
1097 {
1098 if ((*anIt)->evaluate(aRecordSection))
1099 {
1100 aMatch = true;
1101 break;
1102 }
1103 }
1104 }
1105 else if (mConjunctive == CSSM_DB_AND || mConjunctive == CSSM_DB_NONE)
1106 {
1107 // If mConjunctive is AND (or NONE), the first predicate that returns
1108 // false indicates a mismatch. Dropthough means a match
1109 aMatch = true;
1110 for (; anIt != anEnd; anIt++)
1111 {
1112 if (!(*anIt)->evaluate(aRecordSection))
1113 {
1114 aMatch = false;
1115 break;
1116 }
1117 }
1118 }
1119 else
1120 {
1121 // XXX Should be CSSMERR_DL_INVALID_QUERY (or CSSMERR_DL_INVALID_CONJUNTIVE).
1122 CssmError::throwMe(CSSMERR_DL_UNSUPPORTED_QUERY);
1123 }
1124
1125 if (aMatch)
1126 {
1127 // Get the actual record.
1128 mMetaRecord.unpackRecord(aRecordSection, inAllocator,
1129 inoutAttributes, inoutData,
1130 mQueryFlags);
1131 outTableId = mMetaRecord.dataRecordType();
1132 recordId = MetaRecord::unpackRecordId(aRecordSection);
1133 return true;
1134 }
1135 }
1136
1137 return false;
1138 }
1139
1140 //
1141 // IndexCursor
1142 //
1143
1144 IndexCursor::IndexCursor(DbQueryKey *queryKey, const DbVersion &inDbVersion,
1145 const Table &table, const DbConstIndex *index)
1146 : mQueryKey(queryKey), mDbVersion(inDbVersion), mTable(table), mIndex(index)
1147 {
1148 index->performQuery(*queryKey, mBegin, mEnd);
1149 }
1150
1151 IndexCursor::~IndexCursor()
1152 {
1153 // the query key will be deleted automatically, since it's an auto_ptr
1154 }
1155
1156 bool
1157 IndexCursor::next(Table::Id &outTableId,
1158 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR outAttributes,
1159 CssmData *outData,
1160 CssmAllocator &inAllocator, RecordId &recordId)
1161 {
1162 if (mBegin == mEnd)
1163 return false;
1164
1165 ReadSection rs = mIndex->getRecordSection(mBegin++);
1166 const MetaRecord &metaRecord = mTable.getMetaRecord();
1167
1168 outTableId = metaRecord.dataRecordType();
1169 metaRecord.unpackRecord(rs, inAllocator, outAttributes, outData, 0);
1170
1171 recordId = MetaRecord::unpackRecordId(rs);
1172 return true;
1173 }
1174
1175 //
1176 // MultiCursor
1177 //
1178 MultiCursor::MultiCursor(const CSSM_QUERY *inQuery, const DbVersion &inDbVersion) :
1179 mDbVersion(&inDbVersion), mTableIterator(inDbVersion.begin())
1180 {
1181 if (inQuery)
1182 mQuery.reset(new CssmAutoQuery(*inQuery));
1183 else
1184 {
1185 mQuery.reset(new CssmAutoQuery());
1186 mQuery->recordType(CSSM_DL_DB_RECORD_ANY);
1187 }
1188 }
1189
1190 MultiCursor::~MultiCursor()
1191 {
1192 }
1193
1194 bool
1195 MultiCursor::next(Table::Id &outTableId,
1196 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes,
1197 CssmData *inoutData, CssmAllocator &inAllocator, RecordId &recordId)
1198 {
1199 for (;;)
1200 {
1201 if (!mCursor.get())
1202 {
1203 if (mTableIterator == mDbVersion->end())
1204 return false;
1205
1206 const Table &aTable = *mTableIterator++;
1207 if (!aTable.matchesTableId(mQuery->recordType()))
1208 continue;
1209
1210 mCursor.reset(aTable.createCursor(mQuery.get(), *mDbVersion));
1211 }
1212
1213 if (mCursor->next(outTableId, inoutAttributes, inoutData, inAllocator, recordId))
1214 return true;
1215
1216 mCursor.reset(NULL);
1217 }
1218 }
1219
1220
1221 //
1222 // DbModifier
1223 //
1224 DbModifier::DbModifier(AtomicFile &inAtomicFile, const AppleDatabase &db) :
1225 Metadata(),
1226 mDbVersion(),
1227 mAtomicFile(inAtomicFile),
1228 mDb(db)
1229 {
1230 }
1231
1232 DbModifier::~DbModifier()
1233 {
1234 try
1235 {
1236 for_each_map_delete(mModifiedTableMap.begin(), mModifiedTableMap.end());
1237 // mAtomicTempFile will do automatic rollback on destruction.
1238 }
1239 catch(...) {}
1240 }
1241
1242 const RefPointer<const DbVersion>
1243 DbModifier::getDbVersion()
1244 {
1245 StLock<Mutex> _(mDbVersionLock);
1246 RefPointer <AtomicBufferedFile> atomicBufferedFile(mAtomicFile.read());
1247 off_t length = atomicBufferedFile->open();
1248 if (mDbVersion)
1249 {
1250 if (length < AtomSize)
1251 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
1252
1253 off_t bytesRead = 0;
1254 const uint8 *ptr = atomicBufferedFile->read(length - AtomSize, AtomSize, bytesRead);
1255 ReadSection aVersionSection(ptr, bytesRead);
1256 uint32 aVersionId = aVersionSection[0];
1257
1258 if (aVersionId == mDbVersion->getVersionId())
1259 return mDbVersion;
1260 }
1261
1262 mDbVersion = new DbVersion(mDb, atomicBufferedFile);
1263
1264 return mDbVersion;
1265 }
1266
1267 void
1268 DbModifier::createDatabase(const CSSM_DBINFO &inDbInfo,
1269 const CSSM_ACL_ENTRY_INPUT *inInitialAclEntry,
1270 mode_t mode)
1271 {
1272 // XXX This needs better locking. There is a possible race condition between
1273 // two concurrent creators. Or a writer/creator or a close/create etc.
1274 if (mAtomicTempFile || !mModifiedTableMap.empty())
1275 CssmError::throwMe(CSSMERR_DL_DATASTORE_ALREADY_EXISTS);
1276
1277 mAtomicTempFile = mAtomicFile.create(mode);
1278 // Set mVersionId to one since this is the first version of the database.
1279 mVersionId = 1;
1280
1281 // we need to create the meta tables first, because inserting tables
1282 // (including the meta tables themselves) relies on them being there
1283 createTable(new MetaRecord(mDb.schemaRelations));
1284 createTable(new MetaRecord(mDb.schemaAttributes));
1285 createTable(new MetaRecord(mDb.schemaIndexes));
1286 createTable(new MetaRecord(mDb.schemaParsingModule));
1287
1288 // now add the meta-tables' schema to the meta tables themselves
1289 insertTableSchema(mDb.schemaRelations);
1290 insertTableSchema(mDb.schemaAttributes);
1291 insertTableSchema(mDb.schemaIndexes);
1292 insertTableSchema(mDb.schemaParsingModule);
1293
1294 if (inInitialAclEntry != NULL)
1295 {
1296 //createACL(*inInitialAclEntry);
1297 }
1298
1299 if (inDbInfo.NumberOfRecordTypes == 0)
1300 return;
1301 if (inDbInfo.RecordAttributeNames == NULL)
1302 CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE);
1303 if (inDbInfo.RecordIndexes == NULL)
1304 CssmError::throwMe(CSSMERR_DL_INVALID_RECORD_INDEX);
1305 if (inDbInfo.DefaultParsingModules == NULL)
1306 CssmError::throwMe(CSSMERR_DL_INVALID_PARSING_MODULE);
1307
1308 for (uint32 anIndex = 0; anIndex < inDbInfo.NumberOfRecordTypes; anIndex++)
1309 {
1310 insertTable(CssmDbRecordAttributeInfo::overlay(inDbInfo.RecordAttributeNames[anIndex]),
1311 &inDbInfo.RecordIndexes[anIndex],
1312 &inDbInfo.DefaultParsingModules[anIndex]);
1313 }
1314 }
1315
1316 void DbModifier::openDatabase()
1317 {
1318 commit(); // XXX Requires write lock.
1319 getDbVersion();
1320 }
1321
1322 void DbModifier::closeDatabase()
1323 {
1324 commit(); // XXX Requires write lock.
1325 StLock<Mutex> _(mDbVersionLock);
1326 mDbVersion = NULL;
1327 }
1328
1329 void DbModifier::deleteDatabase()
1330 {
1331 rollback(); // XXX Requires write lock. Also if autoCommit was disabled
1332 // this will incorrectly cause the performDelete to throw CSSMERR_DB_DOES_NOT_EXIST.
1333 StLock<Mutex> _(mDbVersionLock);
1334
1335 // Clean up mModifiedTableMap in case this object gets reused again for
1336 // a new create.
1337 for_each_map_delete(mModifiedTableMap.begin(), mModifiedTableMap.end());
1338 mModifiedTableMap.clear();
1339
1340 mDbVersion = NULL;
1341 mAtomicFile.performDelete();
1342 }
1343
1344 void
1345 DbModifier::modifyDatabase()
1346 {
1347 if (mAtomicTempFile)
1348 return;
1349
1350 try
1351 {
1352 mAtomicTempFile = mAtomicFile.write();
1353 // Now we are holding the write lock make sure we get the latest greatest version of the db.
1354 // Also set mVersionId to one more that that of the old database.
1355 mVersionId = getDbVersion()->getVersionId() + 1;
1356
1357 // Never make a database with mVersionId 0 since it makes bad things happen to Jaguar and older systems
1358 if (mVersionId == 0)
1359 mVersionId = 1;
1360
1361 // Remove all old modified tables
1362 for_each_map_delete(mModifiedTableMap.begin(), mModifiedTableMap.end());
1363 mModifiedTableMap.clear();
1364
1365 // Setup the new tables
1366 DbVersion::TableMap::const_iterator anIt =
1367 mDbVersion->mTableMap.begin();
1368 DbVersion::TableMap::const_iterator anEnd =
1369 mDbVersion->mTableMap.end();
1370 for (; anIt != anEnd; ++anIt)
1371 {
1372 auto_ptr<ModifiedTable> aTable(new ModifiedTable(anIt->second));
1373 mModifiedTableMap.insert(ModifiedTableMap::value_type(anIt->first,
1374 aTable.get()));
1375 aTable.release();
1376 }
1377 }
1378 catch(...)
1379 {
1380 for_each_map_delete(mModifiedTableMap.begin(), mModifiedTableMap.end());
1381 mModifiedTableMap.clear();
1382 rollback();
1383 throw;
1384 }
1385 }
1386
1387 void
1388 DbModifier::deleteRecord(Table::Id inTableId, const RecordId &inRecordId)
1389 {
1390 modifyDatabase();
1391 findTable(inTableId).deleteRecord(inRecordId);
1392 }
1393
1394 const RecordId
1395 DbModifier::insertRecord(Table::Id inTableId,
1396 const CSSM_DB_RECORD_ATTRIBUTE_DATA *inAttributes,
1397 const CssmData *inData)
1398 {
1399 modifyDatabase();
1400 return findTable(inTableId).insertRecord(mVersionId, inAttributes, inData);
1401 }
1402
1403 const RecordId
1404 DbModifier::updateRecord(Table::Id inTableId, const RecordId &inRecordId,
1405 const CSSM_DB_RECORD_ATTRIBUTE_DATA *inAttributes,
1406 const CssmData *inData,
1407 CSSM_DB_MODIFY_MODE inModifyMode)
1408 {
1409 // @@@ Investigate why update is forcing a commit unlike delete and insert?
1410 commit(); // XXX this is not thread safe, but what is?
1411 modifyDatabase();
1412 return findTable(inTableId).updateRecord(inRecordId, inAttributes, inData, inModifyMode);
1413 }
1414
1415 // Create a table associated with a given metarecord, and add the table
1416 // to the database.
1417
1418 ModifiedTable *
1419 DbModifier::createTable(MetaRecord *inMetaRecord)
1420 {
1421 auto_ptr<MetaRecord> aMetaRecord(inMetaRecord);
1422 auto_ptr<ModifiedTable> aModifiedTable(new ModifiedTable(inMetaRecord));
1423 // Now that aModifiedTable is fully constructed it owns inMetaRecord
1424 aMetaRecord.release();
1425
1426 if (!mModifiedTableMap.insert
1427 (ModifiedTableMap::value_type(inMetaRecord->dataRecordType(),
1428 aModifiedTable.get())).second)
1429 {
1430 // XXX Should be CSSMERR_DL_DUPLICATE_RECORDTYPE. Since that
1431 // doesn't exist we report that the metatable's unique index would
1432 // no longer be valid
1433 CssmError::throwMe(CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA);
1434 }
1435
1436 return aModifiedTable.release();
1437 }
1438
1439 void
1440 DbModifier::deleteTable(Table::Id inTableId)
1441 {
1442 modifyDatabase();
1443 // Can't delete schema tables.
1444 if (CSSM_DB_RECORDTYPE_SCHEMA_START <= inTableId
1445 && inTableId < CSSM_DB_RECORDTYPE_SCHEMA_END)
1446 CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE);
1447
1448 // Find the ModifiedTable and delete it
1449 ModifiedTableMap::iterator it = mModifiedTableMap.find(inTableId);
1450 if (it == mModifiedTableMap.end())
1451 CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE);
1452
1453 delete it->second;
1454 mModifiedTableMap.erase(it);
1455 }
1456
1457 uint32
1458 DbModifier::writeAuthSection(uint32 inSectionOffset)
1459 {
1460 WriteSection anAuthSection;
1461
1462 // XXX Put real data into the authsection.
1463 uint32 anOffset = anAuthSection.put(0, 0);
1464 anAuthSection.size(anOffset);
1465
1466 mAtomicTempFile->write(AtomicFile::FromStart, inSectionOffset,
1467 anAuthSection.address(), anAuthSection.size());
1468 return inSectionOffset + anOffset;
1469 }
1470
1471 uint32
1472 DbModifier::writeSchemaSection(uint32 inSectionOffset)
1473 {
1474 uint32 aTableCount = mModifiedTableMap.size();
1475 WriteSection aTableSection(CssmAllocator::standard(),
1476 OffsetTables + AtomSize * aTableCount);
1477 // Set aTableSection to the correct size.
1478 aTableSection.size(OffsetTables + AtomSize * aTableCount);
1479 aTableSection.put(OffsetTablesCount, aTableCount);
1480
1481 uint32 anOffset = inSectionOffset + OffsetTables + AtomSize * aTableCount;
1482 ModifiedTableMap::const_iterator anIt = mModifiedTableMap.begin();
1483 ModifiedTableMap::const_iterator anEnd = mModifiedTableMap.end();
1484 for (uint32 aTableNumber = 0; anIt != anEnd; anIt++, aTableNumber++)
1485 {
1486 // Put the offset to the current table relative to the start of
1487 // this section into the tables array
1488 aTableSection.put(OffsetTables + AtomSize * aTableNumber,
1489 anOffset - inSectionOffset);
1490 anOffset = anIt->second->writeTable(*mAtomicTempFile, anOffset);
1491 }
1492
1493 aTableSection.put(OffsetSchemaSize, anOffset - inSectionOffset);
1494 mAtomicTempFile->write(AtomicFile::FromStart, inSectionOffset,
1495 aTableSection.address(), aTableSection.size());
1496
1497 return anOffset;
1498 }
1499
1500 void
1501 DbModifier::commit()
1502 {
1503 if (!mAtomicTempFile)
1504 return;
1505 try
1506 {
1507 WriteSection aHeaderSection(CssmAllocator::standard(), size_t(HeaderSize));
1508 // Set aHeaderSection to the correct size.
1509 aHeaderSection.size(HeaderSize);
1510
1511 // Start writing sections after the header
1512 uint32 anOffset = HeaderOffset + HeaderSize;
1513
1514 // Write auth section
1515 aHeaderSection.put(OffsetAuthOffset, anOffset);
1516 anOffset = writeAuthSection(anOffset);
1517 // Write schema section
1518 aHeaderSection.put(OffsetSchemaOffset, anOffset);
1519 anOffset = writeSchemaSection(anOffset);
1520
1521 // Write out the file header.
1522 aHeaderSection.put(OffsetMagic, HeaderMagic);
1523 aHeaderSection.put(OffsetVersion, HeaderVersion);
1524 mAtomicTempFile->write(AtomicFile::FromStart, HeaderOffset,
1525 aHeaderSection.address(), aHeaderSection.size());
1526
1527 // Write out the versionId.
1528 WriteSection aVersionSection(CssmAllocator::standard(), size_t(AtomSize));
1529 anOffset = aVersionSection.put(0, mVersionId);
1530 aVersionSection.size(anOffset);
1531
1532 mAtomicTempFile->write(AtomicFile::FromEnd, 0,
1533 aVersionSection.address(), aVersionSection.size());
1534
1535 mAtomicTempFile->commit();
1536 mAtomicTempFile = NULL;
1537 }
1538 catch(...)
1539 {
1540 rollback();
1541 throw;
1542 }
1543 }
1544
1545 void
1546 DbModifier::rollback() throw()
1547 {
1548 // This will destroy the AtomicTempFile if we have one causing it to rollback.
1549 mAtomicTempFile = NULL;
1550 }
1551
1552 const RecordId
1553 DbModifier::getRecord(Table::Id inTableId, const RecordId &inRecordId,
1554 CSSM_DB_RECORD_ATTRIBUTE_DATA *inoutAttributes,
1555 CssmData *inoutData, CssmAllocator &inAllocator)
1556 {
1557 // XXX never call commit(), rather search our own record tables.
1558 commit(); // XXX Requires write lock.
1559 return getDbVersion()->getRecord(inTableId, inRecordId,
1560 inoutAttributes, inoutData, inAllocator);
1561 }
1562
1563 Cursor *
1564 DbModifier::createCursor(const CSSM_QUERY *inQuery)
1565 {
1566 // XXX Be smarter as to when we must call commit (i.e. don't
1567 // force commit if the table being queried has not been modified).
1568 commit(); // XXX Requires write lock.
1569 return getDbVersion()->createCursor(inQuery);
1570 }
1571
1572 // Insert schema records for a new table into the metatables of the database. This gets
1573 // called while a database is being created.
1574
1575 void
1576 DbModifier::insertTableSchema(const CssmDbRecordAttributeInfo &inInfo,
1577 const CSSM_DB_RECORD_INDEX_INFO *inIndexInfo /* = NULL */)
1578 {
1579 ModifiedTable &aTable = findTable(inInfo.DataRecordType);
1580 const MetaRecord &aMetaRecord = aTable.getMetaRecord();
1581
1582 CssmAutoDbRecordAttributeData aRecordBuilder(5); // Set capacity to 5 so we don't need to grow
1583
1584 // Create the entry for the SchemaRelations table.
1585 aRecordBuilder.add(RelationID, inInfo.recordType());
1586 aRecordBuilder.add(RelationName, mDb.recordName(inInfo.recordType()));
1587
1588 // Insert the record into the SchemaRelations ModifiedTable
1589 findTable(mDb.schemaRelations.DataRecordType).insertRecord(mVersionId,
1590 &aRecordBuilder, NULL);
1591
1592 ModifiedTable &anAttributeTable = findTable(mDb.schemaAttributes.DataRecordType);
1593 for (uint32 anIndex = 0; anIndex < inInfo.size(); anIndex++)
1594 {
1595 // Create an entry for the SchemaAttributes table.
1596 aRecordBuilder.clear();
1597 aRecordBuilder.add(RelationID, inInfo.recordType());
1598 aRecordBuilder.add(AttributeNameFormat, inInfo.at(anIndex).nameFormat());
1599
1600 uint32 attributeId = aMetaRecord.metaAttribute(inInfo.at(anIndex)).attributeId();
1601
1602 switch (inInfo.at(anIndex).nameFormat())
1603 {
1604 case CSSM_DB_ATTRIBUTE_NAME_AS_STRING:
1605 aRecordBuilder.add(AttributeName, inInfo.at(anIndex).Label.AttributeName);
1606 break;
1607 case CSSM_DB_ATTRIBUTE_NAME_AS_OID:
1608 aRecordBuilder.add(AttributeNameID, inInfo.at(anIndex).Label.AttributeOID);
1609 break;
1610 case CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER:
1611 break;
1612 default:
1613 CssmError::throwMe(CSSMERR_DL_INVALID_FIELD_NAME);
1614 }
1615
1616 aRecordBuilder.add(AttributeID, attributeId);
1617 aRecordBuilder.add(AttributeFormat, inInfo.at(anIndex).format());
1618
1619 // Insert the record into the SchemaAttributes ModifiedTable
1620 anAttributeTable.insertRecord(mVersionId, &aRecordBuilder, NULL);
1621 }
1622
1623 if (inIndexInfo != NULL) {
1624
1625 if (inIndexInfo->DataRecordType != inInfo.DataRecordType &&
1626 inIndexInfo->NumberOfIndexes > 0)
1627 CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE);
1628
1629 ModifiedTable &indexMetaTable = findTable(mDb.schemaIndexes.DataRecordType);
1630 uint32 aNumberOfIndexes = inIndexInfo->NumberOfIndexes;
1631
1632 for (uint32 anIndex = 0; anIndex < aNumberOfIndexes; anIndex++)
1633 {
1634 const CssmDbIndexInfo &thisIndex = CssmDbIndexInfo::overlay(inIndexInfo->IndexInfo[anIndex]);
1635
1636 // make sure the index is supported
1637 if (thisIndex.dataLocation() != CSSM_DB_INDEX_ON_ATTRIBUTE)
1638 CssmError::throwMe(CSSMERR_DL_INVALID_INDEX_INFO);
1639
1640 // assign an index ID: the unique index is ID 0, all others are ID > 0
1641 uint32 indexId;
1642 if (thisIndex.IndexType == CSSM_DB_INDEX_UNIQUE)
1643 indexId = 0;
1644 else
1645 indexId = anIndex + 1;
1646
1647 // figure out the attribute ID
1648 uint32 attributeId =
1649 aMetaRecord.metaAttribute(thisIndex.Info).attributeId();
1650
1651 // Create an entry for the SchemaIndexes table.
1652 aRecordBuilder.clear();
1653 aRecordBuilder.add(RelationID, inInfo.DataRecordType);
1654 aRecordBuilder.add(IndexID, indexId);
1655 aRecordBuilder.add(AttributeID, attributeId);
1656 aRecordBuilder.add(IndexType, thisIndex.IndexType);
1657 aRecordBuilder.add(IndexedDataLocation, thisIndex.IndexedDataLocation);
1658
1659 // Insert the record into the SchemaIndexes ModifiedTable
1660 indexMetaTable.insertRecord(mVersionId, &aRecordBuilder, NULL);
1661
1662 // update the table's index objects
1663 DbMutableIndex &index = aTable.findIndex(indexId, aMetaRecord, indexId == 0);
1664 index.appendAttribute(attributeId);
1665 }
1666 }
1667 }
1668
1669 // Insert a new table. The attribute info is required; the index and parsing module
1670 // descriptions are optional. This version gets called during the creation of a
1671 // database.
1672
1673 void
1674 DbModifier::insertTable(const CssmDbRecordAttributeInfo &inInfo,
1675 const CSSM_DB_RECORD_INDEX_INFO *inIndexInfo /* = NULL */,
1676 const CSSM_DB_PARSING_MODULE_INFO *inParsingModule /* = NULL */)
1677 {
1678 modifyDatabase();
1679 createTable(new MetaRecord(inInfo));
1680 insertTableSchema(inInfo, inIndexInfo);
1681 }
1682
1683 // Insert a new table. This is the version that gets called when a table is added
1684 // after a database has been created.
1685
1686 void
1687 DbModifier::insertTable(Table::Id inTableId, const string &inTableName,
1688 uint32 inNumberOfAttributes,
1689 const CSSM_DB_SCHEMA_ATTRIBUTE_INFO *inAttributeInfo,
1690 uint32 inNumberOfIndexes,
1691 const CSSM_DB_SCHEMA_INDEX_INFO *inIndexInfo)
1692 {
1693 modifyDatabase();
1694 ModifiedTable *aTable = createTable(new MetaRecord(inTableId, inNumberOfAttributes, inAttributeInfo));
1695
1696 CssmAutoDbRecordAttributeData aRecordBuilder(6); // Set capacity to 6 so we don't need to grow
1697
1698 // Create the entry for the SchemaRelations table.
1699 aRecordBuilder.add(RelationID, inTableId);
1700 aRecordBuilder.add(RelationName, inTableName);
1701
1702 // Insert the record into the SchemaRelations ModifiedTable
1703 findTable(mDb.schemaRelations.DataRecordType).insertRecord(mVersionId,
1704 &aRecordBuilder, NULL);
1705
1706 ModifiedTable &anAttributeTable = findTable(mDb.schemaAttributes.DataRecordType);
1707 for (uint32 anIndex = 0; anIndex < inNumberOfAttributes; anIndex++)
1708 {
1709 // Create an entry for the SchemaAttributes table.
1710 aRecordBuilder.clear();
1711 aRecordBuilder.add(RelationID, inTableId);
1712 // XXX What should this be? We set it to CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER for now
1713 // since the AttributeID is always valid.
1714 aRecordBuilder.add(AttributeNameFormat, uint32(CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER));
1715 aRecordBuilder.add(AttributeID, inAttributeInfo[anIndex].AttributeId);
1716 if (inAttributeInfo[anIndex].AttributeName)
1717 aRecordBuilder.add(AttributeName, inAttributeInfo[anIndex].AttributeName);
1718 if (inAttributeInfo[anIndex].AttributeNameID.Length > 0)
1719 aRecordBuilder.add(AttributeNameID, inAttributeInfo[anIndex].AttributeNameID);
1720 aRecordBuilder.add(AttributeFormat, inAttributeInfo[anIndex].DataType);
1721
1722 // Insert the record into the SchemaAttributes ModifiedTable
1723 anAttributeTable.insertRecord(mVersionId, &aRecordBuilder, NULL);
1724 }
1725
1726 ModifiedTable &anIndexTable = findTable(mDb.schemaIndexes.DataRecordType);
1727 for (uint32 anIndex = 0; anIndex < inNumberOfIndexes; anIndex++)
1728 {
1729 // Create an entry for the SchemaIndexes table.
1730 aRecordBuilder.clear();
1731 aRecordBuilder.add(RelationID, inTableId);
1732 aRecordBuilder.add(IndexID, inIndexInfo[anIndex].IndexId);
1733 aRecordBuilder.add(AttributeID, inIndexInfo[anIndex].AttributeId);
1734 aRecordBuilder.add(IndexType, inIndexInfo[anIndex].IndexType);
1735 aRecordBuilder.add(IndexedDataLocation, inIndexInfo[anIndex].IndexedDataLocation);
1736
1737 // Insert the record into the SchemaIndexes ModifiedTable
1738 anIndexTable.insertRecord(mVersionId, &aRecordBuilder, NULL);
1739
1740 // update the table's index objects
1741 DbMutableIndex &index = aTable->findIndex(inIndexInfo[anIndex].IndexId,
1742 aTable->getMetaRecord(), inIndexInfo[anIndex].IndexType == CSSM_DB_INDEX_UNIQUE);
1743 index.appendAttribute(inIndexInfo[anIndex].AttributeId);
1744 }
1745 }
1746
1747 ModifiedTable &
1748 DbModifier::findTable(Table::Id inTableId)
1749 {
1750 ModifiedTableMap::iterator it = mModifiedTableMap.find(inTableId);
1751 if (it == mModifiedTableMap.end())
1752 CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE);
1753 return *it->second;
1754 }
1755
1756
1757 //
1758 // AppleDatabaseManager implementation
1759 //
1760
1761 AppleDatabaseManager::AppleDatabaseManager(const AppleDatabaseTableName *tableNames)
1762 : DatabaseManager(),
1763 mTableNames(tableNames)
1764 {
1765 // make sure that a proper set of table ids and names has been provided
1766
1767 if (!mTableNames)
1768 CssmError::throwMe(CSSMERR_DL_INTERNAL_ERROR);
1769 else {
1770 uint32 i;
1771 for (i = 0; mTableNames[i].mTableName; i++) {}
1772 if (i < AppleDatabaseTableName::kNumRequiredTableNames)
1773 CssmError::throwMe(CSSMERR_DL_INTERNAL_ERROR);
1774 }
1775 }
1776
1777 Database *
1778 AppleDatabaseManager::make(const DbName &inDbName)
1779 {
1780 return new AppleDatabase(inDbName, mTableNames);
1781 }
1782
1783
1784 //
1785 // AppleDbContext implementation
1786 //
1787
1788 /* This is the version 0 CSSM_APPLEDL_OPEN_PARAMETERS struct used up to 10.2.x. */
1789 extern "C" {
1790
1791 typedef struct cssm_appledl_open_parameters_v0
1792 {
1793 uint32 length; /* Should be sizeof(CSSM_APPLEDL_OPEN_PARAMETERS_V0). */
1794 uint32 version; /* Should be 0. */
1795 CSSM_BOOL autoCommit;
1796 } CSSM_APPLEDL_OPEN_PARAMETERS_V0;
1797
1798 };
1799
1800 AppleDbContext::AppleDbContext(Database &inDatabase,
1801 DatabaseSession &inDatabaseSession,
1802 CSSM_DB_ACCESS_TYPE inAccessRequest,
1803 const AccessCredentials *inAccessCred,
1804 const void *inOpenParameters) :
1805 DbContext(inDatabase, inDatabaseSession, inAccessRequest, inAccessCred),
1806 mAutoCommit(true),
1807 mMode(0666)
1808 {
1809 const CSSM_APPLEDL_OPEN_PARAMETERS *anOpenParameters =
1810 reinterpret_cast<const CSSM_APPLEDL_OPEN_PARAMETERS *>(inOpenParameters);
1811
1812 if (anOpenParameters)
1813 {
1814 switch (anOpenParameters->version)
1815 {
1816 case 1:
1817 if (anOpenParameters->length < sizeof(CSSM_APPLEDL_OPEN_PARAMETERS))
1818 CssmError::throwMe(CSSMERR_APPLEDL_INVALID_OPEN_PARAMETERS);
1819
1820 if (anOpenParameters->mask & kCSSM_APPLEDL_MASK_MODE)
1821 mMode = anOpenParameters->mode;
1822 /*DROPTHROUGH*/
1823 case 0:
1824 if (anOpenParameters->length < sizeof(CSSM_APPLEDL_OPEN_PARAMETERS_V0))
1825 CssmError::throwMe(CSSMERR_APPLEDL_INVALID_OPEN_PARAMETERS);
1826
1827 mAutoCommit = anOpenParameters->autoCommit == CSSM_FALSE ? false : true;
1828 break;
1829
1830 default:
1831 CssmError::throwMe(CSSMERR_APPLEDL_INVALID_OPEN_PARAMETERS);
1832 }
1833 }
1834 }
1835
1836 AppleDbContext::~AppleDbContext()
1837 {
1838 }
1839
1840 //
1841 // AppleDatabase implementation
1842 //
1843 AppleDatabase::AppleDatabase(const DbName &inDbName, const AppleDatabaseTableName *tableNames) :
1844 Database(inDbName),
1845 schemaRelations(tableNames[AppleDatabaseTableName::kSchemaInfo].mTableId,
1846 sizeof(AttrSchemaRelations) / sizeof(CSSM_DB_ATTRIBUTE_INFO),
1847 const_cast<CSSM_DB_ATTRIBUTE_INFO_PTR>(AttrSchemaRelations)),
1848 schemaAttributes(tableNames[AppleDatabaseTableName::kSchemaAttributes].mTableId,
1849 sizeof(AttrSchemaAttributes) / sizeof(CSSM_DB_ATTRIBUTE_INFO),
1850 const_cast<CSSM_DB_ATTRIBUTE_INFO_PTR>(AttrSchemaAttributes)),
1851 schemaIndexes(tableNames[AppleDatabaseTableName::kSchemaIndexes].mTableId,
1852 sizeof(AttrSchemaIndexes) / sizeof(CSSM_DB_ATTRIBUTE_INFO),
1853 const_cast<CSSM_DB_ATTRIBUTE_INFO_PTR>(AttrSchemaIndexes)),
1854 schemaParsingModule(tableNames[AppleDatabaseTableName::kSchemaParsingModule].mTableId,
1855 sizeof(AttrSchemaParsingModule) / sizeof(CSSM_DB_ATTRIBUTE_INFO),
1856 const_cast<CSSM_DB_ATTRIBUTE_INFO_PTR>(AttrSchemaParsingModule)),
1857 mAtomicFile(mDbName.dbName()),
1858 mDbModifier(mAtomicFile, *this),
1859 mTableNames(tableNames)
1860 {
1861 }
1862
1863 AppleDatabase::~AppleDatabase()
1864 {
1865 }
1866
1867 // Return the name of a record type. This uses a table that maps record types
1868 // to record names. The table is provided when the database is created.
1869
1870 const char *AppleDatabase::recordName(CSSM_DB_RECORDTYPE inRecordType) const
1871 {
1872 if (inRecordType == CSSM_DL_DB_RECORD_ANY || inRecordType == CSSM_DL_DB_RECORD_ALL_KEYS)
1873 CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE);
1874
1875 for (uint32 i = 0; mTableNames[i].mTableName; i++)
1876 if (mTableNames[i].mTableId == inRecordType)
1877 return mTableNames[i].mTableName;
1878
1879 return "";
1880 }
1881
1882 DbContext *
1883 AppleDatabase::makeDbContext(DatabaseSession &inDatabaseSession,
1884 CSSM_DB_ACCESS_TYPE inAccessRequest,
1885 const AccessCredentials *inAccessCred,
1886 const void *inOpenParameters)
1887 {
1888 return new AppleDbContext(*this, inDatabaseSession, inAccessRequest,
1889 inAccessCred, inOpenParameters);
1890 }
1891
1892 void
1893 AppleDatabase::dbCreate(DbContext &inDbContext, const CSSM_DBINFO &inDBInfo,
1894 const CSSM_ACL_ENTRY_INPUT *inInitialAclEntry)
1895 {
1896 AppleDbContext &context = safer_cast<AppleDbContext &>(inDbContext);
1897 try
1898 {
1899 StLock<Mutex> _(mWriteLock);
1900 mDbModifier.createDatabase(inDBInfo, inInitialAclEntry, context.mode());
1901 }
1902 catch(...)
1903 {
1904 mDbModifier.rollback();
1905 throw;
1906 }
1907 if (context.autoCommit())
1908 mDbModifier.commit();
1909 }
1910
1911 void
1912 AppleDatabase::dbOpen(DbContext &inDbContext)
1913 {
1914 mDbModifier.openDatabase();
1915 }
1916
1917 void
1918 AppleDatabase::dbClose()
1919 {
1920 StLock<Mutex> _(mWriteLock);
1921 mDbModifier.closeDatabase();
1922 }
1923
1924 void
1925 AppleDatabase::dbDelete(DatabaseSession &inDatabaseSession,
1926 const AccessCredentials *inAccessCred)
1927 {
1928 StLock<Mutex> _(mWriteLock);
1929 // XXX Check callers credentials.
1930 mDbModifier.deleteDatabase();
1931 }
1932
1933 void
1934 AppleDatabase::createRelation(DbContext &inDbContext,
1935 CSSM_DB_RECORDTYPE inRelationID,
1936 const char *inRelationName,
1937 uint32 inNumberOfAttributes,
1938 const CSSM_DB_SCHEMA_ATTRIBUTE_INFO &inAttributeInfo,
1939 uint32 inNumberOfIndexes,
1940 const CSSM_DB_SCHEMA_INDEX_INFO &inIndexInfo)
1941 {
1942 try
1943 {
1944 StLock<Mutex> _(mWriteLock);
1945 // XXX Fix the refs here.
1946 mDbModifier.insertTable(inRelationID, inRelationName,
1947 inNumberOfAttributes, &inAttributeInfo,
1948 inNumberOfIndexes, &inIndexInfo);
1949 }
1950 catch(...)
1951 {
1952 mDbModifier.rollback();
1953 throw;
1954 }
1955 if (safer_cast<AppleDbContext &>(inDbContext).autoCommit())
1956 mDbModifier.commit();
1957 }
1958
1959 void
1960 AppleDatabase::destroyRelation(DbContext &inDbContext,
1961 CSSM_DB_RECORDTYPE inRelationID)
1962 {
1963 try
1964 {
1965 StLock<Mutex> _(mWriteLock);
1966 mDbModifier.deleteTable(inRelationID);
1967 }
1968 catch(...)
1969 {
1970 mDbModifier.rollback();
1971 throw;
1972 }
1973 if (safer_cast<AppleDbContext &>(inDbContext).autoCommit())
1974 mDbModifier.commit();
1975 }
1976
1977 void
1978 AppleDatabase::authenticate(DbContext &inDbContext,
1979 CSSM_DB_ACCESS_TYPE inAccessRequest,
1980 const AccessCredentials &inAccessCred)
1981 {
1982 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED);
1983 }
1984
1985 void
1986 AppleDatabase::getDbAcl(DbContext &inDbContext,
1987 const CSSM_STRING *inSelectionTag,
1988 uint32 &outNumberOfAclInfos,
1989 CSSM_ACL_ENTRY_INFO_PTR &outAclInfos)
1990 {
1991 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED);
1992 }
1993
1994 void
1995 AppleDatabase::changeDbAcl(DbContext &inDbContext,
1996 const AccessCredentials &inAccessCred,
1997 const CSSM_ACL_EDIT &inAclEdit)
1998 {
1999 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED);
2000 }
2001
2002 void
2003 AppleDatabase::getDbOwner(DbContext &inDbContext,
2004 CSSM_ACL_OWNER_PROTOTYPE &outOwner)
2005 {
2006 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED);
2007 }
2008
2009 void
2010 AppleDatabase::changeDbOwner(DbContext &inDbContext,
2011 const AccessCredentials &inAccessCred,
2012 const CSSM_ACL_OWNER_PROTOTYPE &inNewOwner)
2013 {
2014 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED);
2015 }
2016
2017 char *
2018 AppleDatabase::getDbNameFromHandle(const DbContext &inDbContext) const
2019 {
2020 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED);
2021 }
2022
2023 CSSM_DB_UNIQUE_RECORD_PTR
2024 AppleDatabase::dataInsert(DbContext &inDbContext,
2025 CSSM_DB_RECORDTYPE inRecordType,
2026 const CSSM_DB_RECORD_ATTRIBUTE_DATA *inAttributes,
2027 const CssmData *inData)
2028 {
2029 CSSM_DB_UNIQUE_RECORD_PTR anUniqueRecordPtr = NULL;
2030 try
2031 {
2032 StLock<Mutex> _(mWriteLock);
2033 const RecordId aRecordId =
2034 mDbModifier.insertRecord(inRecordType, inAttributes, inData);
2035
2036 anUniqueRecordPtr = createUniqueRecord(inDbContext, inRecordType,
2037 aRecordId);
2038 if (safer_cast<AppleDbContext &>(inDbContext).autoCommit())
2039 mDbModifier.commit();
2040 }
2041 catch(...)
2042 {
2043 if (anUniqueRecordPtr != NULL)
2044 freeUniqueRecord(inDbContext, *anUniqueRecordPtr);
2045
2046 mDbModifier.rollback();
2047 throw;
2048 }
2049
2050 return anUniqueRecordPtr;
2051 }
2052
2053 void
2054 AppleDatabase::dataDelete(DbContext &inDbContext,
2055 const CSSM_DB_UNIQUE_RECORD &inUniqueRecord)
2056 {
2057 try
2058 {
2059 StLock<Mutex> _(mWriteLock);
2060 Table::Id aTableId;
2061 const RecordId aRecordId(parseUniqueRecord(inUniqueRecord, aTableId));
2062 mDbModifier.deleteRecord(aTableId, aRecordId);
2063 }
2064 catch(...)
2065 {
2066 mDbModifier.rollback();
2067 throw;
2068 }
2069
2070 if (safer_cast<AppleDbContext &>(inDbContext).autoCommit())
2071 mDbModifier.commit();
2072 }
2073
2074 void
2075 AppleDatabase::dataModify(DbContext &inDbContext,
2076 CSSM_DB_RECORDTYPE inRecordType,
2077 CSSM_DB_UNIQUE_RECORD &inoutUniqueRecord,
2078 const CSSM_DB_RECORD_ATTRIBUTE_DATA *inAttributesToBeModified,
2079 const CssmData *inDataToBeModified,
2080 CSSM_DB_MODIFY_MODE inModifyMode)
2081 {
2082 try
2083 {
2084 StLock<Mutex> _(mWriteLock);
2085 Table::Id aTableId;
2086 const RecordId aRecordId =
2087 mDbModifier.updateRecord(aTableId,
2088 parseUniqueRecord(inoutUniqueRecord, aTableId),
2089 inAttributesToBeModified,
2090 inDataToBeModified,
2091 inModifyMode);
2092 updateUniqueRecord(inDbContext, inRecordType, aRecordId, inoutUniqueRecord);
2093 }
2094 catch(...)
2095 {
2096 mDbModifier.rollback();
2097 throw;
2098 }
2099
2100 if (safer_cast<AppleDbContext &>(inDbContext).autoCommit())
2101 mDbModifier.commit();
2102 }
2103
2104 CSSM_HANDLE
2105 AppleDatabase::dataGetFirst(DbContext &inDbContext,
2106 const DLQuery *inQuery,
2107 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes,
2108 CssmData *inoutData,
2109 CSSM_DB_UNIQUE_RECORD_PTR &outUniqueRecord)
2110 {
2111 // XXX: register Cursor with DbContext and have DbContext call
2112 // dataAbortQuery for all outstanding Query objects on close.
2113 auto_ptr<Cursor> aCursor(mDbModifier.createCursor(inQuery));
2114 Table::Id aTableId;
2115 RecordId aRecordId;
2116
2117 if (!aCursor->next(aTableId, inoutAttributes, inoutData,
2118 inDbContext.mDatabaseSession, aRecordId))
2119 // return a NULL handle, and implicitly delete the cursor
2120 return CSSM_INVALID_HANDLE;
2121
2122 outUniqueRecord = createUniqueRecord(inDbContext, aTableId, aRecordId);
2123 return aCursor.release()->handle(); // We didn't throw so keep the Cursor around.
2124 }
2125
2126 bool
2127 AppleDatabase::dataGetNext(DbContext &inDbContext,
2128 CSSM_HANDLE inResultsHandle,
2129 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes,
2130 CssmData *inoutData,
2131 CSSM_DB_UNIQUE_RECORD_PTR &outUniqueRecord)
2132 {
2133 auto_ptr<Cursor> aCursor(&findHandle<Cursor>(inResultsHandle, CSSMERR_DL_INVALID_RESULTS_HANDLE));
2134 Table::Id aTableId;
2135 RecordId aRecordId;
2136
2137 if (!aCursor->next(aTableId, inoutAttributes, inoutData, inDbContext.mDatabaseSession, aRecordId))
2138 return false;
2139
2140 outUniqueRecord = createUniqueRecord(inDbContext, aTableId, aRecordId);
2141
2142 aCursor.release();
2143 return true;
2144 }
2145
2146 void
2147 AppleDatabase::dataAbortQuery(DbContext &inDbContext,
2148 CSSM_HANDLE inResultsHandle)
2149 {
2150 delete &findHandle<Cursor>(inResultsHandle, CSSMERR_DL_INVALID_RESULTS_HANDLE);
2151 }
2152
2153 void
2154 AppleDatabase::dataGetFromUniqueRecordId(DbContext &inDbContext,
2155 const CSSM_DB_UNIQUE_RECORD &inUniqueRecord,
2156 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes,
2157 CssmData *inoutData)
2158 {
2159 Table::Id aTableId;
2160 const RecordId aRecordId(parseUniqueRecord(inUniqueRecord, aTableId));
2161 // XXX Change CDSA spec to use new RecordId returned by this function
2162 mDbModifier.getRecord(aTableId, aRecordId, inoutAttributes, inoutData,
2163 inDbContext.mDatabaseSession);
2164 }
2165
2166 void
2167 AppleDatabase::freeUniqueRecord(DbContext &inDbContext,
2168 CSSM_DB_UNIQUE_RECORD &inUniqueRecord)
2169 {
2170 if (inUniqueRecord.RecordIdentifier.Length != 0
2171 && inUniqueRecord.RecordIdentifier.Data != NULL)
2172 {
2173 inUniqueRecord.RecordIdentifier.Length = 0;
2174 inDbContext.mDatabaseSession.free(inUniqueRecord.RecordIdentifier.Data);
2175 }
2176 inDbContext.mDatabaseSession.free(&inUniqueRecord);
2177 }
2178
2179 void
2180 AppleDatabase::updateUniqueRecord(DbContext &inDbContext,
2181 CSSM_DB_RECORDTYPE inTableId,
2182 const RecordId &inRecordId,
2183 CSSM_DB_UNIQUE_RECORD &inoutUniqueRecord)
2184 {
2185 uint32 *aBuffer = reinterpret_cast<uint32 *>(inoutUniqueRecord.RecordIdentifier.Data);
2186 aBuffer[0] = inTableId;
2187 aBuffer[1] = inRecordId.mRecordNumber;
2188 aBuffer[2] = inRecordId.mCreateVersion;
2189 aBuffer[3] = inRecordId.mRecordVersion;
2190 }
2191
2192 CSSM_DB_UNIQUE_RECORD_PTR
2193 AppleDatabase::createUniqueRecord(DbContext &inDbContext,
2194 CSSM_DB_RECORDTYPE inTableId,
2195 const RecordId &inRecordId)
2196 {
2197 CSSM_DB_UNIQUE_RECORD_PTR aUniqueRecord =
2198 inDbContext.mDatabaseSession.alloc<CSSM_DB_UNIQUE_RECORD>();
2199 memset(aUniqueRecord, 0, sizeof(*aUniqueRecord));
2200 aUniqueRecord->RecordIdentifier.Length = sizeof(uint32) * 4;
2201 try
2202 {
2203 aUniqueRecord->RecordIdentifier.Data =
2204 inDbContext.mDatabaseSession.alloc<uint8>(sizeof(uint32) * 4);
2205 updateUniqueRecord(inDbContext, inTableId, inRecordId, *aUniqueRecord);
2206 }
2207 catch(...)
2208 {
2209 inDbContext.mDatabaseSession.free(aUniqueRecord);
2210 throw;
2211 }
2212
2213 return aUniqueRecord;
2214 }
2215
2216 const RecordId
2217 AppleDatabase::parseUniqueRecord(const CSSM_DB_UNIQUE_RECORD &inUniqueRecord,
2218 CSSM_DB_RECORDTYPE &outTableId)
2219 {
2220 if (inUniqueRecord.RecordIdentifier.Length != sizeof(uint32) * 4)
2221 CssmError::throwMe(CSSMERR_DL_INVALID_RECORD_UID);
2222
2223 uint32 *aBuffer = reinterpret_cast<uint32 *>(inUniqueRecord.RecordIdentifier.Data);
2224 outTableId = aBuffer[0];
2225 return RecordId(aBuffer[1], aBuffer[2], aBuffer[3]);
2226 }
2227
2228 void
2229 AppleDatabase::passThrough(DbContext &dbContext,
2230 uint32 passThroughId,
2231 const void *inputParams,
2232 void **outputParams)
2233 {
2234 switch (passThroughId)
2235 {
2236 case CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT:
2237 {
2238 CSSM_BOOL on = reinterpret_cast<CSSM_BOOL>(inputParams);
2239 safer_cast<AppleDbContext &>(dbContext).autoCommit(on);
2240 }
2241 break;
2242
2243 case CSSM_APPLEFILEDL_COMMIT:
2244 mDbModifier.commit();
2245 break;
2246
2247 case CSSM_APPLEFILEDL_ROLLBACK:
2248 mDbModifier.rollback();
2249 break;
2250
2251 default:
2252 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED);
2253 break;
2254 }
2255 }