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