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