2 * Copyright (c) 2000-2001,2003,2011-2014 Apple Inc. All Rights Reserved.
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
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.
20 // AppleDatabase.cpp - Description t.b.d.
22 #include "AppleDatabase.h"
23 #include <security_cdsa_plugin/DatabaseSession.h>
24 #include <security_cdsa_plugin/DbContext.h>
25 #include <security_cdsa_utilities/cssmdb.h>
26 #include <Security/cssmapple.h>
27 #include <security_utilities/trackingallocator.h>
28 #include <security_utilities/logging.h>
31 #include <libkern/OSAtomic.h>
35 #include <Security/cssmapplePriv.h>
38 static const char *kAppleDatabaseChanged
= "com.apple.AppleDatabaseChanged";
40 /* Number of seconds after which we open/pread/close a db to check it's
41 version number even if we didn't get any notifications. Note that we always
42 check just after we take a write lock and whenever we get a notification
43 that any db on the system has changed. */
44 static const CFTimeInterval kForceReReadTime
= 15.0;
46 /* Token on which we receive notifications and the pthread_once_t protecting
47 it's initialization. */
48 pthread_once_t gCommonInitMutex
= PTHREAD_ONCE_INIT
;
50 /* Global counter of how many notifications we have received and a lock to
51 protect the counter. */
52 static int kSegmentSize
= 4;
53 int32_t* gSegment
= NULL
;
55 /* Registration routine for notifcations. Called inside a pthread_once(). */
56 static void initCommon(void)
59 int segmentDescriptor
= shm_open (kAppleDatabaseChanged
, O_RDWR
| O_CREAT
, S_IRWXU
| S_IRWXG
| S_IRWXO
);
60 if (segmentDescriptor
< 0)
65 // set the segment size
66 ftruncate (segmentDescriptor
, kSegmentSize
);
69 int32_t* tmp
= (int32_t*) mmap (NULL
, kSegmentSize
, PROT_READ
| PROT_WRITE
, MAP_SHARED
, segmentDescriptor
, 0);
70 close (segmentDescriptor
);
72 if (tmp
== (int32_t*) -1) // can't map the memory?
85 Table::Table(const ReadSection
&inTableSection
) :
86 mMetaRecord(inTableSection
[OffsetId
]),
87 mTableSection(inTableSection
),
88 mRecordsCount(inTableSection
[OffsetRecordsCount
]),
89 mFreeListHead(inTableSection
[OffsetFreeListHead
]),
90 mRecordNumbersCount(inTableSection
[OffsetRecordNumbersCount
])
92 // can't easily initialize indexes here, since meta record is incomplete
93 // until much later... see DbVersion::open()
98 for_each_map_delete(mIndexMap
.begin(), mIndexMap
.end());
102 Table::readIndexSection()
104 uint32 indexSectionOffset
= mTableSection
.at(OffsetIndexesOffset
);
106 uint32 numIndexes
= mTableSection
.at(indexSectionOffset
+ AtomSize
);
108 for (uint32 i
= 0; i
< numIndexes
; i
++) {
109 uint32 indexOffset
= mTableSection
.at(indexSectionOffset
+ (i
+ 2) * AtomSize
);
110 ReadSection
indexSection(mTableSection
.subsection(indexOffset
));
112 auto_ptr
<DbConstIndex
> index(new DbConstIndex(*this, indexSection
));
113 mIndexMap
.insert(ConstIndexMap::value_type(index
->indexId(), index
.get()));
119 Table::createCursor(const CSSM_QUERY
*inQuery
, const DbVersion
&inDbVersion
) const
121 // if an index matches the query, return a cursor which uses the index
123 ConstIndexMap::const_iterator it
;
124 DbQueryKey
*queryKey
;
126 for (it
= mIndexMap
.begin(); it
!= mIndexMap
.end(); it
++)
127 if (it
->second
->matchesQuery(*inQuery
, queryKey
)) {
128 IndexCursor
*cursor
= new IndexCursor(queryKey
, inDbVersion
, *this, it
->second
);
132 // otherwise, return a cursor that iterates over all table records
134 return new LinearCursor(inQuery
, inDbVersion
, *this);
138 Table::getRecordSection(uint32 inRecordNumber
) const
140 if (inRecordNumber
>= mRecordNumbersCount
)
141 CssmError::throwMe(CSSMERR_DL_INVALID_RECORD_UID
);
143 uint32 aRecordOffset
= mTableSection
[OffsetRecordNumbers
+ AtomSize
146 // Check if this RecordNumber has been deleted.
147 if (aRecordOffset
& 1 || aRecordOffset
== 0)
148 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND
);
150 return MetaRecord::readSection(mTableSection
, aRecordOffset
);
154 Table::getRecord(const RecordId
&inRecordId
,
155 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes
,
157 Allocator
&inAllocator
) const
159 const ReadSection aRecordSection
= getRecordSection(inRecordId
.mRecordNumber
);
160 const RecordId aRecordId
= MetaRecord::unpackRecordId(aRecordSection
);
162 // Make sure the RecordNumber matches that in the RecordId we just retrived.
163 if (aRecordId
.mRecordNumber
!= inRecordId
.mRecordNumber
)
164 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
166 if (aRecordId
.mCreateVersion
!= inRecordId
.mCreateVersion
)
167 CssmError::throwMe(CSSMERR_DL_INVALID_RECORD_UID
);
169 // XXX Figure out which value to pass for inQueryFlags (5th) argument
170 mMetaRecord
.unpackRecord(aRecordSection
, inAllocator
, inoutAttributes
,
176 Table::popFreeList(uint32
&aFreeListHead
) const
178 assert(aFreeListHead
| 1);
179 uint32 anOffset
= aFreeListHead
^ 1;
180 uint32 aRecordNumber
= (anOffset
- OffsetRecordNumbers
) / AtomSize
;
181 aFreeListHead
= mTableSection
[anOffset
];
182 return aRecordNumber
;
186 Table::getRecordsSection() const
188 return mTableSection
.subsection(mTableSection
[OffsetRecords
]);
192 Table::matchesTableId(Id inTableId
) const
194 Id anId
= mMetaRecord
.dataRecordType();
195 if (inTableId
== CSSM_DL_DB_RECORD_ANY
) // All non schema tables.
196 return !(CSSM_DB_RECORDTYPE_SCHEMA_START
<= anId
197 && anId
< CSSM_DB_RECORDTYPE_SCHEMA_END
);
199 if (inTableId
== CSSM_DL_DB_RECORD_ALL_KEYS
) // All key tables.
200 return (anId
== CSSM_DL_DB_RECORD_PUBLIC_KEY
201 || anId
== CSSM_DL_DB_RECORD_PRIVATE_KEY
202 || anId
== CSSM_DL_DB_RECORD_SYMMETRIC_KEY
);
204 return inTableId
== anId
; // Only if exact match.
211 ModifiedTable::ModifiedTable(const Table
*inTable
) :
214 mRecordNumberCount(inTable
->recordNumberCount()),
215 mFreeListHead(inTable
->freeListHead()),
220 ModifiedTable::ModifiedTable(MetaRecord
*inMetaRecord
) :
222 mNewMetaRecord(inMetaRecord
),
223 mRecordNumberCount(0),
229 ModifiedTable::~ModifiedTable()
231 for_each_map_delete(mIndexMap
.begin(), mIndexMap
.end());
232 for_each_map_delete(mInsertedMap
.begin(), mInsertedMap
.end());
234 delete mNewMetaRecord
;
238 ModifiedTable::deleteRecord(const RecordId
&inRecordId
)
242 uint32 aRecordNumber
= inRecordId
.mRecordNumber
;
244 // remove the record from all the indexes
245 MutableIndexMap::iterator it
;
246 for (it
= mIndexMap
.begin(); it
!= mIndexMap
.end(); it
++)
247 it
->second
->removeRecord(aRecordNumber
);
249 InsertedMap::iterator anIt
= mInsertedMap
.find(aRecordNumber
);
250 if (anIt
== mInsertedMap
.end())
252 // If we have no old table than this record can not exist yet.
254 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND
);
256 #if RECORDVERSIONCHECK
257 const RecordId aRecordId
= MetaRecord::unpackRecordId(mTable
->getRecordSection(aRecordNumber
));
258 if (aRecordId
.mRecordVersion
!= inRecordId
.mRecordVersion
)
259 CssmError::throwMe(CSSMERR_DL_RECORD_MODIFIED
);
262 // Schedule the record for deletion
263 if (!mDeletedSet
.insert(aRecordNumber
).second
)
264 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND
); // It was already deleted
268 const RecordId aRecordId
= MetaRecord::unpackRecordId(*anIt
->second
);
269 if (aRecordId
.mCreateVersion
!= inRecordId
.mCreateVersion
)
270 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND
);
272 #if RECORDVERSIONCHECK
273 if (aRecordId
.mRecordVersion
!= inRecordId
.mRecordVersion
)
274 CssmError::throwMe(CSSMERR_DL_RECORD_MODIFIED
);
277 // Remove the inserted (but uncommited) record. It should already be in mDeletedSet
278 // if it existed previously in mTable.
280 mInsertedMap
.erase(anIt
);
285 ModifiedTable::insertRecord(uint32 inVersionId
,
286 const CSSM_DB_RECORD_ATTRIBUTE_DATA
*inAttributes
,
287 const CssmData
*inData
)
291 auto_ptr
<WriteSection
> aWriteSection(new WriteSection());
292 getMetaRecord().packRecord(*aWriteSection
, inAttributes
, inData
);
293 uint32 aRecordNumber
= nextRecordNumber();
295 // add the record to all the indexes; this will throw if the new record
296 // violates a unique index
297 MutableIndexMap::iterator it
;
298 for (it
= mIndexMap
.begin(); it
!= mIndexMap
.end(); it
++)
299 it
->second
->insertRecord(aRecordNumber
, *(aWriteSection
.get()));
301 // schedule the record for insertion
302 RecordId
aRecordId(aRecordNumber
, inVersionId
);
303 MetaRecord::packRecordId(aRecordId
, *aWriteSection
);
304 mInsertedMap
.insert(InsertedMap::value_type(aRecordNumber
, aWriteSection
.get()));
306 aWriteSection
.release();
312 ModifiedTable::updateRecord(const RecordId
&inRecordId
,
313 const CSSM_DB_RECORD_ATTRIBUTE_DATA
*inAttributes
,
314 const CssmData
*inData
,
315 CSSM_DB_MODIFY_MODE inModifyMode
)
319 uint32 aRecordNumber
= inRecordId
.mRecordNumber
;
320 InsertedMap::iterator anIt
= mInsertedMap
.find(aRecordNumber
);
322 // aReUpdate is true iff we are updating an already updated record.
323 bool aReUpdate
= anIt
!= mInsertedMap
.end();
325 // If we are not re-updating and there is no old table than this record does not exist yet.
326 if (!aReUpdate
&& !mTable
)
327 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND
);
329 const ReadSection
&anOldDbRecord
= aReUpdate
? *anIt
->second
: mTable
->getRecordSection(aRecordNumber
);
330 const RecordId aRecordId
= MetaRecord::unpackRecordId(anOldDbRecord
);
332 // Did someone else delete the record we are trying to update.
333 if (aRecordId
.mCreateVersion
!= inRecordId
.mCreateVersion
)
334 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND
);
336 #if RECORDVERSIONCHECK
337 // Is the record we that our update is based on current?
338 if (aRecordId
.mRecordVersion
!= inRecordId
.mRecordVersion
)
339 CssmError::throwMe(CSSMERR_DL_STALE_UNIQUE_RECORD
);
342 // Update the actual packed record.
343 auto_ptr
<WriteSection
> aDbRecord(new WriteSection());
344 getMetaRecord().updateRecord(anOldDbRecord
, *aDbRecord
,
345 CssmDbRecordAttributeData::overlay(inAttributes
), inData
, inModifyMode
);
348 // Bump the RecordVersion of this record.
349 RecordId
aNewRecordId(aRecordNumber
, inRecordId
.mCreateVersion
, inRecordId
.mRecordVersion
+ 1);
350 // Store the RecordVersion in the packed aDbRecord.
351 MetaRecord::packRecordId(aNewRecordId
, *aDbRecord
);
353 if (!aReUpdate
&& !mDeletedSet
.insert(aRecordNumber
).second
)
354 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND
); // Record was already in mDeletedSet
356 // remove the original record from all the indexes
357 MutableIndexMap::iterator it
;
358 for (it
= mIndexMap
.begin(); it
!= mIndexMap
.end(); it
++)
359 it
->second
->removeRecord(aRecordNumber
);
363 // Add the updated record to all the indexes; this will throw if the new record
364 // violates a unique index
365 for (it
= mIndexMap
.begin(); it
!= mIndexMap
.end(); it
++)
366 it
->second
->insertRecord(aRecordNumber
, *(aDbRecord
.get()));
370 // Get rid of anOldDbRecord from the inserted map and replace it
373 anIt
->second
= aDbRecord
.get();
377 // First time though so let's just put the new value in the map.
378 mInsertedMap
.insert(InsertedMap::value_type(aRecordNumber
, aDbRecord
.get()));
384 // We only remove aRecordNumber from mDeletedSet if we added it above.
386 mDeletedSet
.erase(aRecordNumber
);
388 // The 2 operations below are an attempt to preserve the indices when
391 // Remove the updated record from all the indexes
392 MutableIndexMap::iterator it
;
393 for (it
= mIndexMap
.begin(); it
!= mIndexMap
.end(); it
++)
394 it
->second
->removeRecord(aRecordNumber
);
396 // Add the original record back to all the indexes
397 for (it
= mIndexMap
.begin(); it
!= mIndexMap
.end(); it
++)
398 it
->second
->insertRecord(aRecordNumber
, anOldDbRecord
);
407 ModifiedTable::getRecord(const RecordId
&inRecordId
,
408 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes
,
410 Allocator
&inAllocator
) const
414 uint32 aRecordNumber
= inRecordId
.mRecordNumber
;
415 InsertedMap::const_iterator anIt
= mInsertedMap
.find(aRecordNumber
);
416 if (anIt
!= mInsertedMap
.end())
418 // We found the record in mInsertedMap so we use the inserted
420 const ReadSection
&aRecordSection
= *(anIt
->second
);
421 const RecordId aRecordId
= MetaRecord::unpackRecordId(aRecordSection
);
423 // Make sure the RecordNumber matches that in the RecordId we just retrived.
424 if (aRecordId
.mRecordNumber
!= aRecordNumber
)
425 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
427 if (aRecordId
.mCreateVersion
!= inRecordId
.mCreateVersion
)
428 CssmError::throwMe(CSSMERR_DL_INVALID_RECORD_UID
);
430 // XXX Figure out which value to pass for inQueryFlags (5th) argument
431 getMetaRecord().unpackRecord(aRecordSection
, inAllocator
,
432 inoutAttributes
, inoutData
, 0);
436 else if (mDeletedSet
.find(aRecordNumber
) != mDeletedSet
.end())
438 // If aRecordNumber was not in mInsertedMap but it was in
439 // mDeletedSet then it was deleted but not yet commited.
440 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND
);
445 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND
);
447 // Either this table wasn't modified yet or we didn't find aRecordNumber in
448 // mInsertedMap nor mDeletedSet so just ask mTable for it.
449 return mTable
->getRecord(inRecordId
, inoutAttributes
, inoutData
,
454 ModifiedTable::nextRecordNumber()
456 // If we still have unused free records in mTable get the next one.
458 return mTable
->popFreeList(mFreeListHead
);
460 // Bump up the mRecordNumberCount so we don't reuse the same one.
461 return mRecordNumberCount
++;
465 ModifiedTable::recordNumberCount() const
467 uint32 anOldMax
= !mTable
? 0 : mTable
->recordNumberCount() - 1;
468 uint32 anInsertedMax
= mInsertedMap
.empty() ? 0 : mInsertedMap
.rbegin()->first
;
470 DeletedSet::reverse_iterator anIt
= mDeletedSet
.rbegin();
471 DeletedSet::reverse_iterator anEnd
= mDeletedSet
.rend();
472 for (; anIt
!= anEnd
; anIt
++)
474 if (*anIt
!= anOldMax
|| anOldMax
<= anInsertedMax
)
479 return max(anOldMax
,anInsertedMax
) + 1;
483 ModifiedTable::getMetaRecord() const
485 return mNewMetaRecord
? *mNewMetaRecord
: mTable
->getMetaRecord();
488 // prepare to modify the table
491 ModifiedTable::modifyTable()
494 createMutableIndexes();
499 // create mutable indexes from the read-only indexes in the underlying table
502 ModifiedTable::createMutableIndexes()
507 Table::ConstIndexMap::const_iterator it
;
508 for (it
= mTable
->mIndexMap
.begin(); it
!= mTable
->mIndexMap
.end(); it
++) {
509 auto_ptr
<DbMutableIndex
> mutableIndex(new DbMutableIndex(*it
->second
));
510 mIndexMap
.insert(MutableIndexMap::value_type(it
->first
, mutableIndex
.get()));
511 mutableIndex
.release();
515 // find, and create if needed, an index with the given id
518 ModifiedTable::findIndex(uint32 indexId
, const MetaRecord
&metaRecord
, bool isUniqueIndex
)
520 MutableIndexMap::iterator it
= mIndexMap
.find(indexId
);
522 if (it
== mIndexMap
.end()) {
523 // create the new index
524 auto_ptr
<DbMutableIndex
> index(new DbMutableIndex(metaRecord
, indexId
, isUniqueIndex
));
525 it
= mIndexMap
.insert(MutableIndexMap::value_type(indexId
, index
.get())).first
;
533 ModifiedTable::writeIndexSection(WriteSection
&tableSection
, uint32 offset
)
535 MutableIndexMap::iterator it
;
537 tableSection
.put(Table::OffsetIndexesOffset
, offset
);
539 // leave room for the size, to be written later
540 uint32 indexSectionOffset
= offset
;
543 offset
= tableSection
.put(offset
, (uint32
)mIndexMap
.size());
545 // leave room for the array of offsets to the indexes
546 uint32 indexOffsetOffset
= offset
;
547 offset
+= mIndexMap
.size() * AtomSize
;
550 for (it
= mIndexMap
.begin(); it
!= mIndexMap
.end(); it
++) {
551 indexOffsetOffset
= tableSection
.put(indexOffsetOffset
, offset
);
552 offset
= it
->second
->writeIndex(tableSection
, offset
);
555 // write the total index section size
556 tableSection
.put(indexSectionOffset
, offset
- indexSectionOffset
);
562 ModifiedTable::writeTable(AtomicTempFile
&inAtomicTempFile
, uint32 inSectionOffset
)
564 if (mTable
&& !mIsModified
) {
565 // the table has not been modified, so we can just dump the old table
566 // section into the new database
568 const ReadSection
&tableSection
= mTable
->getTableSection();
569 uint32 tableSize
= tableSection
.at(Table::OffsetSize
);
571 inAtomicTempFile
.write(AtomicFile::FromStart
, inSectionOffset
,
572 tableSection
.range(Range(0, tableSize
)), tableSize
);
574 return inSectionOffset
+ tableSize
;
577 // We should have an old mTable or a mNewMetaRecord but not both.
578 assert(mTable
!= nil
^ mNewMetaRecord
!= nil
);
579 const MetaRecord
&aNewMetaRecord
= getMetaRecord();
581 uint32 aRecordsCount
= 0;
582 uint32 aRecordNumbersCount
= recordNumberCount();
583 uint32 aRecordsOffset
= Table::OffsetRecordNumbers
+ AtomSize
* aRecordNumbersCount
;
584 WriteSection
aTableSection(Allocator::standard(), aRecordsOffset
);
585 aTableSection
.size(aRecordsOffset
);
586 aTableSection
.put(Table::OffsetId
, aNewMetaRecord
.dataRecordType());
587 aTableSection
.put(Table::OffsetRecords
, aRecordsOffset
);
588 aTableSection
.put(Table::OffsetRecordNumbersCount
, aRecordNumbersCount
);
590 uint32 anOffset
= inSectionOffset
+ aRecordsOffset
;
594 // XXX Handle schema changes in the future.
595 assert(mNewMetaRecord
== nil
);
597 // We have a modified old table so copy all non deleted records
598 // The code below is rather elaborate, but this is because it attempts
599 // to copy large ranges of non deleted records with single calls
600 // to AtomicFile::write()
601 uint32 anOldRecordsCount
= mTable
->getRecordsCount();
602 ReadSection aRecordsSection
= mTable
->getRecordsSection();
603 uint32 aReadOffset
= 0; // Offset of current record
604 uint32 aWriteOffset
= aRecordsOffset
; // Offset for current write record
605 uint32 aBlockStart
= aReadOffset
; // Starting point for read
606 uint32 aBlockSize
= 0; // Size of block to read
607 for (uint32 aRecord
= 0; aRecord
< anOldRecordsCount
; aRecord
++)
609 ReadSection aRecordSection
= MetaRecord::readSection(aRecordsSection
, aReadOffset
);
610 uint32 aRecordNumber
= MetaRecord::unpackRecordNumber(aRecordSection
);
611 uint32 aRecordSize
= aRecordSection
.size();
612 aReadOffset
+= aRecordSize
;
613 if (mDeletedSet
.find(aRecordNumber
) == mDeletedSet
.end())
615 // This record has not been deleted. Register the offset
616 // at which it will be in the new file in aTableSection.
617 aTableSection
.put(Table::OffsetRecordNumbers
618 + AtomSize
* aRecordNumber
,
620 aWriteOffset
+= aRecordSize
;
621 aBlockSize
+= aRecordSize
;
623 // XXX update all indexes being created.
627 // The current record has been deleted. Copy all records up
628 // to but not including the current one to the new file.
631 inAtomicTempFile
.write(AtomicFile::FromStart
, anOffset
,
632 aRecordsSection
.range(Range(aBlockStart
,
635 anOffset
+= aBlockSize
;
638 // Set the start of the next block to the start of the next
639 // record, and the size of the block to 0.
640 aBlockStart
= aReadOffset
;
642 } // if (mDeletedSet..)
643 } // for (aRecord...)
645 // Copy all records that have not yet been copied to the new file.
648 inAtomicTempFile
.write(AtomicFile::FromStart
, anOffset
,
649 aRecordsSection
.range(Range(aBlockStart
,
652 anOffset
+= aBlockSize
;
656 // Now add all inserted records to the table.
657 InsertedMap::const_iterator anIt
= mInsertedMap
.begin();
658 InsertedMap::const_iterator anEnd
= mInsertedMap
.end();
659 // Iterate over all inserted objects.
660 for (; anIt
!= anEnd
; anIt
++)
662 // Write out each inserted/modified record
663 const WriteSection
&aRecord
= *anIt
->second
;
664 uint32 aRecordNumber
= anIt
->first
;
665 // Put offset relative to start of this table in recordNumber array.
666 aTableSection
.put(Table::OffsetRecordNumbers
+ AtomSize
* aRecordNumber
,
667 anOffset
- inSectionOffset
);
668 inAtomicTempFile
.write(AtomicFile::FromStart
, anOffset
,
669 aRecord
.address(), aRecord
.size());
670 anOffset
+= aRecord
.size();
672 // XXX update all indexes being created.
675 // Reconstruct the freelist (this is O(N) where N is the number of recordNumbers)
676 // We could implement it faster by using the old freelist and skipping the records
677 // that have been inserted. However building the freelist for the newly used
678 // recordNumbers (not in mTable) would look like the code below anyway (starting
679 // from mTable->recordNumberCount()).
680 // The first part of this would be O(M Log(N)) (where M is the old number of
681 // free records, and N is the number of newly inserted records)
682 // The second part would be O(N) where N is the currently max RecordNumber
683 // in use - the old max RecordNumber in use.
684 uint32 aFreeListHead
= 0; // Link to previous free record
685 for (uint32 aRecordNumber
= 0; aRecordNumber
< aRecordNumbersCount
; aRecordNumber
++)
687 // Make the freelist a list of all records with 0 offset (non existing).
688 if (!aTableSection
.at(Table::OffsetRecordNumbers
+ AtomSize
* aRecordNumber
))
690 aTableSection
.put(Table::OffsetRecordNumbers
691 + AtomSize
* aRecordNumber
,
693 // Make aFreeListHead point to the previous free recordNumber slot in the table.
694 aFreeListHead
= (Table::OffsetRecordNumbers
+ AtomSize
* aRecordNumber
) | 1;
697 aTableSection
.put(Table::OffsetFreeListHead
, aFreeListHead
);
699 anOffset
-= inSectionOffset
;
701 // Write out indexes, which are part of the table section
704 uint32 indexOffset
= anOffset
;
705 anOffset
= writeIndexSection(aTableSection
, anOffset
);
706 inAtomicTempFile
.write(AtomicFile::FromStart
, inSectionOffset
+ indexOffset
,
707 aTableSection
.address() + indexOffset
, anOffset
- indexOffset
);
710 // Set the section size and recordCount.
711 aTableSection
.put(Table::OffsetSize
, anOffset
);
712 aTableSection
.put(Table::OffsetRecordsCount
, aRecordsCount
);
714 // Write out aTableSection header.
715 inAtomicTempFile
.write(AtomicFile::FromStart
, inSectionOffset
,
716 aTableSection
.address(), aTableSection
.size());
718 return anOffset
+ inSectionOffset
;
722 #pragma clang diagnostic push
723 #pragma clang diagnostic ignored "-Wunused-const-variable"
729 // Attribute definitions
731 static const CSSM_DB_ATTRIBUTE_INFO RelationID
=
733 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
734 {(char*) "RelationID"},
735 CSSM_DB_ATTRIBUTE_FORMAT_UINT32
737 static const CSSM_DB_ATTRIBUTE_INFO RelationName
=
739 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
740 {(char*) "RelationName"},
741 CSSM_DB_ATTRIBUTE_FORMAT_STRING
743 static const CSSM_DB_ATTRIBUTE_INFO AttributeID
=
745 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
746 {(char*) "AttributeID"},
747 CSSM_DB_ATTRIBUTE_FORMAT_UINT32
749 static const CSSM_DB_ATTRIBUTE_INFO AttributeNameFormat
=
751 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
752 {(char*) "AttributeNameFormat"},
753 CSSM_DB_ATTRIBUTE_FORMAT_UINT32
755 static const CSSM_DB_ATTRIBUTE_INFO AttributeName
=
757 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
758 {(char*) "AttributeName"},
759 CSSM_DB_ATTRIBUTE_FORMAT_STRING
761 static const CSSM_DB_ATTRIBUTE_INFO AttributeNameID
=
763 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
764 {(char*) "AttributeNameID"},
765 CSSM_DB_ATTRIBUTE_FORMAT_BLOB
767 static const CSSM_DB_ATTRIBUTE_INFO AttributeFormat
=
769 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
770 {(char*) "AttributeFormat"},
771 CSSM_DB_ATTRIBUTE_FORMAT_UINT32
773 static const CSSM_DB_ATTRIBUTE_INFO IndexID
=
775 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
777 CSSM_DB_ATTRIBUTE_FORMAT_UINT32
779 static const CSSM_DB_ATTRIBUTE_INFO IndexType
=
781 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
782 {(char*) "IndexType"},
783 CSSM_DB_ATTRIBUTE_FORMAT_UINT32
785 static const CSSM_DB_ATTRIBUTE_INFO IndexedDataLocation
=
787 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
788 {(char*) "IndexedDataLocation"},
789 CSSM_DB_ATTRIBUTE_FORMAT_UINT32
791 static const CSSM_DB_ATTRIBUTE_INFO ModuleID
=
793 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
794 {(char*) "ModuleID"},
795 CSSM_DB_ATTRIBUTE_FORMAT_BLOB
797 static const CSSM_DB_ATTRIBUTE_INFO AddinVersion
=
799 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
800 {(char*) "AddinVersion"},
801 CSSM_DB_ATTRIBUTE_FORMAT_STRING
803 static const CSSM_DB_ATTRIBUTE_INFO SSID
=
805 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
807 CSSM_DB_ATTRIBUTE_FORMAT_UINT32
809 static const CSSM_DB_ATTRIBUTE_INFO SubserviceType
=
811 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
812 {(char*) "SubserviceType"},
813 CSSM_DB_ATTRIBUTE_FORMAT_UINT32
816 #define ATTRIBUTE(type, name) \
817 { CSSM_DB_ATTRIBUTE_NAME_AS_STRING, { (char*) #name }, CSSM_DB_ATTRIBUTE_FORMAT_ ## type }
819 static const CSSM_DB_ATTRIBUTE_INFO AttrSchemaRelations
[] =
821 //RelationID, RelationName
822 ATTRIBUTE(UINT32
, RelationID
),
823 ATTRIBUTE(STRING
, RelationName
)
826 static const CSSM_DB_ATTRIBUTE_INFO AttrSchemaAttributes
[] =
828 //RelationID, AttributeID,
829 //AttributeNameFormat, AttributeName, AttributeNameID,
831 ATTRIBUTE(UINT32
, RelationID
),
832 ATTRIBUTE(UINT32
, AttributeID
),
833 ATTRIBUTE(UINT32
, AttributeNameFormat
),
834 ATTRIBUTE(STRING
, AttributeName
),
835 ATTRIBUTE(BLOB
, AttributeNameID
),
836 ATTRIBUTE(UINT32
, AttributeFormat
)
839 static const CSSM_DB_ATTRIBUTE_INFO AttrSchemaIndexes
[] =
841 ATTRIBUTE(UINT32
, RelationID
),
842 ATTRIBUTE(UINT32
, IndexID
),
843 ATTRIBUTE(UINT32
, AttributeID
),
844 ATTRIBUTE(UINT32
, IndexType
),
845 ATTRIBUTE(UINT32
, IndexedDataLocation
)
846 //RelationID, IndexID, AttributeID,
847 //IndexType, IndexedDataLocation
850 static const CSSM_DB_ATTRIBUTE_INFO AttrSchemaParsingModule
[] =
852 ATTRIBUTE(UINT32
, RelationID
),
853 ATTRIBUTE(UINT32
, AttributeID
),
854 ATTRIBUTE(BLOB
, ModuleID
),
855 ATTRIBUTE(STRING
, AddinVersion
),
856 ATTRIBUTE(UINT32
, SSID
),
857 ATTRIBUTE(UINT32
, SubserviceType
)
858 //RelationID, AttributeID,
859 //ModuleID, AddinVersion, SSID, SubserviceType
863 #pragma clang diagnostic pop
868 DbVersion::DbVersion(const AppleDatabase
&db
, const RefPointer
<AtomicBufferedFile
> &inAtomicBufferedFile
) :
869 mDatabase(reinterpret_cast<const uint8
*>(NULL
), 0),
871 mBufferedFile(inAtomicBufferedFile
)
873 off_t aLength
= mBufferedFile
->length();
875 const uint8
*ptr
= mBufferedFile
->read(0, aLength
, bytesRead
);
876 mBufferedFile
->close();
877 mDatabase
= ReadSection(ptr
, (size_t)bytesRead
);
881 DbVersion::~DbVersion()
885 for_each_map_delete(mTableMap
.begin(), mTableMap
.end());
895 // This is the oposite of DbModifier::commit()
896 mVersionId
= mDatabase
[mDatabase
.size() - AtomSize
];
898 const ReadSection aHeaderSection
= mDatabase
.subsection(HeaderOffset
,
900 if (aHeaderSection
.at(OffsetMagic
) != HeaderMagic
)
901 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
903 // We currently only support one version. If we support additional
904 // file format versions in the future fix this.
905 uint32 aVersion
= aHeaderSection
.at(OffsetVersion
);
906 if (aVersion
!= HeaderVersion
)
907 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
909 //const ReadSection anAuthSection =
910 // mDatabase.subsection(HeaderOffset + aHeaderSection.at(OffsetAuthOffset));
911 // XXX Do something with anAuthSection.
913 uint32 aSchemaOffset
= aHeaderSection
.at(OffsetSchemaOffset
);
914 const ReadSection aSchemaSection
=
915 mDatabase
.subsection(HeaderOffset
+ aSchemaOffset
);
917 uint32 aSchemaSize
= aSchemaSection
[OffsetSchemaSize
];
918 // Make sure that the given range exists.
919 aSchemaSection
.subsection(0, aSchemaSize
);
920 uint32 aTableCount
= aSchemaSection
[OffsetTablesCount
];
922 // Assert that the size of this section is big enough.
923 if (aSchemaSize
< OffsetTables
+ AtomSize
* aTableCount
)
924 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
926 for (uint32 aTableNumber
= 0; aTableNumber
< aTableCount
;
929 uint32 aTableOffset
= aSchemaSection
.at(OffsetTables
+ AtomSize
931 // XXX Set the size boundary on aTableSection.
932 const ReadSection aTableSection
=
933 aSchemaSection
.subsection(aTableOffset
);
934 auto_ptr
<Table
> aTable(new Table(aTableSection
));
935 Table::Id aTableId
= aTable
->getMetaRecord().dataRecordType();
936 mTableMap
.insert(TableMap::value_type(aTableId
, aTable
.get()));
940 // Fill in the schema for the meta tables.
942 findTable(mDb
.schemaRelations
.DataRecordType
).getMetaRecord().
943 setRecordAttributeInfo(mDb
.schemaRelations
);
944 findTable(mDb
.schemaIndexes
.DataRecordType
).getMetaRecord().
945 setRecordAttributeInfo(mDb
.schemaIndexes
);
946 findTable(mDb
.schemaParsingModule
.DataRecordType
).getMetaRecord().
947 setRecordAttributeInfo(mDb
.schemaParsingModule
);
949 // OK, we have created all the tables in the tableMap. Now
950 // lets read the schema and proccess it accordingly.
951 // Iterate over all schema records.
952 Table
&aTable
= findTable(mDb
.schemaAttributes
.DataRecordType
);
953 aTable
.getMetaRecord().setRecordAttributeInfo(mDb
.schemaAttributes
);
954 uint32 aRecordsCount
= aTable
.getRecordsCount();
955 ReadSection aRecordsSection
= aTable
.getRecordsSection();
956 uint32 aReadOffset
= 0;
957 const MetaRecord
&aMetaRecord
= aTable
.getMetaRecord();
959 CSSM_DB_ATTRIBUTE_DATA aRelationIDData
=
965 CSSM_DB_ATTRIBUTE_DATA aAttributeIDData
=
971 CSSM_DB_ATTRIBUTE_DATA aAttributeNameFormatData
=
977 CSSM_DB_ATTRIBUTE_DATA aAttributeNameData
=
983 CSSM_DB_ATTRIBUTE_DATA aAttributeNameIDData
=
989 CSSM_DB_ATTRIBUTE_DATA aAttributeFormatData
=
995 CSSM_DB_ATTRIBUTE_DATA aRecordAttributes
[] =
999 aAttributeNameFormatData
,
1001 aAttributeNameIDData
,
1002 aAttributeFormatData
1004 CSSM_DB_RECORD_ATTRIBUTE_DATA aRecordAttributeData
=
1006 aMetaRecord
.dataRecordType(),
1008 sizeof(aRecordAttributes
) / sizeof(CSSM_DB_ATTRIBUTE_DATA
),
1011 CssmDbRecordAttributeData
&aRecordData
= CssmDbRecordAttributeData::overlay(aRecordAttributeData
);
1013 TrackingAllocator
recordAllocator(Allocator::standard());
1014 for (uint32 aRecord
= 0; aRecord
!= aRecordsCount
; aRecord
++)
1016 ReadSection aRecordSection
= MetaRecord::readSection(aRecordsSection
, aReadOffset
);
1017 uint32 aRecordSize
= aRecordSection
.size();
1018 aReadOffset
+= aRecordSize
;
1019 aMetaRecord
.unpackRecord(aRecordSection
, recordAllocator
,
1020 &aRecordAttributeData
, NULL
, 0);
1021 // Create the attribute coresponding to this entry
1022 if (aRecordData
[0].size() != 1 || aRecordData
[0].format() != CSSM_DB_ATTRIBUTE_FORMAT_UINT32
)
1023 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
1024 uint32 aRelationId
= aRecordData
[0];
1026 // Skip the schema relations for the meta tables themselves.
1027 // FIXME: this hard-wires the meta-table relation IDs to be
1028 // within {CSSM_DB_RECORDTYPE_SCHEMA_START...
1029 // CSSM_DB_RECORDTYPE_SCHEMA_END} (which is {0..4}).
1030 // Bogus - the MDS schema relation IDs start at
1031 // CSSM_DB_RELATIONID_MDS_START which is 0x40000000.
1032 // Ref. Radar 2817921.
1033 if (CSSM_DB_RECORDTYPE_SCHEMA_START
<= aRelationId
&& aRelationId
< CSSM_DB_RECORDTYPE_SCHEMA_END
)
1036 // Get the MetaRecord corresponding to the specified RelationId
1037 MetaRecord
&aMetaRecord
= findTable(aRelationId
).getMetaRecord();
1039 if (aRecordData
[1].size() != 1
1040 || aRecordData
[1].format() != CSSM_DB_ATTRIBUTE_FORMAT_UINT32
1041 || aRecordData
[2].size() != 1
1042 || aRecordData
[2].format() != CSSM_DB_ATTRIBUTE_FORMAT_UINT32
1043 || aRecordData
[5].size() != 1
1044 || aRecordData
[5].format() != CSSM_DB_ATTRIBUTE_FORMAT_UINT32
)
1045 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
1047 uint32 anAttributeId
= aRecordData
[1];
1048 uint32 anAttributeNameFormat
= aRecordData
[2];
1049 uint32 anAttributeFormat
= aRecordData
[5];
1050 auto_ptr
<string
> aName
;
1051 const CssmData
*aNameID
= NULL
;
1053 if (aRecordData
[3].size() == 1)
1055 if (aRecordData
[3].format() != CSSM_DB_ATTRIBUTE_FORMAT_STRING
)
1056 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
1058 auto_ptr
<string
> aName2(new string(static_cast<string
>(aRecordData
[3])));
1062 if (aRecordData
[4].size() == 1)
1064 if (aRecordData
[4].format() != CSSM_DB_ATTRIBUTE_FORMAT_BLOB
)
1065 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
1067 // @@@ Invoking conversion operator to CssmData & on aRecordData[4]
1068 // And taking address of result.
1069 aNameID
= &static_cast<const CssmData
&>(aRecordData
[4]);
1072 // Make sure that the attribute specified by anAttributeNameFormat is present.
1073 switch (anAttributeNameFormat
)
1075 case CSSM_DB_ATTRIBUTE_NAME_AS_STRING
:
1076 if (aRecordData
[3].size() != 1)
1077 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
1079 case CSSM_DB_ATTRIBUTE_NAME_AS_OID
:
1080 if (aRecordData
[4].size() != 1)
1081 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
1083 case CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER
:
1086 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
1089 // Create the attribute
1090 aMetaRecord
.createAttribute(aName
.get(), aNameID
, anAttributeId
, anAttributeFormat
);
1093 // initialize the indexes associated with each table
1095 TableMap::iterator it
;
1096 for (it
= mTableMap
.begin(); it
!= mTableMap
.end(); it
++)
1097 it
->second
->readIndexSection();
1102 for_each_map_delete(mTableMap
.begin(), mTableMap
.end());
1109 DbVersion::getRecord(Table::Id inTableId
, const RecordId
&inRecordId
,
1110 CSSM_DB_RECORD_ATTRIBUTE_DATA
*inoutAttributes
,
1111 CssmData
*inoutData
,
1112 Allocator
&inAllocator
) const
1114 return findTable(inTableId
).getRecord(inRecordId
, inoutAttributes
,
1115 inoutData
, inAllocator
);
1119 DbVersion::createCursor(const CSSM_QUERY
*inQuery
) const
1121 // XXX We should add support for these special query types
1122 // By Creating a Cursor that iterates over multiple tables
1123 if (!inQuery
|| inQuery
->RecordType
== CSSM_DL_DB_RECORD_ANY
1124 || inQuery
->RecordType
== CSSM_DL_DB_RECORD_ALL_KEYS
)
1126 return new MultiCursor(inQuery
, *this);
1129 return findTable(inQuery
->RecordType
).createCursor(inQuery
, *this);
1132 bool DbVersion::hasTable(Table::Id inTableId
) const
1134 TableMap::const_iterator it
= mTableMap
.find(inTableId
);
1135 return it
!= mTableMap
.end();
1139 DbVersion::findTable(Table::Id inTableId
) const
1141 TableMap::const_iterator it
= mTableMap
.find(inTableId
);
1142 if (it
== mTableMap
.end())
1143 CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE
);
1148 DbVersion::findTable(Table::Id inTableId
)
1150 TableMap::iterator it
= mTableMap
.find(inTableId
);
1151 if (it
== mTableMap
.end())
1152 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
1157 // Cursor implemetation
1163 Cursor::Cursor(const DbVersion
&inDbVersion
) : mDbVersion(&inDbVersion
)
1172 Cursor::next(Table::Id
&outTableId
,
1173 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR outAttributes
,
1175 Allocator
&inAllocator
,
1182 // LinearCursor implemetation
1184 LinearCursor::LinearCursor(const CSSM_QUERY
*inQuery
, const DbVersion
&inDbVersion
,
1185 const Table
&inTable
) :
1186 Cursor(inDbVersion
),
1187 mRecordsCount(inTable
.getRecordsCount()),
1189 mRecordsSection(inTable
.getRecordsSection()),
1191 mMetaRecord(inTable
.getMetaRecord())
1195 mConjunctive
= inQuery
->Conjunctive
;
1196 mQueryFlags
= inQuery
->QueryFlags
;
1197 // XXX Do something with inQuery->QueryLimits?
1198 uint32 aPredicatesCount
= inQuery
->NumSelectionPredicates
;
1199 mPredicates
.resize(aPredicatesCount
);
1202 for (uint32 anIndex
= 0; anIndex
< aPredicatesCount
; anIndex
++)
1204 CSSM_SELECTION_PREDICATE
&aPredicate
= inQuery
->SelectionPredicate
[anIndex
];
1205 mPredicates
[anIndex
] = new SelectionPredicate(mMetaRecord
, aPredicate
);
1210 for_each_delete(mPredicates
.begin(), mPredicates
.end());
1216 LinearCursor::~LinearCursor()
1218 for_each_delete(mPredicates
.begin(), mPredicates
.end());
1222 LinearCursor::next(Table::Id
&outTableId
,
1223 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes
,
1224 CssmData
*inoutData
, Allocator
&inAllocator
, RecordId
&recordId
)
1226 while (mRecord
++ < mRecordsCount
)
1228 ReadSection aRecordSection
= MetaRecord::readSection(mRecordsSection
, mReadOffset
);
1229 uint32 aRecordSize
= aRecordSection
.size();
1230 mReadOffset
+= aRecordSize
;
1232 PredicateVector::const_iterator anIt
= mPredicates
.begin();
1233 PredicateVector::const_iterator anEnd
= mPredicates
.end();
1237 // If there are no predicates we have a match.
1240 else if (mConjunctive
== CSSM_DB_OR
)
1242 // If mConjunctive is OR, the first predicate that returns
1243 // true indicates a match. Dropthough means no match
1245 for (; anIt
!= anEnd
; anIt
++)
1247 if ((*anIt
)->evaluate(aRecordSection
))
1254 else if (mConjunctive
== CSSM_DB_AND
|| mConjunctive
== CSSM_DB_NONE
)
1256 // If mConjunctive is AND (or NONE), the first predicate that returns
1257 // false indicates a mismatch. Dropthough means a match
1259 for (; anIt
!= anEnd
; anIt
++)
1261 if (!(*anIt
)->evaluate(aRecordSection
))
1270 // XXX Should be CSSMERR_DL_INVALID_QUERY (or CSSMERR_DL_INVALID_CONJUNTIVE).
1271 CssmError::throwMe(CSSMERR_DL_UNSUPPORTED_QUERY
);
1276 // Get the actual record.
1277 mMetaRecord
.unpackRecord(aRecordSection
, inAllocator
,
1278 inoutAttributes
, inoutData
,
1280 outTableId
= mMetaRecord
.dataRecordType();
1281 recordId
= MetaRecord::unpackRecordId(aRecordSection
);
1293 IndexCursor::IndexCursor(DbQueryKey
*queryKey
, const DbVersion
&inDbVersion
,
1294 const Table
&table
, const DbConstIndex
*index
) :
1295 Cursor(inDbVersion
), mQueryKey(queryKey
), mTable(table
), mIndex(index
)
1297 index
->performQuery(*queryKey
, mBegin
, mEnd
);
1300 IndexCursor::~IndexCursor()
1302 // the query key will be deleted automatically, since it's an auto_ptr
1306 IndexCursor::next(Table::Id
&outTableId
,
1307 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR outAttributes
,
1309 Allocator
&inAllocator
, RecordId
&recordId
)
1314 ReadSection rs
= mIndex
->getRecordSection(mBegin
++);
1315 const MetaRecord
&metaRecord
= mTable
.getMetaRecord();
1317 outTableId
= metaRecord
.dataRecordType();
1318 metaRecord
.unpackRecord(rs
, inAllocator
, outAttributes
, outData
, 0);
1320 recordId
= MetaRecord::unpackRecordId(rs
);
1327 MultiCursor::MultiCursor(const CSSM_QUERY
*inQuery
, const DbVersion
&inDbVersion
) :
1328 Cursor(inDbVersion
), mTableIterator(inDbVersion
.begin())
1331 mQuery
.reset(new CssmAutoQuery(*inQuery
));
1334 mQuery
.reset(new CssmAutoQuery());
1335 mQuery
->recordType(CSSM_DL_DB_RECORD_ANY
);
1339 MultiCursor::~MultiCursor()
1344 MultiCursor::next(Table::Id
&outTableId
,
1345 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes
,
1346 CssmData
*inoutData
, Allocator
&inAllocator
, RecordId
&recordId
)
1352 if (mTableIterator
== mDbVersion
->end())
1355 const Table
&aTable
= *mTableIterator
++;
1356 if (!aTable
.matchesTableId(mQuery
->recordType()))
1359 mCursor
.reset(aTable
.createCursor(mQuery
.get(), *mDbVersion
));
1362 if (mCursor
->next(outTableId
, inoutAttributes
, inoutData
, inAllocator
, recordId
))
1365 mCursor
.reset(NULL
);
1373 DbModifier::DbModifier(AtomicFile
&inAtomicFile
, const AppleDatabase
&db
) :
1376 mAtomicFile(inAtomicFile
),
1381 DbModifier::~DbModifier()
1385 for_each_map_delete(mModifiedTableMap
.begin(), mModifiedTableMap
.end());
1386 // mAtomicTempFile will do automatic rollback on destruction.
1391 const RefPointer
<const DbVersion
>
1392 DbModifier::getDbVersion(bool force
)
1394 StLock
<Mutex
> _(mDbVersionLock
);
1396 /* Initialize the shared memory file change mechanism */
1397 pthread_once(&gCommonInitMutex
, initCommon
);
1399 /* If we don't have a mDbVersion yet, or we are force to re-read the file
1400 before a write transaction, or we have received any notifications after
1401 the last time we read the file, or more than kForceReReadTime seconds
1402 have passed since the last time we read the file, we open the file and
1403 check if it has changed. */
1407 mNotifyCount
!= *gSegment
||
1408 CFAbsoluteTimeGetCurrent() > mDbLastRead
+ kForceReReadTime
)
1410 RefPointer
<AtomicBufferedFile
> atomicBufferedFile(mAtomicFile
.read());
1411 off_t length
= atomicBufferedFile
->open();
1412 /* Record the number of notifications we've seen and when we last
1414 if (gSegment
!= NULL
)
1416 mNotifyCount
= *gSegment
;
1419 mDbLastRead
= CFAbsoluteTimeGetCurrent();
1421 /* If we already have a mDbVersion, let's check if we can reuse it. */
1424 if (length
< AtomSize
)
1425 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
1427 off_t bytesRead
= 0;
1428 const uint8
*ptr
= atomicBufferedFile
->read(length
- AtomSize
,
1429 AtomSize
, bytesRead
);
1430 ReadSection
aVersionSection(ptr
, (size_t)bytesRead
);
1431 uint32 aVersionId
= aVersionSection
[0];
1433 /* If the version stamp hasn't changed the old mDbVersion is still
1435 if (aVersionId
== mDbVersion
->getVersionId())
1439 mDbVersion
= new DbVersion(mDb
, atomicBufferedFile
);
1446 DbModifier::createDatabase(const CSSM_DBINFO
&inDbInfo
,
1447 const CSSM_ACL_ENTRY_INPUT
*inInitialAclEntry
,
1450 // XXX This needs better locking. There is a possible race condition between
1451 // two concurrent creators. Or a writer/creator or a close/create etc.
1452 if (mAtomicTempFile
|| !mModifiedTableMap
.empty())
1453 CssmError::throwMe(CSSMERR_DL_DATASTORE_ALREADY_EXISTS
);
1455 mAtomicTempFile
= mAtomicFile
.create(mode
);
1456 // Set mVersionId to one since this is the first version of the database.
1459 // we need to create the meta tables first, because inserting tables
1460 // (including the meta tables themselves) relies on them being there
1461 createTable(new MetaRecord(mDb
.schemaRelations
));
1462 createTable(new MetaRecord(mDb
.schemaAttributes
));
1463 createTable(new MetaRecord(mDb
.schemaIndexes
));
1464 createTable(new MetaRecord(mDb
.schemaParsingModule
));
1466 // now add the meta-tables' schema to the meta tables themselves
1467 insertTableSchema(mDb
.schemaRelations
);
1468 insertTableSchema(mDb
.schemaAttributes
);
1469 insertTableSchema(mDb
.schemaIndexes
);
1470 insertTableSchema(mDb
.schemaParsingModule
);
1472 if (inInitialAclEntry
!= NULL
)
1474 //createACL(*inInitialAclEntry);
1477 if (inDbInfo
.NumberOfRecordTypes
== 0)
1479 if (inDbInfo
.RecordAttributeNames
== NULL
)
1480 CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE
);
1481 if (inDbInfo
.RecordIndexes
== NULL
)
1482 CssmError::throwMe(CSSMERR_DL_INVALID_RECORD_INDEX
);
1483 if (inDbInfo
.DefaultParsingModules
== NULL
)
1484 CssmError::throwMe(CSSMERR_DL_INVALID_PARSING_MODULE
);
1486 for (uint32 anIndex
= 0; anIndex
< inDbInfo
.NumberOfRecordTypes
; anIndex
++)
1488 insertTable(CssmDbRecordAttributeInfo::overlay(inDbInfo
.RecordAttributeNames
[anIndex
]),
1489 &inDbInfo
.RecordIndexes
[anIndex
],
1490 &inDbInfo
.DefaultParsingModules
[anIndex
]);
1494 void DbModifier::openDatabase()
1496 // No need to do anything on open if we are already writing the database.
1497 if (!mAtomicTempFile
)
1498 getDbVersion(false);
1501 void DbModifier::closeDatabase()
1503 commit(); // XXX Requires write lock.
1504 StLock
<Mutex
> _(mDbVersionLock
);
1508 void DbModifier::deleteDatabase()
1510 bool isDirty
= mAtomicTempFile
;
1511 rollback(); // XXX Requires write lock.
1512 StLock
<Mutex
> _(mDbVersionLock
);
1514 // Clean up mModifiedTableMap in case this object gets reused again for
1516 for_each_map_delete(mModifiedTableMap
.begin(), mModifiedTableMap
.end());
1517 mModifiedTableMap
.clear();
1519 // If the database was dirty and we had no mDbVersion yet then rollback()
1520 // would have deleted the db.
1521 if (!isDirty
|| mDbVersion
)
1524 mAtomicFile
.performDelete();
1529 DbModifier::modifyDatabase()
1531 if (mAtomicTempFile
)
1536 mAtomicTempFile
= mAtomicFile
.write();
1537 // Now we are holding the write lock make sure we get the latest greatest version of the db.
1538 // Also set mVersionId to one more that that of the old database.
1539 mVersionId
= getDbVersion(true)->getVersionId() + 1;
1541 // Never make a database with mVersionId 0 since it makes bad things happen to Jaguar and older systems
1542 if (mVersionId
== 0)
1545 // Remove all old modified tables
1546 for_each_map_delete(mModifiedTableMap
.begin(), mModifiedTableMap
.end());
1547 mModifiedTableMap
.clear();
1549 // Setup the new tables
1550 DbVersion::TableMap::const_iterator anIt
=
1551 mDbVersion
->mTableMap
.begin();
1552 DbVersion::TableMap::const_iterator anEnd
=
1553 mDbVersion
->mTableMap
.end();
1554 for (; anIt
!= anEnd
; ++anIt
)
1556 auto_ptr
<ModifiedTable
> aTable(new ModifiedTable(anIt
->second
));
1557 mModifiedTableMap
.insert(ModifiedTableMap::value_type(anIt
->first
,
1564 for_each_map_delete(mModifiedTableMap
.begin(), mModifiedTableMap
.end());
1565 mModifiedTableMap
.clear();
1572 DbModifier::deleteRecord(Table::Id inTableId
, const RecordId
&inRecordId
)
1575 findTable(inTableId
).deleteRecord(inRecordId
);
1579 DbModifier::insertRecord(Table::Id inTableId
,
1580 const CSSM_DB_RECORD_ATTRIBUTE_DATA
*inAttributes
,
1581 const CssmData
*inData
)
1584 return findTable(inTableId
).insertRecord(mVersionId
, inAttributes
, inData
);
1588 DbModifier::updateRecord(Table::Id inTableId
, const RecordId
&inRecordId
,
1589 const CSSM_DB_RECORD_ATTRIBUTE_DATA
*inAttributes
,
1590 const CssmData
*inData
,
1591 CSSM_DB_MODIFY_MODE inModifyMode
)
1594 return findTable(inTableId
).updateRecord(inRecordId
, inAttributes
, inData
, inModifyMode
);
1597 // Create a table associated with a given metarecord, and add the table
1601 DbModifier::createTable(MetaRecord
*inMetaRecord
)
1603 auto_ptr
<MetaRecord
> aMetaRecord(inMetaRecord
);
1604 auto_ptr
<ModifiedTable
> aModifiedTable(new ModifiedTable(inMetaRecord
));
1605 // Now that aModifiedTable is fully constructed it owns inMetaRecord
1606 aMetaRecord
.release();
1608 if (!mModifiedTableMap
.insert
1609 (ModifiedTableMap::value_type(inMetaRecord
->dataRecordType(),
1610 aModifiedTable
.get())).second
)
1612 // XXX Should be CSSMERR_DL_DUPLICATE_RECORDTYPE. Since that
1613 // doesn't exist we report that the metatable's unique index would
1614 // no longer be valid
1615 CssmError::throwMe(CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA
);
1618 return aModifiedTable
.release();
1622 DbModifier::deleteTable(Table::Id inTableId
)
1625 // Can't delete schema tables.
1626 if (CSSM_DB_RECORDTYPE_SCHEMA_START
<= inTableId
1627 && inTableId
< CSSM_DB_RECORDTYPE_SCHEMA_END
)
1628 CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE
);
1630 // Find the ModifiedTable and delete it
1631 ModifiedTableMap::iterator it
= mModifiedTableMap
.find(inTableId
);
1632 if (it
== mModifiedTableMap
.end())
1633 CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE
);
1636 mModifiedTableMap
.erase(it
);
1640 DbModifier::writeAuthSection(uint32 inSectionOffset
)
1642 WriteSection anAuthSection
;
1644 // XXX Put real data into the authsection.
1645 uint32 anOffset
= anAuthSection
.put(0, 0);
1646 anAuthSection
.size(anOffset
);
1648 mAtomicTempFile
->write(AtomicFile::FromStart
, inSectionOffset
,
1649 anAuthSection
.address(), anAuthSection
.size());
1650 return inSectionOffset
+ anOffset
;
1654 DbModifier::writeSchemaSection(uint32 inSectionOffset
)
1656 uint32 aTableCount
= (uint32
) mModifiedTableMap
.size();
1657 WriteSection
aTableSection(Allocator::standard(),
1658 OffsetTables
+ AtomSize
* aTableCount
);
1659 // Set aTableSection to the correct size.
1660 aTableSection
.size(OffsetTables
+ AtomSize
* aTableCount
);
1661 aTableSection
.put(OffsetTablesCount
, aTableCount
);
1663 uint32 anOffset
= inSectionOffset
+ OffsetTables
+ AtomSize
* aTableCount
;
1664 ModifiedTableMap::const_iterator anIt
= mModifiedTableMap
.begin();
1665 ModifiedTableMap::const_iterator anEnd
= mModifiedTableMap
.end();
1666 for (uint32 aTableNumber
= 0; anIt
!= anEnd
; anIt
++, aTableNumber
++)
1668 // Put the offset to the current table relative to the start of
1669 // this section into the tables array
1670 aTableSection
.put(OffsetTables
+ AtomSize
* aTableNumber
,
1671 anOffset
- inSectionOffset
);
1672 anOffset
= anIt
->second
->writeTable(*mAtomicTempFile
, anOffset
);
1675 aTableSection
.put(OffsetSchemaSize
, anOffset
- inSectionOffset
);
1676 mAtomicTempFile
->write(AtomicFile::FromStart
, inSectionOffset
,
1677 aTableSection
.address(), aTableSection
.size());
1683 DbModifier::commit()
1685 if (!mAtomicTempFile
)
1689 WriteSection
aHeaderSection(Allocator::standard(), size_t(HeaderSize
));
1690 // Set aHeaderSection to the correct size.
1691 aHeaderSection
.size(HeaderSize
);
1693 // Start writing sections after the header
1694 uint32 anOffset
= HeaderOffset
+ HeaderSize
;
1696 // Write auth section
1697 aHeaderSection
.put(OffsetAuthOffset
, anOffset
);
1698 anOffset
= writeAuthSection(anOffset
);
1699 // Write schema section
1700 aHeaderSection
.put(OffsetSchemaOffset
, anOffset
);
1701 anOffset
= writeSchemaSection(anOffset
);
1703 // Write out the file header.
1704 aHeaderSection
.put(OffsetMagic
, HeaderMagic
);
1705 aHeaderSection
.put(OffsetVersion
, HeaderVersion
);
1706 mAtomicTempFile
->write(AtomicFile::FromStart
, HeaderOffset
,
1707 aHeaderSection
.address(), aHeaderSection
.size());
1709 // Write out the versionId.
1710 WriteSection
aVersionSection(Allocator::standard(), size_t(AtomSize
));
1711 anOffset
= aVersionSection
.put(0, mVersionId
);
1712 aVersionSection
.size(anOffset
);
1714 mAtomicTempFile
->write(AtomicFile::FromEnd
, 0,
1715 aVersionSection
.address(), aVersionSection
.size());
1717 mAtomicTempFile
->commit();
1718 mAtomicTempFile
= NULL
;
1719 /* Initialize the shared memory file change mechanism */
1720 pthread_once(&gCommonInitMutex
, initCommon
);
1722 if (gSegment
!= NULL
)
1727 The following operation is endian safe because we are not looking
1728 for monotonic increase. I have tested every possible value of
1729 *gSegment, and there is no value for which alternating
1730 big and little endian increments will produce the original value.
1733 OSAtomicIncrement32Barrier (gSegment
);
1744 DbModifier::rollback() throw()
1746 // This will destroy the AtomicTempFile if we have one causing it to rollback.
1747 mAtomicTempFile
= NULL
;
1751 DbModifier::getRecord(Table::Id inTableId
, const RecordId
&inRecordId
,
1752 CSSM_DB_RECORD_ATTRIBUTE_DATA
*inoutAttributes
,
1753 CssmData
*inoutData
, Allocator
&inAllocator
)
1755 if (mAtomicTempFile
)
1757 // We are in the midst of changing the database.
1758 return findTable(inTableId
).getRecord(inRecordId
, inoutAttributes
,
1759 inoutData
, inAllocator
);
1763 return getDbVersion(false)->getRecord(inTableId
, inRecordId
,
1764 inoutAttributes
, inoutData
, inAllocator
);
1769 DbModifier::createCursor(const CSSM_QUERY
*inQuery
)
1771 if (mAtomicTempFile
)
1773 // We are modifying this database.
1775 // If we have a mDbVersion already then it's a snapshot of the database
1776 // right before the modifications started. So return a cursor using
1779 return mDbVersion
->createCursor(inQuery
);
1781 // This is a newly created but never commited database. Return a
1782 // Cursor that will not return any matches.
1783 return new Cursor();
1786 // Get the latest and greatest version of the db and create the cursor
1788 return getDbVersion(false)->createCursor(inQuery
);
1791 // Insert schema records for a new table into the metatables of the database. This gets
1792 // called while a database is being created.
1795 DbModifier::insertTableSchema(const CssmDbRecordAttributeInfo
&inInfo
,
1796 const CSSM_DB_RECORD_INDEX_INFO
*inIndexInfo
/* = NULL */)
1798 ModifiedTable
&aTable
= findTable(inInfo
.DataRecordType
);
1799 const MetaRecord
&aMetaRecord
= aTable
.getMetaRecord();
1801 CssmAutoDbRecordAttributeData
aRecordBuilder(5); // Set capacity to 5 so we don't need to grow
1803 // Create the entry for the SchemaRelations table.
1804 aRecordBuilder
.add(RelationID
, inInfo
.recordType());
1805 aRecordBuilder
.add(RelationName
, mDb
.recordName(inInfo
.recordType()));
1807 // Insert the record into the SchemaRelations ModifiedTable
1808 findTable(mDb
.schemaRelations
.DataRecordType
).insertRecord(mVersionId
,
1809 &aRecordBuilder
, NULL
);
1811 ModifiedTable
&anAttributeTable
= findTable(mDb
.schemaAttributes
.DataRecordType
);
1812 for (uint32 anIndex
= 0; anIndex
< inInfo
.size(); anIndex
++)
1814 // Create an entry for the SchemaAttributes table.
1815 aRecordBuilder
.clear();
1816 aRecordBuilder
.add(RelationID
, inInfo
.recordType());
1817 aRecordBuilder
.add(AttributeNameFormat
, inInfo
.at(anIndex
).nameFormat());
1819 uint32 attributeId
= aMetaRecord
.metaAttribute(inInfo
.at(anIndex
)).attributeId();
1821 switch (inInfo
.at(anIndex
).nameFormat())
1823 case CSSM_DB_ATTRIBUTE_NAME_AS_STRING
:
1824 aRecordBuilder
.add(AttributeName
, inInfo
.at(anIndex
).Label
.AttributeName
);
1826 case CSSM_DB_ATTRIBUTE_NAME_AS_OID
:
1827 aRecordBuilder
.add(AttributeNameID
, inInfo
.at(anIndex
).Label
.AttributeOID
);
1829 case CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER
:
1832 CssmError::throwMe(CSSMERR_DL_INVALID_FIELD_NAME
);
1835 aRecordBuilder
.add(AttributeID
, attributeId
);
1836 aRecordBuilder
.add(AttributeFormat
, inInfo
.at(anIndex
).format());
1838 // Insert the record into the SchemaAttributes ModifiedTable
1839 anAttributeTable
.insertRecord(mVersionId
, &aRecordBuilder
, NULL
);
1842 if (inIndexInfo
!= NULL
) {
1844 if (inIndexInfo
->DataRecordType
!= inInfo
.DataRecordType
&&
1845 inIndexInfo
->NumberOfIndexes
> 0)
1846 CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE
);
1848 ModifiedTable
&indexMetaTable
= findTable(mDb
.schemaIndexes
.DataRecordType
);
1849 uint32 aNumberOfIndexes
= inIndexInfo
->NumberOfIndexes
;
1851 for (uint32 anIndex
= 0; anIndex
< aNumberOfIndexes
; anIndex
++)
1853 const CssmDbIndexInfo
&thisIndex
= CssmDbIndexInfo::overlay(inIndexInfo
->IndexInfo
[anIndex
]);
1855 // make sure the index is supported
1856 if (thisIndex
.dataLocation() != CSSM_DB_INDEX_ON_ATTRIBUTE
)
1857 CssmError::throwMe(CSSMERR_DL_INVALID_INDEX_INFO
);
1859 // assign an index ID: the unique index is ID 0, all others are ID > 0
1861 if (thisIndex
.IndexType
== CSSM_DB_INDEX_UNIQUE
)
1864 indexId
= anIndex
+ 1;
1866 // figure out the attribute ID
1867 uint32 attributeId
=
1868 aMetaRecord
.metaAttribute(thisIndex
.Info
).attributeId();
1870 // Create an entry for the SchemaIndexes table.
1871 aRecordBuilder
.clear();
1872 aRecordBuilder
.add(RelationID
, inInfo
.DataRecordType
);
1873 aRecordBuilder
.add(IndexID
, indexId
);
1874 aRecordBuilder
.add(AttributeID
, attributeId
);
1875 aRecordBuilder
.add(IndexType
, thisIndex
.IndexType
);
1876 aRecordBuilder
.add(IndexedDataLocation
, thisIndex
.IndexedDataLocation
);
1878 // Insert the record into the SchemaIndexes ModifiedTable
1879 indexMetaTable
.insertRecord(mVersionId
, &aRecordBuilder
, NULL
);
1881 // update the table's index objects
1882 DbMutableIndex
&index
= aTable
.findIndex(indexId
, aMetaRecord
, indexId
== 0);
1883 index
.appendAttribute(attributeId
);
1888 // Insert a new table. The attribute info is required; the index and parsing module
1889 // descriptions are optional. This version gets called during the creation of a
1893 DbModifier::insertTable(const CssmDbRecordAttributeInfo
&inInfo
,
1894 const CSSM_DB_RECORD_INDEX_INFO
*inIndexInfo
/* = NULL */,
1895 const CSSM_DB_PARSING_MODULE_INFO
*inParsingModule
/* = NULL */)
1898 createTable(new MetaRecord(inInfo
));
1899 insertTableSchema(inInfo
, inIndexInfo
);
1902 // Insert a new table. This is the version that gets called when a table is added
1903 // after a database has been created.
1906 DbModifier::insertTable(Table::Id inTableId
, const string
&inTableName
,
1907 uint32 inNumberOfAttributes
,
1908 const CSSM_DB_SCHEMA_ATTRIBUTE_INFO
*inAttributeInfo
,
1909 uint32 inNumberOfIndexes
,
1910 const CSSM_DB_SCHEMA_INDEX_INFO
*inIndexInfo
)
1913 ModifiedTable
*aTable
= createTable(new MetaRecord(inTableId
, inNumberOfAttributes
, inAttributeInfo
));
1915 CssmAutoDbRecordAttributeData
aRecordBuilder(6); // Set capacity to 6 so we don't need to grow
1917 // Create the entry for the SchemaRelations table.
1918 aRecordBuilder
.add(RelationID
, inTableId
);
1919 aRecordBuilder
.add(RelationName
, inTableName
);
1921 // Insert the record into the SchemaRelations ModifiedTable
1922 findTable(mDb
.schemaRelations
.DataRecordType
).insertRecord(mVersionId
,
1923 &aRecordBuilder
, NULL
);
1925 ModifiedTable
&anAttributeTable
= findTable(mDb
.schemaAttributes
.DataRecordType
);
1926 for (uint32 anIndex
= 0; anIndex
< inNumberOfAttributes
; anIndex
++)
1928 // Create an entry for the SchemaAttributes table.
1929 aRecordBuilder
.clear();
1930 aRecordBuilder
.add(RelationID
, inTableId
);
1931 // XXX What should this be? We set it to CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER for now
1932 // since the AttributeID is always valid.
1933 aRecordBuilder
.add(AttributeNameFormat
, uint32(CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER
));
1934 aRecordBuilder
.add(AttributeID
, inAttributeInfo
[anIndex
].AttributeId
);
1935 if (inAttributeInfo
[anIndex
].AttributeName
)
1936 aRecordBuilder
.add(AttributeName
, inAttributeInfo
[anIndex
].AttributeName
);
1937 if (inAttributeInfo
[anIndex
].AttributeNameID
.Length
> 0)
1938 aRecordBuilder
.add(AttributeNameID
, inAttributeInfo
[anIndex
].AttributeNameID
);
1939 aRecordBuilder
.add(AttributeFormat
, inAttributeInfo
[anIndex
].DataType
);
1941 // Insert the record into the SchemaAttributes ModifiedTable
1942 anAttributeTable
.insertRecord(mVersionId
, &aRecordBuilder
, NULL
);
1945 ModifiedTable
&anIndexTable
= findTable(mDb
.schemaIndexes
.DataRecordType
);
1946 for (uint32 anIndex
= 0; anIndex
< inNumberOfIndexes
; anIndex
++)
1948 // Create an entry for the SchemaIndexes table.
1949 aRecordBuilder
.clear();
1950 aRecordBuilder
.add(RelationID
, inTableId
);
1951 aRecordBuilder
.add(IndexID
, inIndexInfo
[anIndex
].IndexId
);
1952 aRecordBuilder
.add(AttributeID
, inIndexInfo
[anIndex
].AttributeId
);
1953 aRecordBuilder
.add(IndexType
, inIndexInfo
[anIndex
].IndexType
);
1954 aRecordBuilder
.add(IndexedDataLocation
, inIndexInfo
[anIndex
].IndexedDataLocation
);
1956 // Insert the record into the SchemaIndexes ModifiedTable
1957 anIndexTable
.insertRecord(mVersionId
, &aRecordBuilder
, NULL
);
1959 // update the table's index objects
1960 DbMutableIndex
&index
= aTable
->findIndex(inIndexInfo
[anIndex
].IndexId
,
1961 aTable
->getMetaRecord(), inIndexInfo
[anIndex
].IndexType
== CSSM_DB_INDEX_UNIQUE
);
1962 index
.appendAttribute(inIndexInfo
[anIndex
].AttributeId
);
1968 bool DbModifier::hasTable(Table::Id inTableId
)
1970 return getDbVersion(false)->hasTable(inTableId
);
1976 DbModifier::findTable(Table::Id inTableId
)
1978 ModifiedTableMap::iterator it
= mModifiedTableMap
.find(inTableId
);
1979 if (it
== mModifiedTableMap
.end())
1980 CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE
);
1986 // AppleDatabaseManager implementation
1989 AppleDatabaseManager::AppleDatabaseManager(const AppleDatabaseTableName
*tableNames
)
1990 : DatabaseManager(),
1991 mTableNames(tableNames
)
1993 // make sure that a proper set of table ids and names has been provided
1996 CssmError::throwMe(CSSMERR_DL_INTERNAL_ERROR
);
1999 for (i
= 0; mTableNames
[i
].mTableName
; i
++) {}
2000 if (i
< AppleDatabaseTableName::kNumRequiredTableNames
)
2001 CssmError::throwMe(CSSMERR_DL_INTERNAL_ERROR
);
2006 AppleDatabaseManager::make(const DbName
&inDbName
)
2008 return new AppleDatabase(inDbName
, mTableNames
);
2013 // AppleDbContext implementation
2016 /* This is the version 0 CSSM_APPLEDL_OPEN_PARAMETERS struct used up to 10.2.x. */
2019 typedef struct cssm_appledl_open_parameters_v0
2021 uint32 length
; /* Should be sizeof(CSSM_APPLEDL_OPEN_PARAMETERS_V0). */
2022 uint32 version
; /* Should be 0. */
2023 CSSM_BOOL autoCommit
;
2024 } CSSM_APPLEDL_OPEN_PARAMETERS_V0
;
2028 AppleDbContext::AppleDbContext(Database
&inDatabase
,
2029 DatabaseSession
&inDatabaseSession
,
2030 CSSM_DB_ACCESS_TYPE inAccessRequest
,
2031 const AccessCredentials
*inAccessCred
,
2032 const void *inOpenParameters
) :
2033 DbContext(inDatabase
, inDatabaseSession
, inAccessRequest
, inAccessCred
),
2037 const CSSM_APPLEDL_OPEN_PARAMETERS
*anOpenParameters
=
2038 reinterpret_cast<const CSSM_APPLEDL_OPEN_PARAMETERS
*>(inOpenParameters
);
2040 if (anOpenParameters
)
2042 switch (anOpenParameters
->version
)
2045 if (anOpenParameters
->length
< sizeof(CSSM_APPLEDL_OPEN_PARAMETERS
))
2046 CssmError::throwMe(CSSMERR_APPLEDL_INVALID_OPEN_PARAMETERS
);
2048 if (anOpenParameters
->mask
& kCSSM_APPLEDL_MASK_MODE
)
2049 mMode
= anOpenParameters
->mode
;
2052 if (anOpenParameters
->length
< sizeof(CSSM_APPLEDL_OPEN_PARAMETERS_V0
))
2053 CssmError::throwMe(CSSMERR_APPLEDL_INVALID_OPEN_PARAMETERS
);
2055 mAutoCommit
= anOpenParameters
->autoCommit
== CSSM_FALSE
? false : true;
2059 CssmError::throwMe(CSSMERR_APPLEDL_INVALID_OPEN_PARAMETERS
);
2064 AppleDbContext::~AppleDbContext()
2069 // AppleDatabase implementation
2071 AppleDatabase::AppleDatabase(const DbName
&inDbName
, const AppleDatabaseTableName
*tableNames
) :
2073 schemaRelations(tableNames
[AppleDatabaseTableName::kSchemaInfo
].mTableId
,
2074 sizeof(AttrSchemaRelations
) / sizeof(CSSM_DB_ATTRIBUTE_INFO
),
2075 const_cast<CSSM_DB_ATTRIBUTE_INFO_PTR
>(AttrSchemaRelations
)),
2076 schemaAttributes(tableNames
[AppleDatabaseTableName::kSchemaAttributes
].mTableId
,
2077 sizeof(AttrSchemaAttributes
) / sizeof(CSSM_DB_ATTRIBUTE_INFO
),
2078 const_cast<CSSM_DB_ATTRIBUTE_INFO_PTR
>(AttrSchemaAttributes
)),
2079 schemaIndexes(tableNames
[AppleDatabaseTableName::kSchemaIndexes
].mTableId
,
2080 sizeof(AttrSchemaIndexes
) / sizeof(CSSM_DB_ATTRIBUTE_INFO
),
2081 const_cast<CSSM_DB_ATTRIBUTE_INFO_PTR
>(AttrSchemaIndexes
)),
2082 schemaParsingModule(tableNames
[AppleDatabaseTableName::kSchemaParsingModule
].mTableId
,
2083 sizeof(AttrSchemaParsingModule
) / sizeof(CSSM_DB_ATTRIBUTE_INFO
),
2084 const_cast<CSSM_DB_ATTRIBUTE_INFO_PTR
>(AttrSchemaParsingModule
)),
2085 mAtomicFile(mDbName
.dbName()),
2086 mDbModifier(mAtomicFile
, *this),
2087 mTableNames(tableNames
)
2089 /* temp check for X509Anchors access - this should removed before Leopard GM */
2090 if(!strcmp(inDbName
.dbName(), "/System/Library/Keychains/X509Anchors")) {
2091 Syslog::alert("Warning: accessing obsolete X509Anchors.");
2095 AppleDatabase::~AppleDatabase()
2099 // Return the name of a record type. This uses a table that maps record types
2100 // to record names. The table is provided when the database is created.
2102 const char *AppleDatabase::recordName(CSSM_DB_RECORDTYPE inRecordType
) const
2104 if (inRecordType
== CSSM_DL_DB_RECORD_ANY
|| inRecordType
== CSSM_DL_DB_RECORD_ALL_KEYS
)
2105 CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE
);
2107 for (uint32 i
= 0; mTableNames
[i
].mTableName
; i
++)
2108 if (mTableNames
[i
].mTableId
== inRecordType
)
2109 return mTableNames
[i
].mTableName
;
2115 AppleDatabase::makeDbContext(DatabaseSession
&inDatabaseSession
,
2116 CSSM_DB_ACCESS_TYPE inAccessRequest
,
2117 const AccessCredentials
*inAccessCred
,
2118 const void *inOpenParameters
)
2120 return new AppleDbContext(*this, inDatabaseSession
, inAccessRequest
,
2121 inAccessCred
, inOpenParameters
);
2125 AppleDatabase::dbCreate(DbContext
&inDbContext
, const CSSM_DBINFO
&inDBInfo
,
2126 const CSSM_ACL_ENTRY_INPUT
*inInitialAclEntry
)
2128 AppleDbContext
&context
= safer_cast
<AppleDbContext
&>(inDbContext
);
2131 StLock
<Mutex
> _(mWriteLock
);
2132 mDbModifier
.createDatabase(inDBInfo
, inInitialAclEntry
, context
.mode());
2136 mDbModifier
.rollback();
2139 if (context
.autoCommit())
2140 mDbModifier
.commit();
2144 AppleDatabase::dbOpen(DbContext
&inDbContext
)
2146 mDbModifier
.openDatabase();
2150 AppleDatabase::dbClose()
2152 StLock
<Mutex
> _(mWriteLock
);
2153 mDbModifier
.closeDatabase();
2157 AppleDatabase::dbDelete(DatabaseSession
&inDatabaseSession
,
2158 const AccessCredentials
*inAccessCred
)
2160 StLock
<Mutex
> _(mWriteLock
);
2161 // XXX Check callers credentials.
2162 mDbModifier
.deleteDatabase();
2166 AppleDatabase::createRelation(DbContext
&inDbContext
,
2167 CSSM_DB_RECORDTYPE inRelationID
,
2168 const char *inRelationName
,
2169 uint32 inNumberOfAttributes
,
2170 const CSSM_DB_SCHEMA_ATTRIBUTE_INFO
*inAttributeInfo
,
2171 uint32 inNumberOfIndexes
,
2172 const CSSM_DB_SCHEMA_INDEX_INFO
&inIndexInfo
)
2176 StLock
<Mutex
> _(mWriteLock
);
2177 // XXX Fix the refs here.
2178 mDbModifier
.insertTable(inRelationID
, inRelationName
,
2179 inNumberOfAttributes
, inAttributeInfo
,
2180 inNumberOfIndexes
, &inIndexInfo
);
2184 if (safer_cast
<AppleDbContext
&>(inDbContext
).autoCommit())
2185 mDbModifier
.rollback();
2188 if (safer_cast
<AppleDbContext
&>(inDbContext
).autoCommit())
2189 mDbModifier
.commit();
2193 AppleDatabase::destroyRelation(DbContext
&inDbContext
,
2194 CSSM_DB_RECORDTYPE inRelationID
)
2198 StLock
<Mutex
> _(mWriteLock
);
2199 mDbModifier
.deleteTable(inRelationID
);
2203 if (safer_cast
<AppleDbContext
&>(inDbContext
).autoCommit())
2204 mDbModifier
.rollback();
2207 if (safer_cast
<AppleDbContext
&>(inDbContext
).autoCommit())
2208 mDbModifier
.commit();
2212 AppleDatabase::authenticate(DbContext
&inDbContext
,
2213 CSSM_DB_ACCESS_TYPE inAccessRequest
,
2214 const AccessCredentials
&inAccessCred
)
2216 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED
);
2220 AppleDatabase::getDbAcl(DbContext
&inDbContext
,
2221 const CSSM_STRING
*inSelectionTag
,
2222 uint32
&outNumberOfAclInfos
,
2223 CSSM_ACL_ENTRY_INFO_PTR
&outAclInfos
)
2225 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED
);
2229 AppleDatabase::changeDbAcl(DbContext
&inDbContext
,
2230 const AccessCredentials
&inAccessCred
,
2231 const CSSM_ACL_EDIT
&inAclEdit
)
2233 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED
);
2237 AppleDatabase::getDbOwner(DbContext
&inDbContext
,
2238 CSSM_ACL_OWNER_PROTOTYPE
&outOwner
)
2240 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED
);
2244 AppleDatabase::changeDbOwner(DbContext
&inDbContext
,
2245 const AccessCredentials
&inAccessCred
,
2246 const CSSM_ACL_OWNER_PROTOTYPE
&inNewOwner
)
2248 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED
);
2252 AppleDatabase::getDbNameFromHandle(const DbContext
&inDbContext
) const
2254 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED
);
2257 CSSM_DB_UNIQUE_RECORD_PTR
2258 AppleDatabase::dataInsert(DbContext
&inDbContext
,
2259 CSSM_DB_RECORDTYPE inRecordType
,
2260 const CSSM_DB_RECORD_ATTRIBUTE_DATA
*inAttributes
,
2261 const CssmData
*inData
)
2263 CSSM_DB_UNIQUE_RECORD_PTR anUniqueRecordPtr
= NULL
;
2266 StLock
<Mutex
> _(mWriteLock
);
2267 const RecordId aRecordId
=
2268 mDbModifier
.insertRecord(inRecordType
, inAttributes
, inData
);
2270 anUniqueRecordPtr
= createUniqueRecord(inDbContext
, inRecordType
,
2272 if (safer_cast
<AppleDbContext
&>(inDbContext
).autoCommit())
2273 mDbModifier
.commit();
2277 if (anUniqueRecordPtr
!= NULL
)
2278 freeUniqueRecord(inDbContext
, *anUniqueRecordPtr
);
2280 if (safer_cast
<AppleDbContext
&>(inDbContext
).autoCommit())
2281 mDbModifier
.rollback();
2285 return anUniqueRecordPtr
;
2289 AppleDatabase::dataDelete(DbContext
&inDbContext
,
2290 const CSSM_DB_UNIQUE_RECORD
&inUniqueRecord
)
2294 // syslog if it's the .Mac password
2295 CSSM_DB_RECORD_ATTRIBUTE_DATA attrData
;
2296 // we have to do this in two phases -- the first to get the record type, and the second to actually read the attributes. Otherwise, we might get
2298 memset(&attrData
, 0, sizeof(attrData
));
2299 dataGetFromUniqueRecordId(inDbContext
, inUniqueRecord
, &attrData
, NULL
);
2301 if (attrData
.DataRecordType
== CSSM_DL_DB_RECORD_GENERIC_PASSWORD
)
2303 CSSM_DB_ATTRIBUTE_DATA attributes
;
2305 // setup some attributes and see if we are indeed the .Mac password
2306 attributes
.Info
.AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER
;
2307 attributes
.Info
.Label
.AttributeID
= 'svce';
2308 attributes
.Info
.AttributeFormat
= 0;
2309 attributes
.NumberOfValues
= 1;
2310 attributes
.Value
= NULL
;
2312 attrData
.NumberOfAttributes
= 1;
2313 attrData
.AttributeData
= &attributes
;
2315 dataGetFromUniqueRecordId(inDbContext
, inUniqueRecord
, &attrData
, NULL
);
2317 // now check the results
2318 std::string
dataString((const char*) attrData
.AttributeData
[0].Value
[0].Data
, attrData
.AttributeData
[0].Value
[0].Length
);
2319 if (dataString
== "iTools")
2321 syslog(LOG_WARNING
, "Warning: Removed .Me password");
2324 free(attrData
.AttributeData
[0].Value
[0].Data
);
2325 free(attrData
.AttributeData
[0].Value
);
2328 StLock
<Mutex
> _(mWriteLock
);
2330 const RecordId
aRecordId(parseUniqueRecord(inUniqueRecord
, aTableId
));
2331 mDbModifier
.deleteRecord(aTableId
, aRecordId
);
2335 if (safer_cast
<AppleDbContext
&>(inDbContext
).autoCommit())
2336 mDbModifier
.rollback();
2340 if (safer_cast
<AppleDbContext
&>(inDbContext
).autoCommit())
2341 mDbModifier
.commit();
2345 AppleDatabase::dataModify(DbContext
&inDbContext
,
2346 CSSM_DB_RECORDTYPE inRecordType
,
2347 CSSM_DB_UNIQUE_RECORD
&inoutUniqueRecord
,
2348 const CSSM_DB_RECORD_ATTRIBUTE_DATA
*inAttributesToBeModified
,
2349 const CssmData
*inDataToBeModified
,
2350 CSSM_DB_MODIFY_MODE inModifyMode
)
2354 StLock
<Mutex
> _(mWriteLock
);
2356 const RecordId oldRecordId
= parseUniqueRecord(inoutUniqueRecord
,
2359 if (inRecordType
!= aTableId
)
2361 if (inRecordType
!= aTableId
&&
2362 inRecordType
!= CSSM_DL_DB_RECORD_ANY
&&
2363 !(inRecordType
== CSSM_DL_DB_RECORD_ALL_KEYS
&&
2364 (aTableId
== CSSM_DL_DB_RECORD_PUBLIC_KEY
||
2365 aTableId
== CSSM_DL_DB_RECORD_PRIVATE_KEY
||
2366 aTableId
== CSSM_DL_DB_RECORD_SYMMETRIC_KEY
)))
2369 CssmError::throwMe(CSSMERR_DL_INVALID_RECORD_UID
);
2372 const RecordId newRecordId
=
2373 mDbModifier
.updateRecord(aTableId
,
2375 inAttributesToBeModified
,
2378 updateUniqueRecord(inDbContext
, aTableId
, newRecordId
,
2383 if (safer_cast
<AppleDbContext
&>(inDbContext
).autoCommit())
2384 mDbModifier
.rollback();
2388 if (safer_cast
<AppleDbContext
&>(inDbContext
).autoCommit())
2389 mDbModifier
.commit();
2393 AppleDatabase::dataGetFirst(DbContext
&inDbContext
,
2394 const CssmQuery
*inQuery
,
2395 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes
,
2396 CssmData
*inoutData
,
2397 CSSM_DB_UNIQUE_RECORD_PTR
&outUniqueRecord
)
2399 // XXX: register Cursor with DbContext and have DbContext call
2400 // dataAbortQuery for all outstanding Query objects on close.
2401 auto_ptr
<Cursor
> aCursor(mDbModifier
.createCursor(inQuery
));
2405 if (!aCursor
->next(aTableId
, inoutAttributes
, inoutData
,
2406 inDbContext
.mDatabaseSession
, aRecordId
))
2407 // return a NULL handle, and implicitly delete the cursor
2408 return CSSM_INVALID_HANDLE
;
2410 outUniqueRecord
= createUniqueRecord(inDbContext
, aTableId
, aRecordId
);
2411 return aCursor
.release()->handle(); // We didn't throw so keep the Cursor around.
2415 AppleDatabase::dataGetNext(DbContext
&inDbContext
,
2416 CSSM_HANDLE inResultsHandle
,
2417 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes
,
2418 CssmData
*inoutData
,
2419 CSSM_DB_UNIQUE_RECORD_PTR
&outUniqueRecord
)
2421 auto_ptr
<Cursor
> aCursor(&HandleObject::find
<Cursor
>(inResultsHandle
, CSSMERR_DL_INVALID_RESULTS_HANDLE
));
2425 if (!aCursor
->next(aTableId
, inoutAttributes
, inoutData
, inDbContext
.mDatabaseSession
, aRecordId
))
2428 outUniqueRecord
= createUniqueRecord(inDbContext
, aTableId
, aRecordId
);
2435 AppleDatabase::dataAbortQuery(DbContext
&inDbContext
,
2436 CSSM_HANDLE inResultsHandle
)
2438 delete &HandleObject::find
<Cursor
>(inResultsHandle
, CSSMERR_DL_INVALID_RESULTS_HANDLE
);
2442 AppleDatabase::dataGetFromUniqueRecordId(DbContext
&inDbContext
,
2443 const CSSM_DB_UNIQUE_RECORD
&inUniqueRecord
,
2444 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes
,
2445 CssmData
*inoutData
)
2448 const RecordId
aRecordId(parseUniqueRecord(inUniqueRecord
, aTableId
));
2449 // XXX Change CDSA spec to use new RecordId returned by this function
2450 mDbModifier
.getRecord(aTableId
, aRecordId
, inoutAttributes
, inoutData
,
2451 inDbContext
.mDatabaseSession
);
2455 AppleDatabase::freeUniqueRecord(DbContext
&inDbContext
,
2456 CSSM_DB_UNIQUE_RECORD
&inUniqueRecord
)
2458 if (inUniqueRecord
.RecordIdentifier
.Length
!= 0
2459 && inUniqueRecord
.RecordIdentifier
.Data
!= NULL
)
2461 inUniqueRecord
.RecordIdentifier
.Length
= 0;
2462 inDbContext
.mDatabaseSession
.free(inUniqueRecord
.RecordIdentifier
.Data
);
2464 inDbContext
.mDatabaseSession
.free(&inUniqueRecord
);
2468 AppleDatabase::updateUniqueRecord(DbContext
&inDbContext
,
2469 CSSM_DB_RECORDTYPE inTableId
,
2470 const RecordId
&inRecordId
,
2471 CSSM_DB_UNIQUE_RECORD
&inoutUniqueRecord
)
2473 uint32
*aBuffer
= reinterpret_cast<uint32
*>(inoutUniqueRecord
.RecordIdentifier
.Data
);
2474 aBuffer
[0] = inTableId
;
2475 aBuffer
[1] = inRecordId
.mRecordNumber
;
2476 aBuffer
[2] = inRecordId
.mCreateVersion
;
2477 aBuffer
[3] = inRecordId
.mRecordVersion
;
2480 CSSM_DB_UNIQUE_RECORD_PTR
2481 AppleDatabase::createUniqueRecord(DbContext
&inDbContext
,
2482 CSSM_DB_RECORDTYPE inTableId
,
2483 const RecordId
&inRecordId
)
2485 CSSM_DB_UNIQUE_RECORD_PTR aUniqueRecord
=
2486 inDbContext
.mDatabaseSession
.alloc
<CSSM_DB_UNIQUE_RECORD
>();
2487 memset(aUniqueRecord
, 0, sizeof(*aUniqueRecord
));
2488 aUniqueRecord
->RecordIdentifier
.Length
= sizeof(uint32
) * 4;
2491 aUniqueRecord
->RecordIdentifier
.Data
=
2492 inDbContext
.mDatabaseSession
.alloc
<uint8
>(sizeof(uint32
) * 4);
2493 updateUniqueRecord(inDbContext
, inTableId
, inRecordId
, *aUniqueRecord
);
2497 inDbContext
.mDatabaseSession
.free(aUniqueRecord
);
2501 return aUniqueRecord
;
2505 AppleDatabase::parseUniqueRecord(const CSSM_DB_UNIQUE_RECORD
&inUniqueRecord
,
2506 CSSM_DB_RECORDTYPE
&outTableId
)
2508 if (inUniqueRecord
.RecordIdentifier
.Length
!= sizeof(uint32
) * 4)
2509 CssmError::throwMe(CSSMERR_DL_INVALID_RECORD_UID
);
2511 uint32
*aBuffer
= reinterpret_cast<uint32
*>(inUniqueRecord
.RecordIdentifier
.Data
);
2512 outTableId
= aBuffer
[0];
2513 return RecordId(aBuffer
[1], aBuffer
[2], aBuffer
[3]);
2517 AppleDatabase::passThrough(DbContext
&dbContext
,
2518 uint32 passThroughId
,
2519 const void *inputParams
,
2520 void **outputParams
)
2522 switch (passThroughId
)
2524 case CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT
:
2526 AppleDbContext
&dbc
= safer_cast
<AppleDbContext
&>(dbContext
);
2527 // Return the old state of the autoCommit flag if requested
2529 *reinterpret_cast<CSSM_BOOL
*>(outputParams
) = dbc
.autoCommit();
2530 dbc
.autoCommit(inputParams
? CSSM_TRUE
: CSSM_FALSE
);
2534 case CSSM_APPLEFILEDL_COMMIT
:
2535 mDbModifier
.commit();
2538 case CSSM_APPLEFILEDL_ROLLBACK
:
2539 mDbModifier
.rollback();
2542 case CSSM_APPLECSPDL_DB_RELATION_EXISTS
:
2544 CSSM_BOOL returnValue
;
2546 CSSM_DB_RECORDTYPE recordType
= *(CSSM_DB_RECORDTYPE
*) inputParams
;
2547 if (recordType
== CSSM_DL_DB_RECORD_ANY
|| recordType
== CSSM_DL_DB_RECORD_ALL_KEYS
)
2549 returnValue
= CSSM_TRUE
;
2553 returnValue
= mDbModifier
.hasTable(recordType
);
2556 *(CSSM_BOOL
*) outputParams
= returnValue
;
2561 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED
);