2 * Copyright (c) 2000-2001, 2003 Apple Computer, 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
;
727 // Attribute definitions
729 static const CSSM_DB_ATTRIBUTE_INFO RelationID
=
731 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
732 {(char*) "RelationID"},
733 CSSM_DB_ATTRIBUTE_FORMAT_UINT32
735 static const CSSM_DB_ATTRIBUTE_INFO RelationName
=
737 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
738 {(char*) "RelationName"},
739 CSSM_DB_ATTRIBUTE_FORMAT_STRING
741 static const CSSM_DB_ATTRIBUTE_INFO AttributeID
=
743 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
744 {(char*) "AttributeID"},
745 CSSM_DB_ATTRIBUTE_FORMAT_UINT32
747 static const CSSM_DB_ATTRIBUTE_INFO AttributeNameFormat
=
749 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
750 {(char*) "AttributeNameFormat"},
751 CSSM_DB_ATTRIBUTE_FORMAT_UINT32
753 static const CSSM_DB_ATTRIBUTE_INFO AttributeName
=
755 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
756 {(char*) "AttributeName"},
757 CSSM_DB_ATTRIBUTE_FORMAT_STRING
759 static const CSSM_DB_ATTRIBUTE_INFO AttributeNameID
=
761 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
762 {(char*) "AttributeNameID"},
763 CSSM_DB_ATTRIBUTE_FORMAT_BLOB
765 static const CSSM_DB_ATTRIBUTE_INFO AttributeFormat
=
767 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
768 {(char*) "AttributeFormat"},
769 CSSM_DB_ATTRIBUTE_FORMAT_UINT32
771 static const CSSM_DB_ATTRIBUTE_INFO IndexID
=
773 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
775 CSSM_DB_ATTRIBUTE_FORMAT_UINT32
777 static const CSSM_DB_ATTRIBUTE_INFO IndexType
=
779 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
780 {(char*) "IndexType"},
781 CSSM_DB_ATTRIBUTE_FORMAT_UINT32
783 static const CSSM_DB_ATTRIBUTE_INFO IndexedDataLocation
=
785 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
786 {(char*) "IndexedDataLocation"},
787 CSSM_DB_ATTRIBUTE_FORMAT_UINT32
789 static const CSSM_DB_ATTRIBUTE_INFO ModuleID
=
791 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
792 {(char*) "ModuleID"},
793 CSSM_DB_ATTRIBUTE_FORMAT_BLOB
795 static const CSSM_DB_ATTRIBUTE_INFO AddinVersion
=
797 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
798 {(char*) "AddinVersion"},
799 CSSM_DB_ATTRIBUTE_FORMAT_STRING
801 static const CSSM_DB_ATTRIBUTE_INFO SSID
=
803 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
805 CSSM_DB_ATTRIBUTE_FORMAT_UINT32
807 static const CSSM_DB_ATTRIBUTE_INFO SubserviceType
=
809 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
810 {(char*) "SubserviceType"},
811 CSSM_DB_ATTRIBUTE_FORMAT_UINT32
814 #define ATTRIBUTE(type, name) \
815 { CSSM_DB_ATTRIBUTE_NAME_AS_STRING, { (char*) #name }, CSSM_DB_ATTRIBUTE_FORMAT_ ## type }
817 static const CSSM_DB_ATTRIBUTE_INFO AttrSchemaRelations
[] =
819 //RelationID, RelationName
820 ATTRIBUTE(UINT32
, RelationID
),
821 ATTRIBUTE(STRING
, RelationName
)
824 static const CSSM_DB_ATTRIBUTE_INFO AttrSchemaAttributes
[] =
826 //RelationID, AttributeID,
827 //AttributeNameFormat, AttributeName, AttributeNameID,
829 ATTRIBUTE(UINT32
, RelationID
),
830 ATTRIBUTE(UINT32
, AttributeID
),
831 ATTRIBUTE(UINT32
, AttributeNameFormat
),
832 ATTRIBUTE(STRING
, AttributeName
),
833 ATTRIBUTE(BLOB
, AttributeNameID
),
834 ATTRIBUTE(UINT32
, AttributeFormat
)
837 static const CSSM_DB_ATTRIBUTE_INFO AttrSchemaIndexes
[] =
839 ATTRIBUTE(UINT32
, RelationID
),
840 ATTRIBUTE(UINT32
, IndexID
),
841 ATTRIBUTE(UINT32
, AttributeID
),
842 ATTRIBUTE(UINT32
, IndexType
),
843 ATTRIBUTE(UINT32
, IndexedDataLocation
)
844 //RelationID, IndexID, AttributeID,
845 //IndexType, IndexedDataLocation
848 static const CSSM_DB_ATTRIBUTE_INFO AttrSchemaParsingModule
[] =
850 ATTRIBUTE(UINT32
, RelationID
),
851 ATTRIBUTE(UINT32
, AttributeID
),
852 ATTRIBUTE(BLOB
, ModuleID
),
853 ATTRIBUTE(STRING
, AddinVersion
),
854 ATTRIBUTE(UINT32
, SSID
),
855 ATTRIBUTE(UINT32
, SubserviceType
)
856 //RelationID, AttributeID,
857 //ModuleID, AddinVersion, SSID, SubserviceType
865 DbVersion::DbVersion(const AppleDatabase
&db
, const RefPointer
<AtomicBufferedFile
> &inAtomicBufferedFile
) :
866 mDatabase(reinterpret_cast<const uint8
*>(NULL
), 0),
868 mBufferedFile(inAtomicBufferedFile
)
870 off_t aLength
= mBufferedFile
->length();
872 const uint8
*ptr
= mBufferedFile
->read(0, aLength
, bytesRead
);
873 mBufferedFile
->close();
874 mDatabase
= ReadSection(ptr
, (size_t)bytesRead
);
878 DbVersion::~DbVersion()
882 for_each_map_delete(mTableMap
.begin(), mTableMap
.end());
892 // This is the oposite of DbModifier::commit()
893 mVersionId
= mDatabase
[mDatabase
.size() - AtomSize
];
895 const ReadSection aHeaderSection
= mDatabase
.subsection(HeaderOffset
,
897 if (aHeaderSection
.at(OffsetMagic
) != HeaderMagic
)
898 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
900 // We currently only support one version. If we support additional
901 // file format versions in the future fix this.
902 uint32 aVersion
= aHeaderSection
.at(OffsetVersion
);
903 if (aVersion
!= HeaderVersion
)
904 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
906 //const ReadSection anAuthSection =
907 // mDatabase.subsection(HeaderOffset + aHeaderSection.at(OffsetAuthOffset));
908 // XXX Do something with anAuthSection.
910 uint32 aSchemaOffset
= aHeaderSection
.at(OffsetSchemaOffset
);
911 const ReadSection aSchemaSection
=
912 mDatabase
.subsection(HeaderOffset
+ aSchemaOffset
);
914 uint32 aSchemaSize
= aSchemaSection
[OffsetSchemaSize
];
915 // Make sure that the given range exists.
916 aSchemaSection
.subsection(0, aSchemaSize
);
917 uint32 aTableCount
= aSchemaSection
[OffsetTablesCount
];
919 // Assert that the size of this section is big enough.
920 if (aSchemaSize
< OffsetTables
+ AtomSize
* aTableCount
)
921 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
923 for (uint32 aTableNumber
= 0; aTableNumber
< aTableCount
;
926 uint32 aTableOffset
= aSchemaSection
.at(OffsetTables
+ AtomSize
928 // XXX Set the size boundary on aTableSection.
929 const ReadSection aTableSection
=
930 aSchemaSection
.subsection(aTableOffset
);
931 auto_ptr
<Table
> aTable(new Table(aTableSection
));
932 Table::Id aTableId
= aTable
->getMetaRecord().dataRecordType();
933 mTableMap
.insert(TableMap::value_type(aTableId
, aTable
.get()));
937 // Fill in the schema for the meta tables.
939 findTable(mDb
.schemaRelations
.DataRecordType
).getMetaRecord().
940 setRecordAttributeInfo(mDb
.schemaRelations
);
941 findTable(mDb
.schemaIndexes
.DataRecordType
).getMetaRecord().
942 setRecordAttributeInfo(mDb
.schemaIndexes
);
943 findTable(mDb
.schemaParsingModule
.DataRecordType
).getMetaRecord().
944 setRecordAttributeInfo(mDb
.schemaParsingModule
);
946 // OK, we have created all the tables in the tableMap. Now
947 // lets read the schema and proccess it accordingly.
948 // Iterate over all schema records.
949 Table
&aTable
= findTable(mDb
.schemaAttributes
.DataRecordType
);
950 aTable
.getMetaRecord().setRecordAttributeInfo(mDb
.schemaAttributes
);
951 uint32 aRecordsCount
= aTable
.getRecordsCount();
952 ReadSection aRecordsSection
= aTable
.getRecordsSection();
953 uint32 aReadOffset
= 0;
954 const MetaRecord
&aMetaRecord
= aTable
.getMetaRecord();
956 CSSM_DB_ATTRIBUTE_DATA aRelationIDData
=
962 CSSM_DB_ATTRIBUTE_DATA aAttributeIDData
=
968 CSSM_DB_ATTRIBUTE_DATA aAttributeNameFormatData
=
974 CSSM_DB_ATTRIBUTE_DATA aAttributeNameData
=
980 CSSM_DB_ATTRIBUTE_DATA aAttributeNameIDData
=
986 CSSM_DB_ATTRIBUTE_DATA aAttributeFormatData
=
992 CSSM_DB_ATTRIBUTE_DATA aRecordAttributes
[] =
996 aAttributeNameFormatData
,
998 aAttributeNameIDData
,
1001 CSSM_DB_RECORD_ATTRIBUTE_DATA aRecordAttributeData
=
1003 aMetaRecord
.dataRecordType(),
1005 sizeof(aRecordAttributes
) / sizeof(CSSM_DB_ATTRIBUTE_DATA
),
1008 CssmDbRecordAttributeData
&aRecordData
= CssmDbRecordAttributeData::overlay(aRecordAttributeData
);
1010 TrackingAllocator
recordAllocator(Allocator::standard());
1011 for (uint32 aRecord
= 0; aRecord
!= aRecordsCount
; aRecord
++)
1013 ReadSection aRecordSection
= MetaRecord::readSection(aRecordsSection
, aReadOffset
);
1014 uint32 aRecordSize
= aRecordSection
.size();
1015 aReadOffset
+= aRecordSize
;
1016 aMetaRecord
.unpackRecord(aRecordSection
, recordAllocator
,
1017 &aRecordAttributeData
, NULL
, 0);
1018 // Create the attribute coresponding to this entry
1019 if (aRecordData
[0].size() != 1 || aRecordData
[0].format() != CSSM_DB_ATTRIBUTE_FORMAT_UINT32
)
1020 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
1021 uint32 aRelationId
= aRecordData
[0];
1023 // Skip the schema relations for the meta tables themselves.
1024 // FIXME: this hard-wires the meta-table relation IDs to be
1025 // within {CSSM_DB_RECORDTYPE_SCHEMA_START...
1026 // CSSM_DB_RECORDTYPE_SCHEMA_END} (which is {0..4}).
1027 // Bogus - the MDS schema relation IDs start at
1028 // CSSM_DB_RELATIONID_MDS_START which is 0x40000000.
1029 // Ref. Radar 2817921.
1030 if (CSSM_DB_RECORDTYPE_SCHEMA_START
<= aRelationId
&& aRelationId
< CSSM_DB_RECORDTYPE_SCHEMA_END
)
1033 // Get the MetaRecord corresponding to the specified RelationId
1034 MetaRecord
&aMetaRecord
= findTable(aRelationId
).getMetaRecord();
1036 if (aRecordData
[1].size() != 1
1037 || aRecordData
[1].format() != CSSM_DB_ATTRIBUTE_FORMAT_UINT32
1038 || aRecordData
[2].size() != 1
1039 || aRecordData
[2].format() != CSSM_DB_ATTRIBUTE_FORMAT_UINT32
1040 || aRecordData
[5].size() != 1
1041 || aRecordData
[5].format() != CSSM_DB_ATTRIBUTE_FORMAT_UINT32
)
1042 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
1044 uint32 anAttributeId
= aRecordData
[1];
1045 uint32 anAttributeNameFormat
= aRecordData
[2];
1046 uint32 anAttributeFormat
= aRecordData
[5];
1047 auto_ptr
<string
> aName
;
1048 const CssmData
*aNameID
= NULL
;
1050 if (aRecordData
[3].size() == 1)
1052 if (aRecordData
[3].format() != CSSM_DB_ATTRIBUTE_FORMAT_STRING
)
1053 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
1055 auto_ptr
<string
> aName2(new string(static_cast<string
>(aRecordData
[3])));
1059 if (aRecordData
[4].size() == 1)
1061 if (aRecordData
[4].format() != CSSM_DB_ATTRIBUTE_FORMAT_BLOB
)
1062 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
1064 // @@@ Invoking conversion operator to CssmData & on aRecordData[4]
1065 // And taking address of result.
1066 aNameID
= &static_cast<const CssmData
&>(aRecordData
[4]);
1069 // Make sure that the attribute specified by anAttributeNameFormat is present.
1070 switch (anAttributeNameFormat
)
1072 case CSSM_DB_ATTRIBUTE_NAME_AS_STRING
:
1073 if (aRecordData
[3].size() != 1)
1074 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
1076 case CSSM_DB_ATTRIBUTE_NAME_AS_OID
:
1077 if (aRecordData
[4].size() != 1)
1078 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
1080 case CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER
:
1083 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
1086 // Create the attribute
1087 aMetaRecord
.createAttribute(aName
.get(), aNameID
, anAttributeId
, anAttributeFormat
);
1090 // initialize the indexes associated with each table
1092 TableMap::iterator it
;
1093 for (it
= mTableMap
.begin(); it
!= mTableMap
.end(); it
++)
1094 it
->second
->readIndexSection();
1099 for_each_map_delete(mTableMap
.begin(), mTableMap
.end());
1106 DbVersion::getRecord(Table::Id inTableId
, const RecordId
&inRecordId
,
1107 CSSM_DB_RECORD_ATTRIBUTE_DATA
*inoutAttributes
,
1108 CssmData
*inoutData
,
1109 Allocator
&inAllocator
) const
1111 return findTable(inTableId
).getRecord(inRecordId
, inoutAttributes
,
1112 inoutData
, inAllocator
);
1116 DbVersion::createCursor(const CSSM_QUERY
*inQuery
) const
1118 // XXX We should add support for these special query types
1119 // By Creating a Cursor that iterates over multiple tables
1120 if (!inQuery
|| inQuery
->RecordType
== CSSM_DL_DB_RECORD_ANY
1121 || inQuery
->RecordType
== CSSM_DL_DB_RECORD_ALL_KEYS
)
1123 return new MultiCursor(inQuery
, *this);
1126 return findTable(inQuery
->RecordType
).createCursor(inQuery
, *this);
1129 bool DbVersion::hasTable(Table::Id inTableId
) const
1131 TableMap::const_iterator it
= mTableMap
.find(inTableId
);
1132 return it
!= mTableMap
.end();
1136 DbVersion::findTable(Table::Id inTableId
) const
1138 TableMap::const_iterator it
= mTableMap
.find(inTableId
);
1139 if (it
== mTableMap
.end())
1140 CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE
);
1145 DbVersion::findTable(Table::Id inTableId
)
1147 TableMap::iterator it
= mTableMap
.find(inTableId
);
1148 if (it
== mTableMap
.end())
1149 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
1154 // Cursor implemetation
1160 Cursor::Cursor(const DbVersion
&inDbVersion
) : mDbVersion(&inDbVersion
)
1169 Cursor::next(Table::Id
&outTableId
,
1170 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR outAttributes
,
1172 Allocator
&inAllocator
,
1179 // LinearCursor implemetation
1181 LinearCursor::LinearCursor(const CSSM_QUERY
*inQuery
, const DbVersion
&inDbVersion
,
1182 const Table
&inTable
) :
1183 Cursor(inDbVersion
),
1184 mRecordsCount(inTable
.getRecordsCount()),
1186 mRecordsSection(inTable
.getRecordsSection()),
1188 mMetaRecord(inTable
.getMetaRecord())
1192 mConjunctive
= inQuery
->Conjunctive
;
1193 mQueryFlags
= inQuery
->QueryFlags
;
1194 // XXX Do something with inQuery->QueryLimits?
1195 uint32 aPredicatesCount
= inQuery
->NumSelectionPredicates
;
1196 mPredicates
.resize(aPredicatesCount
);
1199 for (uint32 anIndex
= 0; anIndex
< aPredicatesCount
; anIndex
++)
1201 CSSM_SELECTION_PREDICATE
&aPredicate
= inQuery
->SelectionPredicate
[anIndex
];
1202 mPredicates
[anIndex
] = new SelectionPredicate(mMetaRecord
, aPredicate
);
1207 for_each_delete(mPredicates
.begin(), mPredicates
.end());
1213 LinearCursor::~LinearCursor()
1215 for_each_delete(mPredicates
.begin(), mPredicates
.end());
1219 LinearCursor::next(Table::Id
&outTableId
,
1220 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes
,
1221 CssmData
*inoutData
, Allocator
&inAllocator
, RecordId
&recordId
)
1223 while (mRecord
++ < mRecordsCount
)
1225 ReadSection aRecordSection
= MetaRecord::readSection(mRecordsSection
, mReadOffset
);
1226 uint32 aRecordSize
= aRecordSection
.size();
1227 mReadOffset
+= aRecordSize
;
1229 PredicateVector::const_iterator anIt
= mPredicates
.begin();
1230 PredicateVector::const_iterator anEnd
= mPredicates
.end();
1234 // If there are no predicates we have a match.
1237 else if (mConjunctive
== CSSM_DB_OR
)
1239 // If mConjunctive is OR, the first predicate that returns
1240 // true indicates a match. Dropthough means no match
1242 for (; anIt
!= anEnd
; anIt
++)
1244 if ((*anIt
)->evaluate(aRecordSection
))
1251 else if (mConjunctive
== CSSM_DB_AND
|| mConjunctive
== CSSM_DB_NONE
)
1253 // If mConjunctive is AND (or NONE), the first predicate that returns
1254 // false indicates a mismatch. Dropthough means a match
1256 for (; anIt
!= anEnd
; anIt
++)
1258 if (!(*anIt
)->evaluate(aRecordSection
))
1267 // XXX Should be CSSMERR_DL_INVALID_QUERY (or CSSMERR_DL_INVALID_CONJUNTIVE).
1268 CssmError::throwMe(CSSMERR_DL_UNSUPPORTED_QUERY
);
1273 // Get the actual record.
1274 mMetaRecord
.unpackRecord(aRecordSection
, inAllocator
,
1275 inoutAttributes
, inoutData
,
1277 outTableId
= mMetaRecord
.dataRecordType();
1278 recordId
= MetaRecord::unpackRecordId(aRecordSection
);
1290 IndexCursor::IndexCursor(DbQueryKey
*queryKey
, const DbVersion
&inDbVersion
,
1291 const Table
&table
, const DbConstIndex
*index
) :
1292 Cursor(inDbVersion
), mQueryKey(queryKey
), mTable(table
), mIndex(index
)
1294 index
->performQuery(*queryKey
, mBegin
, mEnd
);
1297 IndexCursor::~IndexCursor()
1299 // the query key will be deleted automatically, since it's an auto_ptr
1303 IndexCursor::next(Table::Id
&outTableId
,
1304 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR outAttributes
,
1306 Allocator
&inAllocator
, RecordId
&recordId
)
1311 ReadSection rs
= mIndex
->getRecordSection(mBegin
++);
1312 const MetaRecord
&metaRecord
= mTable
.getMetaRecord();
1314 outTableId
= metaRecord
.dataRecordType();
1315 metaRecord
.unpackRecord(rs
, inAllocator
, outAttributes
, outData
, 0);
1317 recordId
= MetaRecord::unpackRecordId(rs
);
1324 MultiCursor::MultiCursor(const CSSM_QUERY
*inQuery
, const DbVersion
&inDbVersion
) :
1325 Cursor(inDbVersion
), mTableIterator(inDbVersion
.begin())
1328 mQuery
.reset(new CssmAutoQuery(*inQuery
));
1331 mQuery
.reset(new CssmAutoQuery());
1332 mQuery
->recordType(CSSM_DL_DB_RECORD_ANY
);
1336 MultiCursor::~MultiCursor()
1341 MultiCursor::next(Table::Id
&outTableId
,
1342 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes
,
1343 CssmData
*inoutData
, Allocator
&inAllocator
, RecordId
&recordId
)
1349 if (mTableIterator
== mDbVersion
->end())
1352 const Table
&aTable
= *mTableIterator
++;
1353 if (!aTable
.matchesTableId(mQuery
->recordType()))
1356 mCursor
.reset(aTable
.createCursor(mQuery
.get(), *mDbVersion
));
1359 if (mCursor
->next(outTableId
, inoutAttributes
, inoutData
, inAllocator
, recordId
))
1362 mCursor
.reset(NULL
);
1370 DbModifier::DbModifier(AtomicFile
&inAtomicFile
, const AppleDatabase
&db
) :
1373 mAtomicFile(inAtomicFile
),
1378 DbModifier::~DbModifier()
1382 for_each_map_delete(mModifiedTableMap
.begin(), mModifiedTableMap
.end());
1383 // mAtomicTempFile will do automatic rollback on destruction.
1388 const RefPointer
<const DbVersion
>
1389 DbModifier::getDbVersion(bool force
)
1391 StLock
<Mutex
> _(mDbVersionLock
);
1393 /* Initialize the shared memory file change mechanism */
1394 pthread_once(&gCommonInitMutex
, initCommon
);
1396 /* If we don't have a mDbVersion yet, or we are force to re-read the file
1397 before a write transaction, or we have received any notifications after
1398 the last time we read the file, or more than kForceReReadTime seconds
1399 have passed since the last time we read the file, we open the file and
1400 check if it has changed. */
1404 mNotifyCount
!= *gSegment
||
1405 CFAbsoluteTimeGetCurrent() > mDbLastRead
+ kForceReReadTime
)
1407 RefPointer
<AtomicBufferedFile
> atomicBufferedFile(mAtomicFile
.read());
1408 off_t length
= atomicBufferedFile
->open();
1409 /* Record the number of notifications we've seen and when we last
1411 if (gSegment
!= NULL
)
1413 mNotifyCount
= *gSegment
;
1416 mDbLastRead
= CFAbsoluteTimeGetCurrent();
1418 /* If we already have a mDbVersion, let's check if we can reuse it. */
1421 if (length
< AtomSize
)
1422 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
1424 off_t bytesRead
= 0;
1425 const uint8
*ptr
= atomicBufferedFile
->read(length
- AtomSize
,
1426 AtomSize
, bytesRead
);
1427 ReadSection
aVersionSection(ptr
, (size_t)bytesRead
);
1428 uint32 aVersionId
= aVersionSection
[0];
1430 /* If the version stamp hasn't changed the old mDbVersion is still
1432 if (aVersionId
== mDbVersion
->getVersionId())
1436 mDbVersion
= new DbVersion(mDb
, atomicBufferedFile
);
1443 DbModifier::createDatabase(const CSSM_DBINFO
&inDbInfo
,
1444 const CSSM_ACL_ENTRY_INPUT
*inInitialAclEntry
,
1447 // XXX This needs better locking. There is a possible race condition between
1448 // two concurrent creators. Or a writer/creator or a close/create etc.
1449 if (mAtomicTempFile
|| !mModifiedTableMap
.empty())
1450 CssmError::throwMe(CSSMERR_DL_DATASTORE_ALREADY_EXISTS
);
1452 mAtomicTempFile
= mAtomicFile
.create(mode
);
1453 // Set mVersionId to one since this is the first version of the database.
1456 // we need to create the meta tables first, because inserting tables
1457 // (including the meta tables themselves) relies on them being there
1458 createTable(new MetaRecord(mDb
.schemaRelations
));
1459 createTable(new MetaRecord(mDb
.schemaAttributes
));
1460 createTable(new MetaRecord(mDb
.schemaIndexes
));
1461 createTable(new MetaRecord(mDb
.schemaParsingModule
));
1463 // now add the meta-tables' schema to the meta tables themselves
1464 insertTableSchema(mDb
.schemaRelations
);
1465 insertTableSchema(mDb
.schemaAttributes
);
1466 insertTableSchema(mDb
.schemaIndexes
);
1467 insertTableSchema(mDb
.schemaParsingModule
);
1469 if (inInitialAclEntry
!= NULL
)
1471 //createACL(*inInitialAclEntry);
1474 if (inDbInfo
.NumberOfRecordTypes
== 0)
1476 if (inDbInfo
.RecordAttributeNames
== NULL
)
1477 CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE
);
1478 if (inDbInfo
.RecordIndexes
== NULL
)
1479 CssmError::throwMe(CSSMERR_DL_INVALID_RECORD_INDEX
);
1480 if (inDbInfo
.DefaultParsingModules
== NULL
)
1481 CssmError::throwMe(CSSMERR_DL_INVALID_PARSING_MODULE
);
1483 for (uint32 anIndex
= 0; anIndex
< inDbInfo
.NumberOfRecordTypes
; anIndex
++)
1485 insertTable(CssmDbRecordAttributeInfo::overlay(inDbInfo
.RecordAttributeNames
[anIndex
]),
1486 &inDbInfo
.RecordIndexes
[anIndex
],
1487 &inDbInfo
.DefaultParsingModules
[anIndex
]);
1491 void DbModifier::openDatabase()
1493 // No need to do anything on open if we are already writing the database.
1494 if (!mAtomicTempFile
)
1495 getDbVersion(false);
1498 void DbModifier::closeDatabase()
1500 commit(); // XXX Requires write lock.
1501 StLock
<Mutex
> _(mDbVersionLock
);
1505 void DbModifier::deleteDatabase()
1507 bool isDirty
= mAtomicTempFile
;
1508 rollback(); // XXX Requires write lock.
1509 StLock
<Mutex
> _(mDbVersionLock
);
1511 // Clean up mModifiedTableMap in case this object gets reused again for
1513 for_each_map_delete(mModifiedTableMap
.begin(), mModifiedTableMap
.end());
1514 mModifiedTableMap
.clear();
1516 // If the database was dirty and we had no mDbVersion yet then rollback()
1517 // would have deleted the db.
1518 if (!isDirty
|| mDbVersion
)
1521 mAtomicFile
.performDelete();
1526 DbModifier::modifyDatabase()
1528 if (mAtomicTempFile
)
1533 mAtomicTempFile
= mAtomicFile
.write();
1534 // Now we are holding the write lock make sure we get the latest greatest version of the db.
1535 // Also set mVersionId to one more that that of the old database.
1536 mVersionId
= getDbVersion(true)->getVersionId() + 1;
1538 // Never make a database with mVersionId 0 since it makes bad things happen to Jaguar and older systems
1539 if (mVersionId
== 0)
1542 // Remove all old modified tables
1543 for_each_map_delete(mModifiedTableMap
.begin(), mModifiedTableMap
.end());
1544 mModifiedTableMap
.clear();
1546 // Setup the new tables
1547 DbVersion::TableMap::const_iterator anIt
=
1548 mDbVersion
->mTableMap
.begin();
1549 DbVersion::TableMap::const_iterator anEnd
=
1550 mDbVersion
->mTableMap
.end();
1551 for (; anIt
!= anEnd
; ++anIt
)
1553 auto_ptr
<ModifiedTable
> aTable(new ModifiedTable(anIt
->second
));
1554 mModifiedTableMap
.insert(ModifiedTableMap::value_type(anIt
->first
,
1561 for_each_map_delete(mModifiedTableMap
.begin(), mModifiedTableMap
.end());
1562 mModifiedTableMap
.clear();
1569 DbModifier::deleteRecord(Table::Id inTableId
, const RecordId
&inRecordId
)
1572 findTable(inTableId
).deleteRecord(inRecordId
);
1576 DbModifier::insertRecord(Table::Id inTableId
,
1577 const CSSM_DB_RECORD_ATTRIBUTE_DATA
*inAttributes
,
1578 const CssmData
*inData
)
1581 return findTable(inTableId
).insertRecord(mVersionId
, inAttributes
, inData
);
1585 DbModifier::updateRecord(Table::Id inTableId
, const RecordId
&inRecordId
,
1586 const CSSM_DB_RECORD_ATTRIBUTE_DATA
*inAttributes
,
1587 const CssmData
*inData
,
1588 CSSM_DB_MODIFY_MODE inModifyMode
)
1591 return findTable(inTableId
).updateRecord(inRecordId
, inAttributes
, inData
, inModifyMode
);
1594 // Create a table associated with a given metarecord, and add the table
1598 DbModifier::createTable(MetaRecord
*inMetaRecord
)
1600 auto_ptr
<MetaRecord
> aMetaRecord(inMetaRecord
);
1601 auto_ptr
<ModifiedTable
> aModifiedTable(new ModifiedTable(inMetaRecord
));
1602 // Now that aModifiedTable is fully constructed it owns inMetaRecord
1603 aMetaRecord
.release();
1605 if (!mModifiedTableMap
.insert
1606 (ModifiedTableMap::value_type(inMetaRecord
->dataRecordType(),
1607 aModifiedTable
.get())).second
)
1609 // XXX Should be CSSMERR_DL_DUPLICATE_RECORDTYPE. Since that
1610 // doesn't exist we report that the metatable's unique index would
1611 // no longer be valid
1612 CssmError::throwMe(CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA
);
1615 return aModifiedTable
.release();
1619 DbModifier::deleteTable(Table::Id inTableId
)
1622 // Can't delete schema tables.
1623 if (CSSM_DB_RECORDTYPE_SCHEMA_START
<= inTableId
1624 && inTableId
< CSSM_DB_RECORDTYPE_SCHEMA_END
)
1625 CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE
);
1627 // Find the ModifiedTable and delete it
1628 ModifiedTableMap::iterator it
= mModifiedTableMap
.find(inTableId
);
1629 if (it
== mModifiedTableMap
.end())
1630 CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE
);
1633 mModifiedTableMap
.erase(it
);
1637 DbModifier::writeAuthSection(uint32 inSectionOffset
)
1639 WriteSection anAuthSection
;
1641 // XXX Put real data into the authsection.
1642 uint32 anOffset
= anAuthSection
.put(0, 0);
1643 anAuthSection
.size(anOffset
);
1645 mAtomicTempFile
->write(AtomicFile::FromStart
, inSectionOffset
,
1646 anAuthSection
.address(), anAuthSection
.size());
1647 return inSectionOffset
+ anOffset
;
1651 DbModifier::writeSchemaSection(uint32 inSectionOffset
)
1653 uint32 aTableCount
= (uint32
) mModifiedTableMap
.size();
1654 WriteSection
aTableSection(Allocator::standard(),
1655 OffsetTables
+ AtomSize
* aTableCount
);
1656 // Set aTableSection to the correct size.
1657 aTableSection
.size(OffsetTables
+ AtomSize
* aTableCount
);
1658 aTableSection
.put(OffsetTablesCount
, aTableCount
);
1660 uint32 anOffset
= inSectionOffset
+ OffsetTables
+ AtomSize
* aTableCount
;
1661 ModifiedTableMap::const_iterator anIt
= mModifiedTableMap
.begin();
1662 ModifiedTableMap::const_iterator anEnd
= mModifiedTableMap
.end();
1663 for (uint32 aTableNumber
= 0; anIt
!= anEnd
; anIt
++, aTableNumber
++)
1665 // Put the offset to the current table relative to the start of
1666 // this section into the tables array
1667 aTableSection
.put(OffsetTables
+ AtomSize
* aTableNumber
,
1668 anOffset
- inSectionOffset
);
1669 anOffset
= anIt
->second
->writeTable(*mAtomicTempFile
, anOffset
);
1672 aTableSection
.put(OffsetSchemaSize
, anOffset
- inSectionOffset
);
1673 mAtomicTempFile
->write(AtomicFile::FromStart
, inSectionOffset
,
1674 aTableSection
.address(), aTableSection
.size());
1680 DbModifier::commit()
1682 if (!mAtomicTempFile
)
1686 WriteSection
aHeaderSection(Allocator::standard(), size_t(HeaderSize
));
1687 // Set aHeaderSection to the correct size.
1688 aHeaderSection
.size(HeaderSize
);
1690 // Start writing sections after the header
1691 uint32 anOffset
= HeaderOffset
+ HeaderSize
;
1693 // Write auth section
1694 aHeaderSection
.put(OffsetAuthOffset
, anOffset
);
1695 anOffset
= writeAuthSection(anOffset
);
1696 // Write schema section
1697 aHeaderSection
.put(OffsetSchemaOffset
, anOffset
);
1698 anOffset
= writeSchemaSection(anOffset
);
1700 // Write out the file header.
1701 aHeaderSection
.put(OffsetMagic
, HeaderMagic
);
1702 aHeaderSection
.put(OffsetVersion
, HeaderVersion
);
1703 mAtomicTempFile
->write(AtomicFile::FromStart
, HeaderOffset
,
1704 aHeaderSection
.address(), aHeaderSection
.size());
1706 // Write out the versionId.
1707 WriteSection
aVersionSection(Allocator::standard(), size_t(AtomSize
));
1708 anOffset
= aVersionSection
.put(0, mVersionId
);
1709 aVersionSection
.size(anOffset
);
1711 mAtomicTempFile
->write(AtomicFile::FromEnd
, 0,
1712 aVersionSection
.address(), aVersionSection
.size());
1714 mAtomicTempFile
->commit();
1715 mAtomicTempFile
= NULL
;
1716 /* Initialize the shared memory file change mechanism */
1717 pthread_once(&gCommonInitMutex
, initCommon
);
1719 if (gSegment
!= NULL
)
1724 The following operation is endian safe because we are not looking
1725 for monotonic increase. I have tested every possible value of
1726 *gSegment, and there is no value for which alternating
1727 big and little endian increments will produce the original value.
1730 OSAtomicIncrement32Barrier (gSegment
);
1741 DbModifier::rollback() throw()
1743 // This will destroy the AtomicTempFile if we have one causing it to rollback.
1744 mAtomicTempFile
= NULL
;
1748 DbModifier::getRecord(Table::Id inTableId
, const RecordId
&inRecordId
,
1749 CSSM_DB_RECORD_ATTRIBUTE_DATA
*inoutAttributes
,
1750 CssmData
*inoutData
, Allocator
&inAllocator
)
1752 if (mAtomicTempFile
)
1754 // We are in the midst of changing the database.
1755 return findTable(inTableId
).getRecord(inRecordId
, inoutAttributes
,
1756 inoutData
, inAllocator
);
1760 return getDbVersion(false)->getRecord(inTableId
, inRecordId
,
1761 inoutAttributes
, inoutData
, inAllocator
);
1766 DbModifier::createCursor(const CSSM_QUERY
*inQuery
)
1768 if (mAtomicTempFile
)
1770 // We are modifying this database.
1772 // If we have a mDbVersion already then it's a snapshot of the database
1773 // right before the modifications started. So return a cursor using
1776 return mDbVersion
->createCursor(inQuery
);
1778 // This is a newly created but never commited database. Return a
1779 // Cursor that will not return any matches.
1780 return new Cursor();
1783 // Get the latest and greatest version of the db and create the cursor
1785 return getDbVersion(false)->createCursor(inQuery
);
1788 // Insert schema records for a new table into the metatables of the database. This gets
1789 // called while a database is being created.
1792 DbModifier::insertTableSchema(const CssmDbRecordAttributeInfo
&inInfo
,
1793 const CSSM_DB_RECORD_INDEX_INFO
*inIndexInfo
/* = NULL */)
1795 ModifiedTable
&aTable
= findTable(inInfo
.DataRecordType
);
1796 const MetaRecord
&aMetaRecord
= aTable
.getMetaRecord();
1798 CssmAutoDbRecordAttributeData
aRecordBuilder(5); // Set capacity to 5 so we don't need to grow
1800 // Create the entry for the SchemaRelations table.
1801 aRecordBuilder
.add(RelationID
, inInfo
.recordType());
1802 aRecordBuilder
.add(RelationName
, mDb
.recordName(inInfo
.recordType()));
1804 // Insert the record into the SchemaRelations ModifiedTable
1805 findTable(mDb
.schemaRelations
.DataRecordType
).insertRecord(mVersionId
,
1806 &aRecordBuilder
, NULL
);
1808 ModifiedTable
&anAttributeTable
= findTable(mDb
.schemaAttributes
.DataRecordType
);
1809 for (uint32 anIndex
= 0; anIndex
< inInfo
.size(); anIndex
++)
1811 // Create an entry for the SchemaAttributes table.
1812 aRecordBuilder
.clear();
1813 aRecordBuilder
.add(RelationID
, inInfo
.recordType());
1814 aRecordBuilder
.add(AttributeNameFormat
, inInfo
.at(anIndex
).nameFormat());
1816 uint32 attributeId
= aMetaRecord
.metaAttribute(inInfo
.at(anIndex
)).attributeId();
1818 switch (inInfo
.at(anIndex
).nameFormat())
1820 case CSSM_DB_ATTRIBUTE_NAME_AS_STRING
:
1821 aRecordBuilder
.add(AttributeName
, inInfo
.at(anIndex
).Label
.AttributeName
);
1823 case CSSM_DB_ATTRIBUTE_NAME_AS_OID
:
1824 aRecordBuilder
.add(AttributeNameID
, inInfo
.at(anIndex
).Label
.AttributeOID
);
1826 case CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER
:
1829 CssmError::throwMe(CSSMERR_DL_INVALID_FIELD_NAME
);
1832 aRecordBuilder
.add(AttributeID
, attributeId
);
1833 aRecordBuilder
.add(AttributeFormat
, inInfo
.at(anIndex
).format());
1835 // Insert the record into the SchemaAttributes ModifiedTable
1836 anAttributeTable
.insertRecord(mVersionId
, &aRecordBuilder
, NULL
);
1839 if (inIndexInfo
!= NULL
) {
1841 if (inIndexInfo
->DataRecordType
!= inInfo
.DataRecordType
&&
1842 inIndexInfo
->NumberOfIndexes
> 0)
1843 CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE
);
1845 ModifiedTable
&indexMetaTable
= findTable(mDb
.schemaIndexes
.DataRecordType
);
1846 uint32 aNumberOfIndexes
= inIndexInfo
->NumberOfIndexes
;
1848 for (uint32 anIndex
= 0; anIndex
< aNumberOfIndexes
; anIndex
++)
1850 const CssmDbIndexInfo
&thisIndex
= CssmDbIndexInfo::overlay(inIndexInfo
->IndexInfo
[anIndex
]);
1852 // make sure the index is supported
1853 if (thisIndex
.dataLocation() != CSSM_DB_INDEX_ON_ATTRIBUTE
)
1854 CssmError::throwMe(CSSMERR_DL_INVALID_INDEX_INFO
);
1856 // assign an index ID: the unique index is ID 0, all others are ID > 0
1858 if (thisIndex
.IndexType
== CSSM_DB_INDEX_UNIQUE
)
1861 indexId
= anIndex
+ 1;
1863 // figure out the attribute ID
1864 uint32 attributeId
=
1865 aMetaRecord
.metaAttribute(thisIndex
.Info
).attributeId();
1867 // Create an entry for the SchemaIndexes table.
1868 aRecordBuilder
.clear();
1869 aRecordBuilder
.add(RelationID
, inInfo
.DataRecordType
);
1870 aRecordBuilder
.add(IndexID
, indexId
);
1871 aRecordBuilder
.add(AttributeID
, attributeId
);
1872 aRecordBuilder
.add(IndexType
, thisIndex
.IndexType
);
1873 aRecordBuilder
.add(IndexedDataLocation
, thisIndex
.IndexedDataLocation
);
1875 // Insert the record into the SchemaIndexes ModifiedTable
1876 indexMetaTable
.insertRecord(mVersionId
, &aRecordBuilder
, NULL
);
1878 // update the table's index objects
1879 DbMutableIndex
&index
= aTable
.findIndex(indexId
, aMetaRecord
, indexId
== 0);
1880 index
.appendAttribute(attributeId
);
1885 // Insert a new table. The attribute info is required; the index and parsing module
1886 // descriptions are optional. This version gets called during the creation of a
1890 DbModifier::insertTable(const CssmDbRecordAttributeInfo
&inInfo
,
1891 const CSSM_DB_RECORD_INDEX_INFO
*inIndexInfo
/* = NULL */,
1892 const CSSM_DB_PARSING_MODULE_INFO
*inParsingModule
/* = NULL */)
1895 createTable(new MetaRecord(inInfo
));
1896 insertTableSchema(inInfo
, inIndexInfo
);
1899 // Insert a new table. This is the version that gets called when a table is added
1900 // after a database has been created.
1903 DbModifier::insertTable(Table::Id inTableId
, const string
&inTableName
,
1904 uint32 inNumberOfAttributes
,
1905 const CSSM_DB_SCHEMA_ATTRIBUTE_INFO
*inAttributeInfo
,
1906 uint32 inNumberOfIndexes
,
1907 const CSSM_DB_SCHEMA_INDEX_INFO
*inIndexInfo
)
1910 ModifiedTable
*aTable
= createTable(new MetaRecord(inTableId
, inNumberOfAttributes
, inAttributeInfo
));
1912 CssmAutoDbRecordAttributeData
aRecordBuilder(6); // Set capacity to 6 so we don't need to grow
1914 // Create the entry for the SchemaRelations table.
1915 aRecordBuilder
.add(RelationID
, inTableId
);
1916 aRecordBuilder
.add(RelationName
, inTableName
);
1918 // Insert the record into the SchemaRelations ModifiedTable
1919 findTable(mDb
.schemaRelations
.DataRecordType
).insertRecord(mVersionId
,
1920 &aRecordBuilder
, NULL
);
1922 ModifiedTable
&anAttributeTable
= findTable(mDb
.schemaAttributes
.DataRecordType
);
1923 for (uint32 anIndex
= 0; anIndex
< inNumberOfAttributes
; anIndex
++)
1925 // Create an entry for the SchemaAttributes table.
1926 aRecordBuilder
.clear();
1927 aRecordBuilder
.add(RelationID
, inTableId
);
1928 // XXX What should this be? We set it to CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER for now
1929 // since the AttributeID is always valid.
1930 aRecordBuilder
.add(AttributeNameFormat
, uint32(CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER
));
1931 aRecordBuilder
.add(AttributeID
, inAttributeInfo
[anIndex
].AttributeId
);
1932 if (inAttributeInfo
[anIndex
].AttributeName
)
1933 aRecordBuilder
.add(AttributeName
, inAttributeInfo
[anIndex
].AttributeName
);
1934 if (inAttributeInfo
[anIndex
].AttributeNameID
.Length
> 0)
1935 aRecordBuilder
.add(AttributeNameID
, inAttributeInfo
[anIndex
].AttributeNameID
);
1936 aRecordBuilder
.add(AttributeFormat
, inAttributeInfo
[anIndex
].DataType
);
1938 // Insert the record into the SchemaAttributes ModifiedTable
1939 anAttributeTable
.insertRecord(mVersionId
, &aRecordBuilder
, NULL
);
1942 ModifiedTable
&anIndexTable
= findTable(mDb
.schemaIndexes
.DataRecordType
);
1943 for (uint32 anIndex
= 0; anIndex
< inNumberOfIndexes
; anIndex
++)
1945 // Create an entry for the SchemaIndexes table.
1946 aRecordBuilder
.clear();
1947 aRecordBuilder
.add(RelationID
, inTableId
);
1948 aRecordBuilder
.add(IndexID
, inIndexInfo
[anIndex
].IndexId
);
1949 aRecordBuilder
.add(AttributeID
, inIndexInfo
[anIndex
].AttributeId
);
1950 aRecordBuilder
.add(IndexType
, inIndexInfo
[anIndex
].IndexType
);
1951 aRecordBuilder
.add(IndexedDataLocation
, inIndexInfo
[anIndex
].IndexedDataLocation
);
1953 // Insert the record into the SchemaIndexes ModifiedTable
1954 anIndexTable
.insertRecord(mVersionId
, &aRecordBuilder
, NULL
);
1956 // update the table's index objects
1957 DbMutableIndex
&index
= aTable
->findIndex(inIndexInfo
[anIndex
].IndexId
,
1958 aTable
->getMetaRecord(), inIndexInfo
[anIndex
].IndexType
== CSSM_DB_INDEX_UNIQUE
);
1959 index
.appendAttribute(inIndexInfo
[anIndex
].AttributeId
);
1965 bool DbModifier::hasTable(Table::Id inTableId
)
1967 return getDbVersion(false)->hasTable(inTableId
);
1973 DbModifier::findTable(Table::Id inTableId
)
1975 ModifiedTableMap::iterator it
= mModifiedTableMap
.find(inTableId
);
1976 if (it
== mModifiedTableMap
.end())
1977 CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE
);
1983 // AppleDatabaseManager implementation
1986 AppleDatabaseManager::AppleDatabaseManager(const AppleDatabaseTableName
*tableNames
)
1987 : DatabaseManager(),
1988 mTableNames(tableNames
)
1990 // make sure that a proper set of table ids and names has been provided
1993 CssmError::throwMe(CSSMERR_DL_INTERNAL_ERROR
);
1996 for (i
= 0; mTableNames
[i
].mTableName
; i
++) {}
1997 if (i
< AppleDatabaseTableName::kNumRequiredTableNames
)
1998 CssmError::throwMe(CSSMERR_DL_INTERNAL_ERROR
);
2003 AppleDatabaseManager::make(const DbName
&inDbName
)
2005 return new AppleDatabase(inDbName
, mTableNames
);
2010 // AppleDbContext implementation
2013 /* This is the version 0 CSSM_APPLEDL_OPEN_PARAMETERS struct used up to 10.2.x. */
2016 typedef struct cssm_appledl_open_parameters_v0
2018 uint32 length
; /* Should be sizeof(CSSM_APPLEDL_OPEN_PARAMETERS_V0). */
2019 uint32 version
; /* Should be 0. */
2020 CSSM_BOOL autoCommit
;
2021 } CSSM_APPLEDL_OPEN_PARAMETERS_V0
;
2025 AppleDbContext::AppleDbContext(Database
&inDatabase
,
2026 DatabaseSession
&inDatabaseSession
,
2027 CSSM_DB_ACCESS_TYPE inAccessRequest
,
2028 const AccessCredentials
*inAccessCred
,
2029 const void *inOpenParameters
) :
2030 DbContext(inDatabase
, inDatabaseSession
, inAccessRequest
, inAccessCred
),
2034 const CSSM_APPLEDL_OPEN_PARAMETERS
*anOpenParameters
=
2035 reinterpret_cast<const CSSM_APPLEDL_OPEN_PARAMETERS
*>(inOpenParameters
);
2037 if (anOpenParameters
)
2039 switch (anOpenParameters
->version
)
2042 if (anOpenParameters
->length
< sizeof(CSSM_APPLEDL_OPEN_PARAMETERS
))
2043 CssmError::throwMe(CSSMERR_APPLEDL_INVALID_OPEN_PARAMETERS
);
2045 if (anOpenParameters
->mask
& kCSSM_APPLEDL_MASK_MODE
)
2046 mMode
= anOpenParameters
->mode
;
2049 if (anOpenParameters
->length
< sizeof(CSSM_APPLEDL_OPEN_PARAMETERS_V0
))
2050 CssmError::throwMe(CSSMERR_APPLEDL_INVALID_OPEN_PARAMETERS
);
2052 mAutoCommit
= anOpenParameters
->autoCommit
== CSSM_FALSE
? false : true;
2056 CssmError::throwMe(CSSMERR_APPLEDL_INVALID_OPEN_PARAMETERS
);
2061 AppleDbContext::~AppleDbContext()
2066 // AppleDatabase implementation
2068 AppleDatabase::AppleDatabase(const DbName
&inDbName
, const AppleDatabaseTableName
*tableNames
) :
2070 schemaRelations(tableNames
[AppleDatabaseTableName::kSchemaInfo
].mTableId
,
2071 sizeof(AttrSchemaRelations
) / sizeof(CSSM_DB_ATTRIBUTE_INFO
),
2072 const_cast<CSSM_DB_ATTRIBUTE_INFO_PTR
>(AttrSchemaRelations
)),
2073 schemaAttributes(tableNames
[AppleDatabaseTableName::kSchemaAttributes
].mTableId
,
2074 sizeof(AttrSchemaAttributes
) / sizeof(CSSM_DB_ATTRIBUTE_INFO
),
2075 const_cast<CSSM_DB_ATTRIBUTE_INFO_PTR
>(AttrSchemaAttributes
)),
2076 schemaIndexes(tableNames
[AppleDatabaseTableName::kSchemaIndexes
].mTableId
,
2077 sizeof(AttrSchemaIndexes
) / sizeof(CSSM_DB_ATTRIBUTE_INFO
),
2078 const_cast<CSSM_DB_ATTRIBUTE_INFO_PTR
>(AttrSchemaIndexes
)),
2079 schemaParsingModule(tableNames
[AppleDatabaseTableName::kSchemaParsingModule
].mTableId
,
2080 sizeof(AttrSchemaParsingModule
) / sizeof(CSSM_DB_ATTRIBUTE_INFO
),
2081 const_cast<CSSM_DB_ATTRIBUTE_INFO_PTR
>(AttrSchemaParsingModule
)),
2082 mAtomicFile(mDbName
.dbName()),
2083 mDbModifier(mAtomicFile
, *this),
2084 mTableNames(tableNames
)
2086 /* temp check for X509Anchors access - this should removed before Leopard GM */
2087 if(!strcmp(inDbName
.dbName(), "/System/Library/Keychains/X509Anchors")) {
2088 Syslog::alert("Warning: accessing obsolete X509Anchors.");
2092 AppleDatabase::~AppleDatabase()
2096 // Return the name of a record type. This uses a table that maps record types
2097 // to record names. The table is provided when the database is created.
2099 const char *AppleDatabase::recordName(CSSM_DB_RECORDTYPE inRecordType
) const
2101 if (inRecordType
== CSSM_DL_DB_RECORD_ANY
|| inRecordType
== CSSM_DL_DB_RECORD_ALL_KEYS
)
2102 CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE
);
2104 for (uint32 i
= 0; mTableNames
[i
].mTableName
; i
++)
2105 if (mTableNames
[i
].mTableId
== inRecordType
)
2106 return mTableNames
[i
].mTableName
;
2112 AppleDatabase::makeDbContext(DatabaseSession
&inDatabaseSession
,
2113 CSSM_DB_ACCESS_TYPE inAccessRequest
,
2114 const AccessCredentials
*inAccessCred
,
2115 const void *inOpenParameters
)
2117 return new AppleDbContext(*this, inDatabaseSession
, inAccessRequest
,
2118 inAccessCred
, inOpenParameters
);
2122 AppleDatabase::dbCreate(DbContext
&inDbContext
, const CSSM_DBINFO
&inDBInfo
,
2123 const CSSM_ACL_ENTRY_INPUT
*inInitialAclEntry
)
2125 AppleDbContext
&context
= safer_cast
<AppleDbContext
&>(inDbContext
);
2128 StLock
<Mutex
> _(mWriteLock
);
2129 mDbModifier
.createDatabase(inDBInfo
, inInitialAclEntry
, context
.mode());
2133 mDbModifier
.rollback();
2136 if (context
.autoCommit())
2137 mDbModifier
.commit();
2141 AppleDatabase::dbOpen(DbContext
&inDbContext
)
2143 mDbModifier
.openDatabase();
2147 AppleDatabase::dbClose()
2149 StLock
<Mutex
> _(mWriteLock
);
2150 mDbModifier
.closeDatabase();
2154 AppleDatabase::dbDelete(DatabaseSession
&inDatabaseSession
,
2155 const AccessCredentials
*inAccessCred
)
2157 StLock
<Mutex
> _(mWriteLock
);
2158 // XXX Check callers credentials.
2159 mDbModifier
.deleteDatabase();
2163 AppleDatabase::createRelation(DbContext
&inDbContext
,
2164 CSSM_DB_RECORDTYPE inRelationID
,
2165 const char *inRelationName
,
2166 uint32 inNumberOfAttributes
,
2167 const CSSM_DB_SCHEMA_ATTRIBUTE_INFO
*inAttributeInfo
,
2168 uint32 inNumberOfIndexes
,
2169 const CSSM_DB_SCHEMA_INDEX_INFO
&inIndexInfo
)
2173 StLock
<Mutex
> _(mWriteLock
);
2174 // XXX Fix the refs here.
2175 mDbModifier
.insertTable(inRelationID
, inRelationName
,
2176 inNumberOfAttributes
, inAttributeInfo
,
2177 inNumberOfIndexes
, &inIndexInfo
);
2181 if (safer_cast
<AppleDbContext
&>(inDbContext
).autoCommit())
2182 mDbModifier
.rollback();
2185 if (safer_cast
<AppleDbContext
&>(inDbContext
).autoCommit())
2186 mDbModifier
.commit();
2190 AppleDatabase::destroyRelation(DbContext
&inDbContext
,
2191 CSSM_DB_RECORDTYPE inRelationID
)
2195 StLock
<Mutex
> _(mWriteLock
);
2196 mDbModifier
.deleteTable(inRelationID
);
2200 if (safer_cast
<AppleDbContext
&>(inDbContext
).autoCommit())
2201 mDbModifier
.rollback();
2204 if (safer_cast
<AppleDbContext
&>(inDbContext
).autoCommit())
2205 mDbModifier
.commit();
2209 AppleDatabase::authenticate(DbContext
&inDbContext
,
2210 CSSM_DB_ACCESS_TYPE inAccessRequest
,
2211 const AccessCredentials
&inAccessCred
)
2213 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED
);
2217 AppleDatabase::getDbAcl(DbContext
&inDbContext
,
2218 const CSSM_STRING
*inSelectionTag
,
2219 uint32
&outNumberOfAclInfos
,
2220 CSSM_ACL_ENTRY_INFO_PTR
&outAclInfos
)
2222 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED
);
2226 AppleDatabase::changeDbAcl(DbContext
&inDbContext
,
2227 const AccessCredentials
&inAccessCred
,
2228 const CSSM_ACL_EDIT
&inAclEdit
)
2230 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED
);
2234 AppleDatabase::getDbOwner(DbContext
&inDbContext
,
2235 CSSM_ACL_OWNER_PROTOTYPE
&outOwner
)
2237 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED
);
2241 AppleDatabase::changeDbOwner(DbContext
&inDbContext
,
2242 const AccessCredentials
&inAccessCred
,
2243 const CSSM_ACL_OWNER_PROTOTYPE
&inNewOwner
)
2245 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED
);
2249 AppleDatabase::getDbNameFromHandle(const DbContext
&inDbContext
) const
2251 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED
);
2254 CSSM_DB_UNIQUE_RECORD_PTR
2255 AppleDatabase::dataInsert(DbContext
&inDbContext
,
2256 CSSM_DB_RECORDTYPE inRecordType
,
2257 const CSSM_DB_RECORD_ATTRIBUTE_DATA
*inAttributes
,
2258 const CssmData
*inData
)
2260 CSSM_DB_UNIQUE_RECORD_PTR anUniqueRecordPtr
= NULL
;
2263 StLock
<Mutex
> _(mWriteLock
);
2264 const RecordId aRecordId
=
2265 mDbModifier
.insertRecord(inRecordType
, inAttributes
, inData
);
2267 anUniqueRecordPtr
= createUniqueRecord(inDbContext
, inRecordType
,
2269 if (safer_cast
<AppleDbContext
&>(inDbContext
).autoCommit())
2270 mDbModifier
.commit();
2274 if (anUniqueRecordPtr
!= NULL
)
2275 freeUniqueRecord(inDbContext
, *anUniqueRecordPtr
);
2277 if (safer_cast
<AppleDbContext
&>(inDbContext
).autoCommit())
2278 mDbModifier
.rollback();
2282 return anUniqueRecordPtr
;
2286 AppleDatabase::dataDelete(DbContext
&inDbContext
,
2287 const CSSM_DB_UNIQUE_RECORD
&inUniqueRecord
)
2291 // syslog if it's the .Mac password
2292 CSSM_DB_RECORD_ATTRIBUTE_DATA attrData
;
2293 // 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
2295 memset(&attrData
, 0, sizeof(attrData
));
2296 dataGetFromUniqueRecordId(inDbContext
, inUniqueRecord
, &attrData
, NULL
);
2298 if (attrData
.DataRecordType
== CSSM_DL_DB_RECORD_GENERIC_PASSWORD
)
2300 CSSM_DB_ATTRIBUTE_DATA attributes
;
2302 // setup some attributes and see if we are indeed the .Mac password
2303 attributes
.Info
.AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER
;
2304 attributes
.Info
.Label
.AttributeID
= 'svce';
2305 attributes
.Info
.AttributeFormat
= 0;
2306 attributes
.NumberOfValues
= 1;
2307 attributes
.Value
= NULL
;
2309 attrData
.NumberOfAttributes
= 1;
2310 attrData
.AttributeData
= &attributes
;
2312 dataGetFromUniqueRecordId(inDbContext
, inUniqueRecord
, &attrData
, NULL
);
2314 // now check the results
2315 std::string
dataString((const char*) attrData
.AttributeData
[0].Value
[0].Data
, attrData
.AttributeData
[0].Value
[0].Length
);
2316 if (dataString
== "iTools")
2318 syslog(LOG_WARNING
, "Warning: Removed .Me password");
2321 free(attrData
.AttributeData
[0].Value
[0].Data
);
2322 free(attrData
.AttributeData
[0].Value
);
2325 StLock
<Mutex
> _(mWriteLock
);
2327 const RecordId
aRecordId(parseUniqueRecord(inUniqueRecord
, aTableId
));
2328 mDbModifier
.deleteRecord(aTableId
, aRecordId
);
2332 if (safer_cast
<AppleDbContext
&>(inDbContext
).autoCommit())
2333 mDbModifier
.rollback();
2337 if (safer_cast
<AppleDbContext
&>(inDbContext
).autoCommit())
2338 mDbModifier
.commit();
2342 AppleDatabase::dataModify(DbContext
&inDbContext
,
2343 CSSM_DB_RECORDTYPE inRecordType
,
2344 CSSM_DB_UNIQUE_RECORD
&inoutUniqueRecord
,
2345 const CSSM_DB_RECORD_ATTRIBUTE_DATA
*inAttributesToBeModified
,
2346 const CssmData
*inDataToBeModified
,
2347 CSSM_DB_MODIFY_MODE inModifyMode
)
2351 StLock
<Mutex
> _(mWriteLock
);
2353 const RecordId oldRecordId
= parseUniqueRecord(inoutUniqueRecord
,
2356 if (inRecordType
!= aTableId
)
2358 if (inRecordType
!= aTableId
&&
2359 inRecordType
!= CSSM_DL_DB_RECORD_ANY
&&
2360 !(inRecordType
== CSSM_DL_DB_RECORD_ALL_KEYS
&&
2361 (aTableId
== CSSM_DL_DB_RECORD_PUBLIC_KEY
||
2362 aTableId
== CSSM_DL_DB_RECORD_PRIVATE_KEY
||
2363 aTableId
== CSSM_DL_DB_RECORD_SYMMETRIC_KEY
)))
2366 CssmError::throwMe(CSSMERR_DL_INVALID_RECORD_UID
);
2369 const RecordId newRecordId
=
2370 mDbModifier
.updateRecord(aTableId
,
2372 inAttributesToBeModified
,
2375 updateUniqueRecord(inDbContext
, aTableId
, newRecordId
,
2380 if (safer_cast
<AppleDbContext
&>(inDbContext
).autoCommit())
2381 mDbModifier
.rollback();
2385 if (safer_cast
<AppleDbContext
&>(inDbContext
).autoCommit())
2386 mDbModifier
.commit();
2390 AppleDatabase::dataGetFirst(DbContext
&inDbContext
,
2391 const CssmQuery
*inQuery
,
2392 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes
,
2393 CssmData
*inoutData
,
2394 CSSM_DB_UNIQUE_RECORD_PTR
&outUniqueRecord
)
2396 // XXX: register Cursor with DbContext and have DbContext call
2397 // dataAbortQuery for all outstanding Query objects on close.
2398 auto_ptr
<Cursor
> aCursor(mDbModifier
.createCursor(inQuery
));
2402 if (!aCursor
->next(aTableId
, inoutAttributes
, inoutData
,
2403 inDbContext
.mDatabaseSession
, aRecordId
))
2404 // return a NULL handle, and implicitly delete the cursor
2405 return CSSM_INVALID_HANDLE
;
2407 outUniqueRecord
= createUniqueRecord(inDbContext
, aTableId
, aRecordId
);
2408 return aCursor
.release()->handle(); // We didn't throw so keep the Cursor around.
2412 AppleDatabase::dataGetNext(DbContext
&inDbContext
,
2413 CSSM_HANDLE inResultsHandle
,
2414 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes
,
2415 CssmData
*inoutData
,
2416 CSSM_DB_UNIQUE_RECORD_PTR
&outUniqueRecord
)
2418 auto_ptr
<Cursor
> aCursor(&HandleObject::find
<Cursor
>(inResultsHandle
, CSSMERR_DL_INVALID_RESULTS_HANDLE
));
2422 if (!aCursor
->next(aTableId
, inoutAttributes
, inoutData
, inDbContext
.mDatabaseSession
, aRecordId
))
2425 outUniqueRecord
= createUniqueRecord(inDbContext
, aTableId
, aRecordId
);
2432 AppleDatabase::dataAbortQuery(DbContext
&inDbContext
,
2433 CSSM_HANDLE inResultsHandle
)
2435 delete &HandleObject::find
<Cursor
>(inResultsHandle
, CSSMERR_DL_INVALID_RESULTS_HANDLE
);
2439 AppleDatabase::dataGetFromUniqueRecordId(DbContext
&inDbContext
,
2440 const CSSM_DB_UNIQUE_RECORD
&inUniqueRecord
,
2441 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes
,
2442 CssmData
*inoutData
)
2445 const RecordId
aRecordId(parseUniqueRecord(inUniqueRecord
, aTableId
));
2446 // XXX Change CDSA spec to use new RecordId returned by this function
2447 mDbModifier
.getRecord(aTableId
, aRecordId
, inoutAttributes
, inoutData
,
2448 inDbContext
.mDatabaseSession
);
2452 AppleDatabase::freeUniqueRecord(DbContext
&inDbContext
,
2453 CSSM_DB_UNIQUE_RECORD
&inUniqueRecord
)
2455 if (inUniqueRecord
.RecordIdentifier
.Length
!= 0
2456 && inUniqueRecord
.RecordIdentifier
.Data
!= NULL
)
2458 inUniqueRecord
.RecordIdentifier
.Length
= 0;
2459 inDbContext
.mDatabaseSession
.free(inUniqueRecord
.RecordIdentifier
.Data
);
2461 inDbContext
.mDatabaseSession
.free(&inUniqueRecord
);
2465 AppleDatabase::updateUniqueRecord(DbContext
&inDbContext
,
2466 CSSM_DB_RECORDTYPE inTableId
,
2467 const RecordId
&inRecordId
,
2468 CSSM_DB_UNIQUE_RECORD
&inoutUniqueRecord
)
2470 uint32
*aBuffer
= reinterpret_cast<uint32
*>(inoutUniqueRecord
.RecordIdentifier
.Data
);
2471 aBuffer
[0] = inTableId
;
2472 aBuffer
[1] = inRecordId
.mRecordNumber
;
2473 aBuffer
[2] = inRecordId
.mCreateVersion
;
2474 aBuffer
[3] = inRecordId
.mRecordVersion
;
2477 CSSM_DB_UNIQUE_RECORD_PTR
2478 AppleDatabase::createUniqueRecord(DbContext
&inDbContext
,
2479 CSSM_DB_RECORDTYPE inTableId
,
2480 const RecordId
&inRecordId
)
2482 CSSM_DB_UNIQUE_RECORD_PTR aUniqueRecord
=
2483 inDbContext
.mDatabaseSession
.alloc
<CSSM_DB_UNIQUE_RECORD
>();
2484 memset(aUniqueRecord
, 0, sizeof(*aUniqueRecord
));
2485 aUniqueRecord
->RecordIdentifier
.Length
= sizeof(uint32
) * 4;
2488 aUniqueRecord
->RecordIdentifier
.Data
=
2489 inDbContext
.mDatabaseSession
.alloc
<uint8
>(sizeof(uint32
) * 4);
2490 updateUniqueRecord(inDbContext
, inTableId
, inRecordId
, *aUniqueRecord
);
2494 inDbContext
.mDatabaseSession
.free(aUniqueRecord
);
2498 return aUniqueRecord
;
2502 AppleDatabase::parseUniqueRecord(const CSSM_DB_UNIQUE_RECORD
&inUniqueRecord
,
2503 CSSM_DB_RECORDTYPE
&outTableId
)
2505 if (inUniqueRecord
.RecordIdentifier
.Length
!= sizeof(uint32
) * 4)
2506 CssmError::throwMe(CSSMERR_DL_INVALID_RECORD_UID
);
2508 uint32
*aBuffer
= reinterpret_cast<uint32
*>(inUniqueRecord
.RecordIdentifier
.Data
);
2509 outTableId
= aBuffer
[0];
2510 return RecordId(aBuffer
[1], aBuffer
[2], aBuffer
[3]);
2514 AppleDatabase::passThrough(DbContext
&dbContext
,
2515 uint32 passThroughId
,
2516 const void *inputParams
,
2517 void **outputParams
)
2519 switch (passThroughId
)
2521 case CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT
:
2523 AppleDbContext
&dbc
= safer_cast
<AppleDbContext
&>(dbContext
);
2524 // Return the old state of the autoCommit flag if requested
2526 *reinterpret_cast<CSSM_BOOL
*>(outputParams
) = dbc
.autoCommit();
2527 dbc
.autoCommit(inputParams
? CSSM_TRUE
: CSSM_FALSE
);
2531 case CSSM_APPLEFILEDL_COMMIT
:
2532 mDbModifier
.commit();
2535 case CSSM_APPLEFILEDL_ROLLBACK
:
2536 mDbModifier
.rollback();
2539 case CSSM_APPLECSPDL_DB_RELATION_EXISTS
:
2541 CSSM_BOOL returnValue
;
2543 CSSM_DB_RECORDTYPE recordType
= *(CSSM_DB_RECORDTYPE
*) inputParams
;
2544 if (recordType
== CSSM_DL_DB_RECORD_ANY
|| recordType
== CSSM_DL_DB_RECORD_ALL_KEYS
)
2546 returnValue
= CSSM_TRUE
;
2550 returnValue
= mDbModifier
.hasTable(recordType
);
2553 *(CSSM_BOOL
*) outputParams
= returnValue
;
2558 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED
);