X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/libsecurity_filedb/lib/AppleDatabase.cpp diff --git a/libsecurity_filedb/lib/AppleDatabase.cpp b/libsecurity_filedb/lib/AppleDatabase.cpp deleted file mode 100644 index 4d640ebd..00000000 --- a/libsecurity_filedb/lib/AppleDatabase.cpp +++ /dev/null @@ -1,2561 +0,0 @@ -/* - * Copyright (c) 2000-2001, 2003 Apple Computer, Inc. All Rights Reserved. - * - * The contents of this file constitute Original Code as defined in and are - * subject to the Apple Public Source License Version 1.2 (the 'License'). - * You may not use this file except in compliance with the License. Please obtain - * a copy of the License at http://www.apple.com/publicsource and read it before - * using this file. - * - * This Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS - * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT - * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR - * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the - * specific language governing rights and limitations under the License. - */ - - -// -// AppleDatabase.cpp - Description t.b.d. -// -#include "AppleDatabase.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static const char *kAppleDatabaseChanged = "com.apple.AppleDatabaseChanged"; - -/* Number of seconds after which we open/pread/close a db to check it's - version number even if we didn't get any notifications. Note that we always - check just after we take a write lock and whenever we get a notification - that any db on the system has changed. */ -static const CFTimeInterval kForceReReadTime = 15.0; - -/* Token on which we receive notifications and the pthread_once_t protecting - it's initialization. */ -pthread_once_t gCommonInitMutex = PTHREAD_ONCE_INIT; - -/* Global counter of how many notifications we have received and a lock to - protect the counter. */ -static int kSegmentSize = 4; -int32_t* gSegment = NULL; - -/* Registration routine for notifcations. Called inside a pthread_once(). */ -static void initCommon(void) -{ - // open the file - int segmentDescriptor = shm_open (kAppleDatabaseChanged, O_RDWR | O_CREAT, S_IRWXU | S_IRWXG | S_IRWXO); - if (segmentDescriptor < 0) - { - return; - } - - // set the segment size - ftruncate (segmentDescriptor, kSegmentSize); - - // map it into memory - int32_t* tmp = (int32_t*) mmap (NULL, kSegmentSize, PROT_READ | PROT_WRITE, MAP_SHARED, segmentDescriptor, 0); - close (segmentDescriptor); - - if (tmp == (int32_t*) -1) // can't map the memory? - { - gSegment = NULL; - } - else - { - gSegment = tmp; - } -} - -// -// Table -// -Table::Table(const ReadSection &inTableSection) : - mMetaRecord(inTableSection[OffsetId]), - mTableSection(inTableSection), - mRecordsCount(inTableSection[OffsetRecordsCount]), - mFreeListHead(inTableSection[OffsetFreeListHead]), - mRecordNumbersCount(inTableSection[OffsetRecordNumbersCount]) -{ - // can't easily initialize indexes here, since meta record is incomplete - // until much later... see DbVersion::open() -} - -Table::~Table() -{ - for_each_map_delete(mIndexMap.begin(), mIndexMap.end()); -} - -void -Table::readIndexSection() -{ - uint32 indexSectionOffset = mTableSection.at(OffsetIndexesOffset); - - uint32 numIndexes = mTableSection.at(indexSectionOffset + AtomSize); - - for (uint32 i = 0; i < numIndexes; i++) { - uint32 indexOffset = mTableSection.at(indexSectionOffset + (i + 2) * AtomSize); - ReadSection indexSection(mTableSection.subsection(indexOffset)); - - auto_ptr index(new DbConstIndex(*this, indexSection)); - mIndexMap.insert(ConstIndexMap::value_type(index->indexId(), index.get())); - index.release(); - } -} - -Cursor * -Table::createCursor(const CSSM_QUERY *inQuery, const DbVersion &inDbVersion) const -{ - // if an index matches the query, return a cursor which uses the index - - ConstIndexMap::const_iterator it; - DbQueryKey *queryKey; - - for (it = mIndexMap.begin(); it != mIndexMap.end(); it++) - if (it->second->matchesQuery(*inQuery, queryKey)) { - IndexCursor *cursor = new IndexCursor(queryKey, inDbVersion, *this, it->second); - return cursor; - } - - // otherwise, return a cursor that iterates over all table records - - return new LinearCursor(inQuery, inDbVersion, *this); -} - -const ReadSection -Table::getRecordSection(uint32 inRecordNumber) const -{ - if (inRecordNumber >= mRecordNumbersCount) - CssmError::throwMe(CSSMERR_DL_INVALID_RECORD_UID); - - uint32 aRecordOffset = mTableSection[OffsetRecordNumbers + AtomSize - * inRecordNumber]; - - // Check if this RecordNumber has been deleted. - if (aRecordOffset & 1 || aRecordOffset == 0) - CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND); - - return MetaRecord::readSection(mTableSection, aRecordOffset); -} - -const RecordId -Table::getRecord(const RecordId &inRecordId, - CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes, - CssmData *inoutData, - Allocator &inAllocator) const -{ - const ReadSection aRecordSection = getRecordSection(inRecordId.mRecordNumber); - const RecordId aRecordId = MetaRecord::unpackRecordId(aRecordSection); - - // Make sure the RecordNumber matches that in the RecordId we just retrived. - if (aRecordId.mRecordNumber != inRecordId.mRecordNumber) - CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT); - - if (aRecordId.mCreateVersion != inRecordId.mCreateVersion) - CssmError::throwMe(CSSMERR_DL_INVALID_RECORD_UID); - - // XXX Figure out which value to pass for inQueryFlags (5th) argument - mMetaRecord.unpackRecord(aRecordSection, inAllocator, inoutAttributes, - inoutData, 0); - return aRecordId; -} - -uint32 -Table::popFreeList(uint32 &aFreeListHead) const -{ - assert(aFreeListHead | 1); - uint32 anOffset = aFreeListHead ^ 1; - uint32 aRecordNumber = (anOffset - OffsetRecordNumbers) / AtomSize; - aFreeListHead = mTableSection[anOffset]; - return aRecordNumber; -} - -const ReadSection -Table::getRecordsSection() const -{ - return mTableSection.subsection(mTableSection[OffsetRecords]); -} - -bool -Table::matchesTableId(Id inTableId) const -{ - Id anId = mMetaRecord.dataRecordType(); - if (inTableId == CSSM_DL_DB_RECORD_ANY) // All non schema tables. - return !(CSSM_DB_RECORDTYPE_SCHEMA_START <= anId - && anId < CSSM_DB_RECORDTYPE_SCHEMA_END); - - if (inTableId == CSSM_DL_DB_RECORD_ALL_KEYS) // All key tables. - return (anId == CSSM_DL_DB_RECORD_PUBLIC_KEY - || anId == CSSM_DL_DB_RECORD_PRIVATE_KEY - || anId == CSSM_DL_DB_RECORD_SYMMETRIC_KEY); - - return inTableId == anId; // Only if exact match. -} - - -// -// ModifiedTable -// -ModifiedTable::ModifiedTable(const Table *inTable) : - mTable(inTable), - mNewMetaRecord(nil), - mRecordNumberCount(inTable->recordNumberCount()), - mFreeListHead(inTable->freeListHead()), - mIsModified(false) -{ -} - -ModifiedTable::ModifiedTable(MetaRecord *inMetaRecord) : - mTable(nil), - mNewMetaRecord(inMetaRecord), - mRecordNumberCount(0), - mFreeListHead(0), - mIsModified(true) -{ -} - -ModifiedTable::~ModifiedTable() -{ - for_each_map_delete(mIndexMap.begin(), mIndexMap.end()); - for_each_map_delete(mInsertedMap.begin(), mInsertedMap.end()); - - delete mNewMetaRecord; -} - -void -ModifiedTable::deleteRecord(const RecordId &inRecordId) -{ - modifyTable(); - - uint32 aRecordNumber = inRecordId.mRecordNumber; - - // remove the record from all the indexes - MutableIndexMap::iterator it; - for (it = mIndexMap.begin(); it != mIndexMap.end(); it++) - it->second->removeRecord(aRecordNumber); - - InsertedMap::iterator anIt = mInsertedMap.find(aRecordNumber); - if (anIt == mInsertedMap.end()) - { - // If we have no old table than this record can not exist yet. - if (!mTable) - CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND); - -#if RECORDVERSIONCHECK - const RecordId aRecordId = MetaRecord::unpackRecordId(mTable->getRecordSection(aRecordNumber)); - if (aRecordId.mRecordVersion != inRecordId.mRecordVersion) - CssmError::throwMe(CSSMERR_DL_RECORD_MODIFIED); -#endif - - // Schedule the record for deletion - if (!mDeletedSet.insert(aRecordNumber).second) - CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND); // It was already deleted - } - else - { - const RecordId aRecordId = MetaRecord::unpackRecordId(*anIt->second); - if (aRecordId.mCreateVersion != inRecordId.mCreateVersion) - CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND); - -#if RECORDVERSIONCHECK - if (aRecordId.mRecordVersion != inRecordId.mRecordVersion) - CssmError::throwMe(CSSMERR_DL_RECORD_MODIFIED); -#endif - - // Remove the inserted (but uncommited) record. It should already be in mDeletedSet - // if it existed previously in mTable. - delete anIt->second; - mInsertedMap.erase(anIt); - } -} - -const RecordId -ModifiedTable::insertRecord(uint32 inVersionId, - const CSSM_DB_RECORD_ATTRIBUTE_DATA *inAttributes, - const CssmData *inData) -{ - modifyTable(); - - auto_ptr aWriteSection(new WriteSection()); - getMetaRecord().packRecord(*aWriteSection, inAttributes, inData); - uint32 aRecordNumber = nextRecordNumber(); - - // add the record to all the indexes; this will throw if the new record - // violates a unique index - MutableIndexMap::iterator it; - for (it = mIndexMap.begin(); it != mIndexMap.end(); it++) - it->second->insertRecord(aRecordNumber, *(aWriteSection.get())); - - // schedule the record for insertion - RecordId aRecordId(aRecordNumber, inVersionId); - MetaRecord::packRecordId(aRecordId, *aWriteSection); - mInsertedMap.insert(InsertedMap::value_type(aRecordNumber, aWriteSection.get())); - - aWriteSection.release(); - - return aRecordId; -} - -const RecordId -ModifiedTable::updateRecord(const RecordId &inRecordId, - const CSSM_DB_RECORD_ATTRIBUTE_DATA *inAttributes, - const CssmData *inData, - CSSM_DB_MODIFY_MODE inModifyMode) -{ - modifyTable(); - - uint32 aRecordNumber = inRecordId.mRecordNumber; - InsertedMap::iterator anIt = mInsertedMap.find(aRecordNumber); - - // aReUpdate is true iff we are updating an already updated record. - bool aReUpdate = anIt != mInsertedMap.end(); - - // If we are not re-updating and there is no old table than this record does not exist yet. - if (!aReUpdate && !mTable) - CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND); - - const ReadSection &anOldDbRecord = aReUpdate ? *anIt->second : mTable->getRecordSection(aRecordNumber); - const RecordId aRecordId = MetaRecord::unpackRecordId(anOldDbRecord); - - // Did someone else delete the record we are trying to update. - if (aRecordId.mCreateVersion != inRecordId.mCreateVersion) - CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND); - -#if RECORDVERSIONCHECK - // Is the record we that our update is based on current? - if (aRecordId.mRecordVersion != inRecordId.mRecordVersion) - CssmError::throwMe(CSSMERR_DL_STALE_UNIQUE_RECORD); -#endif - - // Update the actual packed record. - auto_ptr aDbRecord(new WriteSection()); - getMetaRecord().updateRecord(anOldDbRecord, *aDbRecord, - CssmDbRecordAttributeData::overlay(inAttributes), inData, inModifyMode); - - - // Bump the RecordVersion of this record. - RecordId aNewRecordId(aRecordNumber, inRecordId.mCreateVersion, inRecordId.mRecordVersion + 1); - // Store the RecordVersion in the packed aDbRecord. - MetaRecord::packRecordId(aNewRecordId, *aDbRecord); - - if (!aReUpdate && !mDeletedSet.insert(aRecordNumber).second) - CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND); // Record was already in mDeletedSet - - // remove the original record from all the indexes - MutableIndexMap::iterator it; - for (it = mIndexMap.begin(); it != mIndexMap.end(); it++) - it->second->removeRecord(aRecordNumber); - - try - { - // Add the updated record to all the indexes; this will throw if the new record - // violates a unique index - for (it = mIndexMap.begin(); it != mIndexMap.end(); it++) - it->second->insertRecord(aRecordNumber, *(aDbRecord.get())); - - if (aReUpdate) - { - // Get rid of anOldDbRecord from the inserted map and replace it - // with aDbRecord. - delete anIt->second; - anIt->second = aDbRecord.get(); - } - else - { - // First time though so let's just put the new value in the map. - mInsertedMap.insert(InsertedMap::value_type(aRecordNumber, aDbRecord.get())); - } - aDbRecord.release(); - } - catch(...) - { - // We only remove aRecordNumber from mDeletedSet if we added it above. - if (!aReUpdate) - mDeletedSet.erase(aRecordNumber); - - // The 2 operations below are an attempt to preserve the indices when - // an insert fails. - - // Remove the updated record from all the indexes - MutableIndexMap::iterator it; - for (it = mIndexMap.begin(); it != mIndexMap.end(); it++) - it->second->removeRecord(aRecordNumber); - - // Add the original record back to all the indexes - for (it = mIndexMap.begin(); it != mIndexMap.end(); it++) - it->second->insertRecord(aRecordNumber, anOldDbRecord); - - throw; - } - - return aNewRecordId; -} - -const RecordId -ModifiedTable::getRecord(const RecordId &inRecordId, - CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes, - CssmData *inoutData, - Allocator &inAllocator) const -{ - if (mIsModified) - { - uint32 aRecordNumber = inRecordId.mRecordNumber; - InsertedMap::const_iterator anIt = mInsertedMap.find(aRecordNumber); - if (anIt != mInsertedMap.end()) - { - // We found the record in mInsertedMap so we use the inserted - // record. - const ReadSection &aRecordSection = *(anIt->second); - const RecordId aRecordId = MetaRecord::unpackRecordId(aRecordSection); - - // Make sure the RecordNumber matches that in the RecordId we just retrived. - if (aRecordId.mRecordNumber != aRecordNumber) - CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT); - - if (aRecordId.mCreateVersion != inRecordId.mCreateVersion) - CssmError::throwMe(CSSMERR_DL_INVALID_RECORD_UID); - - // XXX Figure out which value to pass for inQueryFlags (5th) argument - getMetaRecord().unpackRecord(aRecordSection, inAllocator, - inoutAttributes, inoutData, 0); - - return aRecordId; - } - else if (mDeletedSet.find(aRecordNumber) != mDeletedSet.end()) - { - // If aRecordNumber was not in mInsertedMap but it was in - // mDeletedSet then it was deleted but not yet commited. - CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND); - } - } - - if (!mTable) - CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND); - - // Either this table wasn't modified yet or we didn't find aRecordNumber in - // mInsertedMap nor mDeletedSet so just ask mTable for it. - return mTable->getRecord(inRecordId, inoutAttributes, inoutData, - inAllocator); -} - -uint32 -ModifiedTable::nextRecordNumber() -{ - // If we still have unused free records in mTable get the next one. - if (mFreeListHead) - return mTable->popFreeList(mFreeListHead); - - // Bump up the mRecordNumberCount so we don't reuse the same one. - return mRecordNumberCount++; -} - -uint32 -ModifiedTable::recordNumberCount() const -{ - uint32 anOldMax = !mTable ? 0 : mTable->recordNumberCount() - 1; - uint32 anInsertedMax = mInsertedMap.empty() ? 0 : mInsertedMap.rbegin()->first; - - DeletedSet::reverse_iterator anIt = mDeletedSet.rbegin(); - DeletedSet::reverse_iterator anEnd = mDeletedSet.rend(); - for (; anIt != anEnd; anIt++) - { - if (*anIt != anOldMax || anOldMax <= anInsertedMax) - break; - anOldMax--; - } - - return max(anOldMax,anInsertedMax) + 1; -} - -const MetaRecord & -ModifiedTable::getMetaRecord() const -{ - return mNewMetaRecord ? *mNewMetaRecord : mTable->getMetaRecord(); -} - -// prepare to modify the table - -void -ModifiedTable::modifyTable() -{ - if (!mIsModified) { - createMutableIndexes(); - mIsModified = true; - } -} - -// create mutable indexes from the read-only indexes in the underlying table - -void -ModifiedTable::createMutableIndexes() -{ - if (mTable == NULL) - return; - - Table::ConstIndexMap::const_iterator it; - for (it = mTable->mIndexMap.begin(); it != mTable->mIndexMap.end(); it++) { - auto_ptr mutableIndex(new DbMutableIndex(*it->second)); - mIndexMap.insert(MutableIndexMap::value_type(it->first, mutableIndex.get())); - mutableIndex.release(); - } -} - -// find, and create if needed, an index with the given id - -DbMutableIndex & -ModifiedTable::findIndex(uint32 indexId, const MetaRecord &metaRecord, bool isUniqueIndex) -{ - MutableIndexMap::iterator it = mIndexMap.find(indexId); - - if (it == mIndexMap.end()) { - // create the new index - auto_ptr index(new DbMutableIndex(metaRecord, indexId, isUniqueIndex)); - it = mIndexMap.insert(MutableIndexMap::value_type(indexId, index.get())).first; - index.release(); - } - - return *it->second; -} - -uint32 -ModifiedTable::writeIndexSection(WriteSection &tableSection, uint32 offset) -{ - MutableIndexMap::iterator it; - - tableSection.put(Table::OffsetIndexesOffset, offset); - - // leave room for the size, to be written later - uint32 indexSectionOffset = offset; - offset += AtomSize; - - offset = tableSection.put(offset, (uint32)mIndexMap.size()); - - // leave room for the array of offsets to the indexes - uint32 indexOffsetOffset = offset; - offset += mIndexMap.size() * AtomSize; - - // write the indexes - for (it = mIndexMap.begin(); it != mIndexMap.end(); it++) { - indexOffsetOffset = tableSection.put(indexOffsetOffset, offset); - offset = it->second->writeIndex(tableSection, offset); - } - - // write the total index section size - tableSection.put(indexSectionOffset, offset - indexSectionOffset); - - return offset; -} - -uint32 -ModifiedTable::writeTable(AtomicTempFile &inAtomicTempFile, uint32 inSectionOffset) -{ - if (mTable && !mIsModified) { - // the table has not been modified, so we can just dump the old table - // section into the new database - - const ReadSection &tableSection = mTable->getTableSection(); - uint32 tableSize = tableSection.at(Table::OffsetSize); - - inAtomicTempFile.write(AtomicFile::FromStart, inSectionOffset, - tableSection.range(Range(0, tableSize)), tableSize); - - return inSectionOffset + tableSize; - } - - // We should have an old mTable or a mNewMetaRecord but not both. - assert(mTable != nil ^ mNewMetaRecord != nil); - const MetaRecord &aNewMetaRecord = getMetaRecord(); - - uint32 aRecordsCount = 0; - uint32 aRecordNumbersCount = recordNumberCount(); - uint32 aRecordsOffset = Table::OffsetRecordNumbers + AtomSize * aRecordNumbersCount; - WriteSection aTableSection(Allocator::standard(), aRecordsOffset); - aTableSection.size(aRecordsOffset); - aTableSection.put(Table::OffsetId, aNewMetaRecord.dataRecordType()); - aTableSection.put(Table::OffsetRecords, aRecordsOffset); - aTableSection.put(Table::OffsetRecordNumbersCount, aRecordNumbersCount); - - uint32 anOffset = inSectionOffset + aRecordsOffset; - - if (mTable) - { - // XXX Handle schema changes in the future. - assert(mNewMetaRecord == nil); - - // We have a modified old table so copy all non deleted records - // The code below is rather elaborate, but this is because it attempts - // to copy large ranges of non deleted records with single calls - // to AtomicFile::write() - uint32 anOldRecordsCount = mTable->getRecordsCount(); - ReadSection aRecordsSection = mTable->getRecordsSection(); - uint32 aReadOffset = 0; // Offset of current record - uint32 aWriteOffset = aRecordsOffset; // Offset for current write record - uint32 aBlockStart = aReadOffset; // Starting point for read - uint32 aBlockSize = 0; // Size of block to read - for (uint32 aRecord = 0; aRecord < anOldRecordsCount; aRecord++) - { - ReadSection aRecordSection = MetaRecord::readSection(aRecordsSection, aReadOffset); - uint32 aRecordNumber = MetaRecord::unpackRecordNumber(aRecordSection); - uint32 aRecordSize = aRecordSection.size(); - aReadOffset += aRecordSize; - if (mDeletedSet.find(aRecordNumber) == mDeletedSet.end()) - { - // This record has not been deleted. Register the offset - // at which it will be in the new file in aTableSection. - aTableSection.put(Table::OffsetRecordNumbers - + AtomSize * aRecordNumber, - aWriteOffset); - aWriteOffset += aRecordSize; - aBlockSize += aRecordSize; - aRecordsCount++; - // XXX update all indexes being created. - } - else - { - // The current record has been deleted. Copy all records up - // to but not including the current one to the new file. - if (aBlockSize > 0) - { - inAtomicTempFile.write(AtomicFile::FromStart, anOffset, - aRecordsSection.range(Range(aBlockStart, - aBlockSize)), - aBlockSize); - anOffset += aBlockSize; - } - - // Set the start of the next block to the start of the next - // record, and the size of the block to 0. - aBlockStart = aReadOffset; - aBlockSize = 0; - } // if (mDeletedSet..) - } // for (aRecord...) - - // Copy all records that have not yet been copied to the new file. - if (aBlockSize > 0) - { - inAtomicTempFile.write(AtomicFile::FromStart, anOffset, - aRecordsSection.range(Range(aBlockStart, - aBlockSize)), - aBlockSize); - anOffset += aBlockSize; - } - } // if (mTable) - - // Now add all inserted records to the table. - InsertedMap::const_iterator anIt = mInsertedMap.begin(); - InsertedMap::const_iterator anEnd = mInsertedMap.end(); - // Iterate over all inserted objects. - for (; anIt != anEnd; anIt++) - { - // Write out each inserted/modified record - const WriteSection &aRecord = *anIt->second; - uint32 aRecordNumber = anIt->first; - // Put offset relative to start of this table in recordNumber array. - aTableSection.put(Table::OffsetRecordNumbers + AtomSize * aRecordNumber, - anOffset - inSectionOffset); - inAtomicTempFile.write(AtomicFile::FromStart, anOffset, - aRecord.address(), aRecord.size()); - anOffset += aRecord.size(); - aRecordsCount++; - // XXX update all indexes being created. - } - - // Reconstruct the freelist (this is O(N) where N is the number of recordNumbers) - // We could implement it faster by using the old freelist and skipping the records - // that have been inserted. However building the freelist for the newly used - // recordNumbers (not in mTable) would look like the code below anyway (starting - // from mTable->recordNumberCount()). - // The first part of this would be O(M Log(N)) (where M is the old number of - // free records, and N is the number of newly inserted records) - // The second part would be O(N) where N is the currently max RecordNumber - // in use - the old max RecordNumber in use. - uint32 aFreeListHead = 0; // Link to previous free record - for (uint32 aRecordNumber = 0; aRecordNumber < aRecordNumbersCount; aRecordNumber++) - { - // Make the freelist a list of all records with 0 offset (non existing). - if (!aTableSection.at(Table::OffsetRecordNumbers + AtomSize * aRecordNumber)) - { - aTableSection.put(Table::OffsetRecordNumbers - + AtomSize * aRecordNumber, - aFreeListHead); - // Make aFreeListHead point to the previous free recordNumber slot in the table. - aFreeListHead = (Table::OffsetRecordNumbers + AtomSize * aRecordNumber) | 1; - } - } - aTableSection.put(Table::OffsetFreeListHead, aFreeListHead); - - anOffset -= inSectionOffset; - - // Write out indexes, which are part of the table section - - { - uint32 indexOffset = anOffset; - anOffset = writeIndexSection(aTableSection, anOffset); - inAtomicTempFile.write(AtomicFile::FromStart, inSectionOffset + indexOffset, - aTableSection.address() + indexOffset, anOffset - indexOffset); - } - - // Set the section size and recordCount. - aTableSection.put(Table::OffsetSize, anOffset); - aTableSection.put(Table::OffsetRecordsCount, aRecordsCount); - - // Write out aTableSection header. - inAtomicTempFile.write(AtomicFile::FromStart, inSectionOffset, - aTableSection.address(), aTableSection.size()); - - return anOffset + inSectionOffset; -} - - - -// -// Metadata -// - -// Attribute definitions - -static const CSSM_DB_ATTRIBUTE_INFO RelationID = -{ - CSSM_DB_ATTRIBUTE_NAME_AS_STRING, - {(char*) "RelationID"}, - CSSM_DB_ATTRIBUTE_FORMAT_UINT32 -}; -static const CSSM_DB_ATTRIBUTE_INFO RelationName = -{ - CSSM_DB_ATTRIBUTE_NAME_AS_STRING, - {(char*) "RelationName"}, - CSSM_DB_ATTRIBUTE_FORMAT_STRING -}; -static const CSSM_DB_ATTRIBUTE_INFO AttributeID = -{ - CSSM_DB_ATTRIBUTE_NAME_AS_STRING, - {(char*) "AttributeID"}, - CSSM_DB_ATTRIBUTE_FORMAT_UINT32 -}; -static const CSSM_DB_ATTRIBUTE_INFO AttributeNameFormat = -{ - CSSM_DB_ATTRIBUTE_NAME_AS_STRING, - {(char*) "AttributeNameFormat"}, - CSSM_DB_ATTRIBUTE_FORMAT_UINT32 -}; -static const CSSM_DB_ATTRIBUTE_INFO AttributeName = -{ - CSSM_DB_ATTRIBUTE_NAME_AS_STRING, - {(char*) "AttributeName"}, - CSSM_DB_ATTRIBUTE_FORMAT_STRING -}; -static const CSSM_DB_ATTRIBUTE_INFO AttributeNameID = -{ - CSSM_DB_ATTRIBUTE_NAME_AS_STRING, - {(char*) "AttributeNameID"}, - CSSM_DB_ATTRIBUTE_FORMAT_BLOB -}; -static const CSSM_DB_ATTRIBUTE_INFO AttributeFormat = -{ - CSSM_DB_ATTRIBUTE_NAME_AS_STRING, - {(char*) "AttributeFormat"}, - CSSM_DB_ATTRIBUTE_FORMAT_UINT32 -}; -static const CSSM_DB_ATTRIBUTE_INFO IndexID = -{ - CSSM_DB_ATTRIBUTE_NAME_AS_STRING, - {(char*) "IndexID"}, - CSSM_DB_ATTRIBUTE_FORMAT_UINT32 -}; -static const CSSM_DB_ATTRIBUTE_INFO IndexType = -{ - CSSM_DB_ATTRIBUTE_NAME_AS_STRING, - {(char*) "IndexType"}, - CSSM_DB_ATTRIBUTE_FORMAT_UINT32 -}; -static const CSSM_DB_ATTRIBUTE_INFO IndexedDataLocation = -{ - CSSM_DB_ATTRIBUTE_NAME_AS_STRING, - {(char*) "IndexedDataLocation"}, - CSSM_DB_ATTRIBUTE_FORMAT_UINT32 -}; -static const CSSM_DB_ATTRIBUTE_INFO ModuleID = -{ - CSSM_DB_ATTRIBUTE_NAME_AS_STRING, - {(char*) "ModuleID"}, - CSSM_DB_ATTRIBUTE_FORMAT_BLOB -}; -static const CSSM_DB_ATTRIBUTE_INFO AddinVersion = -{ - CSSM_DB_ATTRIBUTE_NAME_AS_STRING, - {(char*) "AddinVersion"}, - CSSM_DB_ATTRIBUTE_FORMAT_STRING -}; -static const CSSM_DB_ATTRIBUTE_INFO SSID = -{ - CSSM_DB_ATTRIBUTE_NAME_AS_STRING, - {(char*) "SSID"}, - CSSM_DB_ATTRIBUTE_FORMAT_UINT32 -}; -static const CSSM_DB_ATTRIBUTE_INFO SubserviceType = -{ - CSSM_DB_ATTRIBUTE_NAME_AS_STRING, - {(char*) "SubserviceType"}, - CSSM_DB_ATTRIBUTE_FORMAT_UINT32 -}; - -#define ATTRIBUTE(type, name) \ - { CSSM_DB_ATTRIBUTE_NAME_AS_STRING, { (char*) #name }, CSSM_DB_ATTRIBUTE_FORMAT_ ## type } - -static const CSSM_DB_ATTRIBUTE_INFO AttrSchemaRelations[] = -{ - //RelationID, RelationName - ATTRIBUTE(UINT32, RelationID), - ATTRIBUTE(STRING, RelationName) -}; - -static const CSSM_DB_ATTRIBUTE_INFO AttrSchemaAttributes[] = -{ - //RelationID, AttributeID, - //AttributeNameFormat, AttributeName, AttributeNameID, - //AttributeFormat - ATTRIBUTE(UINT32, RelationID), - ATTRIBUTE(UINT32, AttributeID), - ATTRIBUTE(UINT32, AttributeNameFormat), - ATTRIBUTE(STRING, AttributeName), - ATTRIBUTE(BLOB, AttributeNameID), - ATTRIBUTE(UINT32, AttributeFormat) -}; - -static const CSSM_DB_ATTRIBUTE_INFO AttrSchemaIndexes[] = -{ - ATTRIBUTE(UINT32, RelationID), - ATTRIBUTE(UINT32, IndexID), - ATTRIBUTE(UINT32, AttributeID), - ATTRIBUTE(UINT32, IndexType), - ATTRIBUTE(UINT32, IndexedDataLocation) - //RelationID, IndexID, AttributeID, - //IndexType, IndexedDataLocation -}; - -static const CSSM_DB_ATTRIBUTE_INFO AttrSchemaParsingModule[] = -{ - ATTRIBUTE(UINT32, RelationID), - ATTRIBUTE(UINT32, AttributeID), - ATTRIBUTE(BLOB, ModuleID), - ATTRIBUTE(STRING, AddinVersion), - ATTRIBUTE(UINT32, SSID), - ATTRIBUTE(UINT32, SubserviceType) - //RelationID, AttributeID, - //ModuleID, AddinVersion, SSID, SubserviceType -}; - -#undef ATTRIBUTE - -// -// DbVersion -// -DbVersion::DbVersion(const AppleDatabase &db, const RefPointer &inAtomicBufferedFile) : - mDatabase(reinterpret_cast(NULL), 0), - mDb(db), - mBufferedFile(inAtomicBufferedFile) -{ - off_t aLength = mBufferedFile->length(); - off_t bytesRead = 0; - const uint8 *ptr = mBufferedFile->read(0, aLength, bytesRead); - mBufferedFile->close(); - mDatabase = ReadSection(ptr, (size_t)bytesRead); - open(); -} - -DbVersion::~DbVersion() -{ - try - { - for_each_map_delete(mTableMap.begin(), mTableMap.end()); - } - catch(...) {} -} - -void -DbVersion::open() -{ - try - { - // This is the oposite of DbModifier::commit() - mVersionId = mDatabase[mDatabase.size() - AtomSize]; - - const ReadSection aHeaderSection = mDatabase.subsection(HeaderOffset, - HeaderSize); - if (aHeaderSection.at(OffsetMagic) != HeaderMagic) - CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT); - - // We currently only support one version. If we support additional - // file format versions in the future fix this. - uint32 aVersion = aHeaderSection.at(OffsetVersion); - if (aVersion != HeaderVersion) - CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT); - - //const ReadSection anAuthSection = - // mDatabase.subsection(HeaderOffset + aHeaderSection.at(OffsetAuthOffset)); - // XXX Do something with anAuthSection. - - uint32 aSchemaOffset = aHeaderSection.at(OffsetSchemaOffset); - const ReadSection aSchemaSection = - mDatabase.subsection(HeaderOffset + aSchemaOffset); - - uint32 aSchemaSize = aSchemaSection[OffsetSchemaSize]; - // Make sure that the given range exists. - aSchemaSection.subsection(0, aSchemaSize); - uint32 aTableCount = aSchemaSection[OffsetTablesCount]; - - // Assert that the size of this section is big enough. - if (aSchemaSize < OffsetTables + AtomSize * aTableCount) - CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT); - - for (uint32 aTableNumber = 0; aTableNumber < aTableCount; - aTableNumber++) - { - uint32 aTableOffset = aSchemaSection.at(OffsetTables + AtomSize - * aTableNumber); - // XXX Set the size boundary on aTableSection. - const ReadSection aTableSection = - aSchemaSection.subsection(aTableOffset); - auto_ptr aTable(new Table(aTableSection)); - Table::Id aTableId = aTable->getMetaRecord().dataRecordType(); - mTableMap.insert(TableMap::value_type(aTableId, aTable.get())); - aTable.release(); - } - - // Fill in the schema for the meta tables. - - findTable(mDb.schemaRelations.DataRecordType).getMetaRecord(). - setRecordAttributeInfo(mDb.schemaRelations); - findTable(mDb.schemaIndexes.DataRecordType).getMetaRecord(). - setRecordAttributeInfo(mDb.schemaIndexes); - findTable(mDb.schemaParsingModule.DataRecordType).getMetaRecord(). - setRecordAttributeInfo(mDb.schemaParsingModule); - - // OK, we have created all the tables in the tableMap. Now - // lets read the schema and proccess it accordingly. - // Iterate over all schema records. - Table &aTable = findTable(mDb.schemaAttributes.DataRecordType); - aTable.getMetaRecord().setRecordAttributeInfo(mDb.schemaAttributes); - uint32 aRecordsCount = aTable.getRecordsCount(); - ReadSection aRecordsSection = aTable.getRecordsSection(); - uint32 aReadOffset = 0; - const MetaRecord &aMetaRecord = aTable.getMetaRecord(); - - CSSM_DB_ATTRIBUTE_DATA aRelationIDData = - { - RelationID, - 0, - NULL - }; - CSSM_DB_ATTRIBUTE_DATA aAttributeIDData = - { - AttributeID, - 0, - NULL - }; - CSSM_DB_ATTRIBUTE_DATA aAttributeNameFormatData = - { - AttributeNameFormat, - 0, - NULL - }; - CSSM_DB_ATTRIBUTE_DATA aAttributeNameData = - { - AttributeName, - 0, - NULL - }; - CSSM_DB_ATTRIBUTE_DATA aAttributeNameIDData = - { - AttributeNameID, - 0, - NULL - }; - CSSM_DB_ATTRIBUTE_DATA aAttributeFormatData = - { - AttributeFormat, - 0, - NULL - }; - CSSM_DB_ATTRIBUTE_DATA aRecordAttributes[] = - { - aRelationIDData, - aAttributeIDData, - aAttributeNameFormatData, - aAttributeNameData, - aAttributeNameIDData, - aAttributeFormatData - }; - CSSM_DB_RECORD_ATTRIBUTE_DATA aRecordAttributeData = - { - aMetaRecord.dataRecordType(), - 0, - sizeof(aRecordAttributes) / sizeof(CSSM_DB_ATTRIBUTE_DATA), - aRecordAttributes - }; - CssmDbRecordAttributeData &aRecordData = CssmDbRecordAttributeData::overlay(aRecordAttributeData); - - TrackingAllocator recordAllocator(Allocator::standard()); - for (uint32 aRecord = 0; aRecord != aRecordsCount; aRecord++) - { - ReadSection aRecordSection = MetaRecord::readSection(aRecordsSection, aReadOffset); - uint32 aRecordSize = aRecordSection.size(); - aReadOffset += aRecordSize; - aMetaRecord.unpackRecord(aRecordSection, recordAllocator, - &aRecordAttributeData, NULL, 0); - // Create the attribute coresponding to this entry - if (aRecordData[0].size() != 1 || aRecordData[0].format() != CSSM_DB_ATTRIBUTE_FORMAT_UINT32) - CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT); - uint32 aRelationId = aRecordData[0]; - - // Skip the schema relations for the meta tables themselves. - // FIXME: this hard-wires the meta-table relation IDs to be - // within {CSSM_DB_RECORDTYPE_SCHEMA_START... - // CSSM_DB_RECORDTYPE_SCHEMA_END} (which is {0..4}). - // Bogus - the MDS schema relation IDs start at - // CSSM_DB_RELATIONID_MDS_START which is 0x40000000. - // Ref. Radar 2817921. - if (CSSM_DB_RECORDTYPE_SCHEMA_START <= aRelationId && aRelationId < CSSM_DB_RECORDTYPE_SCHEMA_END) - continue; - - // Get the MetaRecord corresponding to the specified RelationId - MetaRecord &aMetaRecord = findTable(aRelationId).getMetaRecord(); - - if (aRecordData[1].size() != 1 - || aRecordData[1].format() != CSSM_DB_ATTRIBUTE_FORMAT_UINT32 - || aRecordData[2].size() != 1 - || aRecordData[2].format() != CSSM_DB_ATTRIBUTE_FORMAT_UINT32 - || aRecordData[5].size() != 1 - || aRecordData[5].format() != CSSM_DB_ATTRIBUTE_FORMAT_UINT32) - CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT); - - uint32 anAttributeId = aRecordData[1]; - uint32 anAttributeNameFormat = aRecordData[2]; - uint32 anAttributeFormat = aRecordData[5]; - auto_ptr aName; - const CssmData *aNameID = NULL; - - if (aRecordData[3].size() == 1) - { - if (aRecordData[3].format() != CSSM_DB_ATTRIBUTE_FORMAT_STRING) - CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT); - - auto_ptr aName2(new string(static_cast(aRecordData[3]))); - aName = aName2; - } - - if (aRecordData[4].size() == 1) - { - if (aRecordData[4].format() != CSSM_DB_ATTRIBUTE_FORMAT_BLOB) - CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT); - - // @@@ Invoking conversion operator to CssmData & on aRecordData[4] - // And taking address of result. - aNameID = &static_cast(aRecordData[4]); - } - - // Make sure that the attribute specified by anAttributeNameFormat is present. - switch (anAttributeNameFormat) - { - case CSSM_DB_ATTRIBUTE_NAME_AS_STRING: - if (aRecordData[3].size() != 1) - CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT); - break; - case CSSM_DB_ATTRIBUTE_NAME_AS_OID: - if (aRecordData[4].size() != 1) - CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT); - break; - case CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER: - break; - default: - CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT); - } - - // Create the attribute - aMetaRecord.createAttribute(aName.get(), aNameID, anAttributeId, anAttributeFormat); - } - - // initialize the indexes associated with each table - { - TableMap::iterator it; - for (it = mTableMap.begin(); it != mTableMap.end(); it++) - it->second->readIndexSection(); - } - } - catch(...) - { - for_each_map_delete(mTableMap.begin(), mTableMap.end()); - mTableMap.clear(); - throw; - } -} - -const RecordId -DbVersion::getRecord(Table::Id inTableId, const RecordId &inRecordId, - CSSM_DB_RECORD_ATTRIBUTE_DATA *inoutAttributes, - CssmData *inoutData, - Allocator &inAllocator) const -{ - return findTable(inTableId).getRecord(inRecordId, inoutAttributes, - inoutData, inAllocator); -} - -Cursor * -DbVersion::createCursor(const CSSM_QUERY *inQuery) const -{ - // XXX We should add support for these special query types - // By Creating a Cursor that iterates over multiple tables - if (!inQuery || inQuery->RecordType == CSSM_DL_DB_RECORD_ANY - || inQuery->RecordType == CSSM_DL_DB_RECORD_ALL_KEYS) - { - return new MultiCursor(inQuery, *this); - } - - return findTable(inQuery->RecordType).createCursor(inQuery, *this); -} - -bool DbVersion::hasTable(Table::Id inTableId) const -{ - TableMap::const_iterator it = mTableMap.find(inTableId); - return it != mTableMap.end(); -} - -const Table & -DbVersion::findTable(Table::Id inTableId) const -{ - TableMap::const_iterator it = mTableMap.find(inTableId); - if (it == mTableMap.end()) - CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE); - return *it->second; -} - -Table & -DbVersion::findTable(Table::Id inTableId) -{ - TableMap::iterator it = mTableMap.find(inTableId); - if (it == mTableMap.end()) - CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT); - return *it->second; -} - -// -// Cursor implemetation -// -Cursor::Cursor() -{ -} - -Cursor::Cursor(const DbVersion &inDbVersion) : mDbVersion(&inDbVersion) -{ -} - -Cursor::~Cursor() -{ -} - -bool -Cursor::next(Table::Id &outTableId, - CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR outAttributes, - CssmData *outData, - Allocator &inAllocator, - RecordId &recordId) -{ - return false; -} - -// -// LinearCursor implemetation -// -LinearCursor::LinearCursor(const CSSM_QUERY *inQuery, const DbVersion &inDbVersion, - const Table &inTable) : - Cursor(inDbVersion), - mRecordsCount(inTable.getRecordsCount()), - mRecord(0), - mRecordsSection(inTable.getRecordsSection()), - mReadOffset(0), - mMetaRecord(inTable.getMetaRecord()) -{ - if (inQuery) - { - mConjunctive = inQuery->Conjunctive; - mQueryFlags = inQuery->QueryFlags; - // XXX Do something with inQuery->QueryLimits? - uint32 aPredicatesCount = inQuery->NumSelectionPredicates; - mPredicates.resize(aPredicatesCount); - try - { - for (uint32 anIndex = 0; anIndex < aPredicatesCount; anIndex++) - { - CSSM_SELECTION_PREDICATE &aPredicate = inQuery->SelectionPredicate[anIndex]; - mPredicates[anIndex] = new SelectionPredicate(mMetaRecord, aPredicate); - } - } - catch(...) - { - for_each_delete(mPredicates.begin(), mPredicates.end()); - throw; - } - } -} - -LinearCursor::~LinearCursor() -{ - for_each_delete(mPredicates.begin(), mPredicates.end()); -} - -bool -LinearCursor::next(Table::Id &outTableId, - CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes, - CssmData *inoutData, Allocator &inAllocator, RecordId &recordId) -{ - while (mRecord++ < mRecordsCount) - { - ReadSection aRecordSection = MetaRecord::readSection(mRecordsSection, mReadOffset); - uint32 aRecordSize = aRecordSection.size(); - mReadOffset += aRecordSize; - - PredicateVector::const_iterator anIt = mPredicates.begin(); - PredicateVector::const_iterator anEnd = mPredicates.end(); - bool aMatch; - if (anIt == anEnd) - { - // If there are no predicates we have a match. - aMatch = true; - } - else if (mConjunctive == CSSM_DB_OR) - { - // If mConjunctive is OR, the first predicate that returns - // true indicates a match. Dropthough means no match - aMatch = false; - for (; anIt != anEnd; anIt++) - { - if ((*anIt)->evaluate(aRecordSection)) - { - aMatch = true; - break; - } - } - } - else if (mConjunctive == CSSM_DB_AND || mConjunctive == CSSM_DB_NONE) - { - // If mConjunctive is AND (or NONE), the first predicate that returns - // false indicates a mismatch. Dropthough means a match - aMatch = true; - for (; anIt != anEnd; anIt++) - { - if (!(*anIt)->evaluate(aRecordSection)) - { - aMatch = false; - break; - } - } - } - else - { - // XXX Should be CSSMERR_DL_INVALID_QUERY (or CSSMERR_DL_INVALID_CONJUNTIVE). - CssmError::throwMe(CSSMERR_DL_UNSUPPORTED_QUERY); - } - - if (aMatch) - { - // Get the actual record. - mMetaRecord.unpackRecord(aRecordSection, inAllocator, - inoutAttributes, inoutData, - mQueryFlags); - outTableId = mMetaRecord.dataRecordType(); - recordId = MetaRecord::unpackRecordId(aRecordSection); - return true; - } - } - - return false; -} - -// -// IndexCursor -// - -IndexCursor::IndexCursor(DbQueryKey *queryKey, const DbVersion &inDbVersion, - const Table &table, const DbConstIndex *index) : - Cursor(inDbVersion), mQueryKey(queryKey), mTable(table), mIndex(index) -{ - index->performQuery(*queryKey, mBegin, mEnd); -} - -IndexCursor::~IndexCursor() -{ - // the query key will be deleted automatically, since it's an auto_ptr -} - -bool -IndexCursor::next(Table::Id &outTableId, - CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR outAttributes, - CssmData *outData, - Allocator &inAllocator, RecordId &recordId) -{ - if (mBegin == mEnd) - return false; - - ReadSection rs = mIndex->getRecordSection(mBegin++); - const MetaRecord &metaRecord = mTable.getMetaRecord(); - - outTableId = metaRecord.dataRecordType(); - metaRecord.unpackRecord(rs, inAllocator, outAttributes, outData, 0); - - recordId = MetaRecord::unpackRecordId(rs); - return true; -} - -// -// MultiCursor -// -MultiCursor::MultiCursor(const CSSM_QUERY *inQuery, const DbVersion &inDbVersion) : - Cursor(inDbVersion), mTableIterator(inDbVersion.begin()) -{ - if (inQuery) - mQuery.reset(new CssmAutoQuery(*inQuery)); - else - { - mQuery.reset(new CssmAutoQuery()); - mQuery->recordType(CSSM_DL_DB_RECORD_ANY); - } -} - -MultiCursor::~MultiCursor() -{ -} - -bool -MultiCursor::next(Table::Id &outTableId, - CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes, - CssmData *inoutData, Allocator &inAllocator, RecordId &recordId) -{ - for (;;) - { - if (!mCursor.get()) - { - if (mTableIterator == mDbVersion->end()) - return false; - - const Table &aTable = *mTableIterator++; - if (!aTable.matchesTableId(mQuery->recordType())) - continue; - - mCursor.reset(aTable.createCursor(mQuery.get(), *mDbVersion)); - } - - if (mCursor->next(outTableId, inoutAttributes, inoutData, inAllocator, recordId)) - return true; - - mCursor.reset(NULL); - } -} - - -// -// DbModifier -// -DbModifier::DbModifier(AtomicFile &inAtomicFile, const AppleDatabase &db) : - Metadata(), - mDbVersion(), - mAtomicFile(inAtomicFile), - mDb(db) -{ -} - -DbModifier::~DbModifier() -{ - try - { - for_each_map_delete(mModifiedTableMap.begin(), mModifiedTableMap.end()); - // mAtomicTempFile will do automatic rollback on destruction. - } - catch(...) {} -} - -const RefPointer -DbModifier::getDbVersion(bool force) -{ - StLock _(mDbVersionLock); - - /* Initialize the shared memory file change mechanism */ - pthread_once(&gCommonInitMutex, initCommon); - - /* If we don't have a mDbVersion yet, or we are force to re-read the file - before a write transaction, or we have received any notifications after - the last time we read the file, or more than kForceReReadTime seconds - have passed since the last time we read the file, we open the file and - check if it has changed. */ - if (!mDbVersion || - force || - gSegment == NULL || - mNotifyCount != *gSegment || - CFAbsoluteTimeGetCurrent() > mDbLastRead + kForceReReadTime) - { - RefPointer atomicBufferedFile(mAtomicFile.read()); - off_t length = atomicBufferedFile->open(); - /* Record the number of notifications we've seen and when we last - opened the file. */ - if (gSegment != NULL) - { - mNotifyCount = *gSegment; - } - - mDbLastRead = CFAbsoluteTimeGetCurrent(); - - /* If we already have a mDbVersion, let's check if we can reuse it. */ - if (mDbVersion) - { - if (length < AtomSize) - CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT); - - off_t bytesRead = 0; - const uint8 *ptr = atomicBufferedFile->read(length - AtomSize, - AtomSize, bytesRead); - ReadSection aVersionSection(ptr, (size_t)bytesRead); - uint32 aVersionId = aVersionSection[0]; - - /* If the version stamp hasn't changed the old mDbVersion is still - current. */ - if (aVersionId == mDbVersion->getVersionId()) - return mDbVersion; - } - - mDbVersion = new DbVersion(mDb, atomicBufferedFile); - } - - return mDbVersion; -} - -void -DbModifier::createDatabase(const CSSM_DBINFO &inDbInfo, - const CSSM_ACL_ENTRY_INPUT *inInitialAclEntry, - mode_t mode) -{ - // XXX This needs better locking. There is a possible race condition between - // two concurrent creators. Or a writer/creator or a close/create etc. - if (mAtomicTempFile || !mModifiedTableMap.empty()) - CssmError::throwMe(CSSMERR_DL_DATASTORE_ALREADY_EXISTS); - - mAtomicTempFile = mAtomicFile.create(mode); - // Set mVersionId to one since this is the first version of the database. - mVersionId = 1; - - // we need to create the meta tables first, because inserting tables - // (including the meta tables themselves) relies on them being there - createTable(new MetaRecord(mDb.schemaRelations)); - createTable(new MetaRecord(mDb.schemaAttributes)); - createTable(new MetaRecord(mDb.schemaIndexes)); - createTable(new MetaRecord(mDb.schemaParsingModule)); - - // now add the meta-tables' schema to the meta tables themselves - insertTableSchema(mDb.schemaRelations); - insertTableSchema(mDb.schemaAttributes); - insertTableSchema(mDb.schemaIndexes); - insertTableSchema(mDb.schemaParsingModule); - - if (inInitialAclEntry != NULL) - { - //createACL(*inInitialAclEntry); - } - - if (inDbInfo.NumberOfRecordTypes == 0) - return; - if (inDbInfo.RecordAttributeNames == NULL) - CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE); - if (inDbInfo.RecordIndexes == NULL) - CssmError::throwMe(CSSMERR_DL_INVALID_RECORD_INDEX); - if (inDbInfo.DefaultParsingModules == NULL) - CssmError::throwMe(CSSMERR_DL_INVALID_PARSING_MODULE); - - for (uint32 anIndex = 0; anIndex < inDbInfo.NumberOfRecordTypes; anIndex++) - { - insertTable(CssmDbRecordAttributeInfo::overlay(inDbInfo.RecordAttributeNames[anIndex]), - &inDbInfo.RecordIndexes[anIndex], - &inDbInfo.DefaultParsingModules[anIndex]); - } -} - -void DbModifier::openDatabase() -{ - // No need to do anything on open if we are already writing the database. - if (!mAtomicTempFile) - getDbVersion(false); -} - -void DbModifier::closeDatabase() -{ - commit(); // XXX Requires write lock. - StLock _(mDbVersionLock); - mDbVersion = NULL; -} - -void DbModifier::deleteDatabase() -{ - bool isDirty = mAtomicTempFile; - rollback(); // XXX Requires write lock. - StLock _(mDbVersionLock); - - // Clean up mModifiedTableMap in case this object gets reused again for - // a new create. - for_each_map_delete(mModifiedTableMap.begin(), mModifiedTableMap.end()); - mModifiedTableMap.clear(); - - // If the database was dirty and we had no mDbVersion yet then rollback() - // would have deleted the db. - if (!isDirty || mDbVersion) - { - mDbVersion = NULL; - mAtomicFile.performDelete(); - } -} - -void -DbModifier::modifyDatabase() -{ - if (mAtomicTempFile) - return; - - try - { - mAtomicTempFile = mAtomicFile.write(); - // Now we are holding the write lock make sure we get the latest greatest version of the db. - // Also set mVersionId to one more that that of the old database. - mVersionId = getDbVersion(true)->getVersionId() + 1; - - // Never make a database with mVersionId 0 since it makes bad things happen to Jaguar and older systems - if (mVersionId == 0) - mVersionId = 1; - - // Remove all old modified tables - for_each_map_delete(mModifiedTableMap.begin(), mModifiedTableMap.end()); - mModifiedTableMap.clear(); - - // Setup the new tables - DbVersion::TableMap::const_iterator anIt = - mDbVersion->mTableMap.begin(); - DbVersion::TableMap::const_iterator anEnd = - mDbVersion->mTableMap.end(); - for (; anIt != anEnd; ++anIt) - { - auto_ptr aTable(new ModifiedTable(anIt->second)); - mModifiedTableMap.insert(ModifiedTableMap::value_type(anIt->first, - aTable.get())); - aTable.release(); - } - } - catch(...) - { - for_each_map_delete(mModifiedTableMap.begin(), mModifiedTableMap.end()); - mModifiedTableMap.clear(); - rollback(); - throw; - } -} - -void -DbModifier::deleteRecord(Table::Id inTableId, const RecordId &inRecordId) -{ - modifyDatabase(); - findTable(inTableId).deleteRecord(inRecordId); -} - -const RecordId -DbModifier::insertRecord(Table::Id inTableId, - const CSSM_DB_RECORD_ATTRIBUTE_DATA *inAttributes, - const CssmData *inData) -{ - modifyDatabase(); - return findTable(inTableId).insertRecord(mVersionId, inAttributes, inData); -} - -const RecordId -DbModifier::updateRecord(Table::Id inTableId, const RecordId &inRecordId, - const CSSM_DB_RECORD_ATTRIBUTE_DATA *inAttributes, - const CssmData *inData, - CSSM_DB_MODIFY_MODE inModifyMode) -{ - modifyDatabase(); - return findTable(inTableId).updateRecord(inRecordId, inAttributes, inData, inModifyMode); -} - -// Create a table associated with a given metarecord, and add the table -// to the database. - -ModifiedTable * -DbModifier::createTable(MetaRecord *inMetaRecord) -{ - auto_ptr aMetaRecord(inMetaRecord); - auto_ptr aModifiedTable(new ModifiedTable(inMetaRecord)); - // Now that aModifiedTable is fully constructed it owns inMetaRecord - aMetaRecord.release(); - - if (!mModifiedTableMap.insert - (ModifiedTableMap::value_type(inMetaRecord->dataRecordType(), - aModifiedTable.get())).second) - { - // XXX Should be CSSMERR_DL_DUPLICATE_RECORDTYPE. Since that - // doesn't exist we report that the metatable's unique index would - // no longer be valid - CssmError::throwMe(CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA); - } - - return aModifiedTable.release(); -} - -void -DbModifier::deleteTable(Table::Id inTableId) -{ - modifyDatabase(); - // Can't delete schema tables. - if (CSSM_DB_RECORDTYPE_SCHEMA_START <= inTableId - && inTableId < CSSM_DB_RECORDTYPE_SCHEMA_END) - CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE); - - // Find the ModifiedTable and delete it - ModifiedTableMap::iterator it = mModifiedTableMap.find(inTableId); - if (it == mModifiedTableMap.end()) - CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE); - - delete it->second; - mModifiedTableMap.erase(it); -} - -uint32 -DbModifier::writeAuthSection(uint32 inSectionOffset) -{ - WriteSection anAuthSection; - - // XXX Put real data into the authsection. - uint32 anOffset = anAuthSection.put(0, 0); - anAuthSection.size(anOffset); - - mAtomicTempFile->write(AtomicFile::FromStart, inSectionOffset, - anAuthSection.address(), anAuthSection.size()); - return inSectionOffset + anOffset; -} - -uint32 -DbModifier::writeSchemaSection(uint32 inSectionOffset) -{ - uint32 aTableCount = (uint32) mModifiedTableMap.size(); - WriteSection aTableSection(Allocator::standard(), - OffsetTables + AtomSize * aTableCount); - // Set aTableSection to the correct size. - aTableSection.size(OffsetTables + AtomSize * aTableCount); - aTableSection.put(OffsetTablesCount, aTableCount); - - uint32 anOffset = inSectionOffset + OffsetTables + AtomSize * aTableCount; - ModifiedTableMap::const_iterator anIt = mModifiedTableMap.begin(); - ModifiedTableMap::const_iterator anEnd = mModifiedTableMap.end(); - for (uint32 aTableNumber = 0; anIt != anEnd; anIt++, aTableNumber++) - { - // Put the offset to the current table relative to the start of - // this section into the tables array - aTableSection.put(OffsetTables + AtomSize * aTableNumber, - anOffset - inSectionOffset); - anOffset = anIt->second->writeTable(*mAtomicTempFile, anOffset); - } - - aTableSection.put(OffsetSchemaSize, anOffset - inSectionOffset); - mAtomicTempFile->write(AtomicFile::FromStart, inSectionOffset, - aTableSection.address(), aTableSection.size()); - - return anOffset; -} - -void -DbModifier::commit() -{ - if (!mAtomicTempFile) - return; - try - { - WriteSection aHeaderSection(Allocator::standard(), size_t(HeaderSize)); - // Set aHeaderSection to the correct size. - aHeaderSection.size(HeaderSize); - - // Start writing sections after the header - uint32 anOffset = HeaderOffset + HeaderSize; - - // Write auth section - aHeaderSection.put(OffsetAuthOffset, anOffset); - anOffset = writeAuthSection(anOffset); - // Write schema section - aHeaderSection.put(OffsetSchemaOffset, anOffset); - anOffset = writeSchemaSection(anOffset); - - // Write out the file header. - aHeaderSection.put(OffsetMagic, HeaderMagic); - aHeaderSection.put(OffsetVersion, HeaderVersion); - mAtomicTempFile->write(AtomicFile::FromStart, HeaderOffset, - aHeaderSection.address(), aHeaderSection.size()); - - // Write out the versionId. - WriteSection aVersionSection(Allocator::standard(), size_t(AtomSize)); - anOffset = aVersionSection.put(0, mVersionId); - aVersionSection.size(anOffset); - - mAtomicTempFile->write(AtomicFile::FromEnd, 0, - aVersionSection.address(), aVersionSection.size()); - - mAtomicTempFile->commit(); - mAtomicTempFile = NULL; - /* Initialize the shared memory file change mechanism */ - pthread_once(&gCommonInitMutex, initCommon); - - if (gSegment != NULL) - { - /* - PLEASE NOTE: - - The following operation is endian safe because we are not looking - for monotonic increase. I have tested every possible value of - *gSegment, and there is no value for which alternating - big and little endian increments will produce the original value. - */ - - OSAtomicIncrement32Barrier (gSegment); - } - } - catch(...) - { - rollback(); - throw; - } -} - -void -DbModifier::rollback() throw() -{ - // This will destroy the AtomicTempFile if we have one causing it to rollback. - mAtomicTempFile = NULL; -} - -const RecordId -DbModifier::getRecord(Table::Id inTableId, const RecordId &inRecordId, - CSSM_DB_RECORD_ATTRIBUTE_DATA *inoutAttributes, - CssmData *inoutData, Allocator &inAllocator) -{ - if (mAtomicTempFile) - { - // We are in the midst of changing the database. - return findTable(inTableId).getRecord(inRecordId, inoutAttributes, - inoutData, inAllocator); - } - else - { - return getDbVersion(false)->getRecord(inTableId, inRecordId, - inoutAttributes, inoutData, inAllocator); - } -} - -Cursor * -DbModifier::createCursor(const CSSM_QUERY *inQuery) -{ - if (mAtomicTempFile) - { - // We are modifying this database. - - // If we have a mDbVersion already then it's a snapshot of the database - // right before the modifications started. So return a cursor using - // that. - if (mDbVersion) - return mDbVersion->createCursor(inQuery); - - // This is a newly created but never commited database. Return a - // Cursor that will not return any matches. - return new Cursor(); - } - - // Get the latest and greatest version of the db and create the cursor - // on that. - return getDbVersion(false)->createCursor(inQuery); -} - -// Insert schema records for a new table into the metatables of the database. This gets -// called while a database is being created. - -void -DbModifier::insertTableSchema(const CssmDbRecordAttributeInfo &inInfo, - const CSSM_DB_RECORD_INDEX_INFO *inIndexInfo /* = NULL */) -{ - ModifiedTable &aTable = findTable(inInfo.DataRecordType); - const MetaRecord &aMetaRecord = aTable.getMetaRecord(); - - CssmAutoDbRecordAttributeData aRecordBuilder(5); // Set capacity to 5 so we don't need to grow - - // Create the entry for the SchemaRelations table. - aRecordBuilder.add(RelationID, inInfo.recordType()); - aRecordBuilder.add(RelationName, mDb.recordName(inInfo.recordType())); - - // Insert the record into the SchemaRelations ModifiedTable - findTable(mDb.schemaRelations.DataRecordType).insertRecord(mVersionId, - &aRecordBuilder, NULL); - - ModifiedTable &anAttributeTable = findTable(mDb.schemaAttributes.DataRecordType); - for (uint32 anIndex = 0; anIndex < inInfo.size(); anIndex++) - { - // Create an entry for the SchemaAttributes table. - aRecordBuilder.clear(); - aRecordBuilder.add(RelationID, inInfo.recordType()); - aRecordBuilder.add(AttributeNameFormat, inInfo.at(anIndex).nameFormat()); - - uint32 attributeId = aMetaRecord.metaAttribute(inInfo.at(anIndex)).attributeId(); - - switch (inInfo.at(anIndex).nameFormat()) - { - case CSSM_DB_ATTRIBUTE_NAME_AS_STRING: - aRecordBuilder.add(AttributeName, inInfo.at(anIndex).Label.AttributeName); - break; - case CSSM_DB_ATTRIBUTE_NAME_AS_OID: - aRecordBuilder.add(AttributeNameID, inInfo.at(anIndex).Label.AttributeOID); - break; - case CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER: - break; - default: - CssmError::throwMe(CSSMERR_DL_INVALID_FIELD_NAME); - } - - aRecordBuilder.add(AttributeID, attributeId); - aRecordBuilder.add(AttributeFormat, inInfo.at(anIndex).format()); - - // Insert the record into the SchemaAttributes ModifiedTable - anAttributeTable.insertRecord(mVersionId, &aRecordBuilder, NULL); - } - - if (inIndexInfo != NULL) { - - if (inIndexInfo->DataRecordType != inInfo.DataRecordType && - inIndexInfo->NumberOfIndexes > 0) - CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE); - - ModifiedTable &indexMetaTable = findTable(mDb.schemaIndexes.DataRecordType); - uint32 aNumberOfIndexes = inIndexInfo->NumberOfIndexes; - - for (uint32 anIndex = 0; anIndex < aNumberOfIndexes; anIndex++) - { - const CssmDbIndexInfo &thisIndex = CssmDbIndexInfo::overlay(inIndexInfo->IndexInfo[anIndex]); - - // make sure the index is supported - if (thisIndex.dataLocation() != CSSM_DB_INDEX_ON_ATTRIBUTE) - CssmError::throwMe(CSSMERR_DL_INVALID_INDEX_INFO); - - // assign an index ID: the unique index is ID 0, all others are ID > 0 - uint32 indexId; - if (thisIndex.IndexType == CSSM_DB_INDEX_UNIQUE) - indexId = 0; - else - indexId = anIndex + 1; - - // figure out the attribute ID - uint32 attributeId = - aMetaRecord.metaAttribute(thisIndex.Info).attributeId(); - - // Create an entry for the SchemaIndexes table. - aRecordBuilder.clear(); - aRecordBuilder.add(RelationID, inInfo.DataRecordType); - aRecordBuilder.add(IndexID, indexId); - aRecordBuilder.add(AttributeID, attributeId); - aRecordBuilder.add(IndexType, thisIndex.IndexType); - aRecordBuilder.add(IndexedDataLocation, thisIndex.IndexedDataLocation); - - // Insert the record into the SchemaIndexes ModifiedTable - indexMetaTable.insertRecord(mVersionId, &aRecordBuilder, NULL); - - // update the table's index objects - DbMutableIndex &index = aTable.findIndex(indexId, aMetaRecord, indexId == 0); - index.appendAttribute(attributeId); - } - } -} - -// Insert a new table. The attribute info is required; the index and parsing module -// descriptions are optional. This version gets called during the creation of a -// database. - -void -DbModifier::insertTable(const CssmDbRecordAttributeInfo &inInfo, - const CSSM_DB_RECORD_INDEX_INFO *inIndexInfo /* = NULL */, - const CSSM_DB_PARSING_MODULE_INFO *inParsingModule /* = NULL */) -{ - modifyDatabase(); - createTable(new MetaRecord(inInfo)); - insertTableSchema(inInfo, inIndexInfo); -} - -// Insert a new table. This is the version that gets called when a table is added -// after a database has been created. - -void -DbModifier::insertTable(Table::Id inTableId, const string &inTableName, - uint32 inNumberOfAttributes, - const CSSM_DB_SCHEMA_ATTRIBUTE_INFO *inAttributeInfo, - uint32 inNumberOfIndexes, - const CSSM_DB_SCHEMA_INDEX_INFO *inIndexInfo) -{ - modifyDatabase(); - ModifiedTable *aTable = createTable(new MetaRecord(inTableId, inNumberOfAttributes, inAttributeInfo)); - - CssmAutoDbRecordAttributeData aRecordBuilder(6); // Set capacity to 6 so we don't need to grow - - // Create the entry for the SchemaRelations table. - aRecordBuilder.add(RelationID, inTableId); - aRecordBuilder.add(RelationName, inTableName); - - // Insert the record into the SchemaRelations ModifiedTable - findTable(mDb.schemaRelations.DataRecordType).insertRecord(mVersionId, - &aRecordBuilder, NULL); - - ModifiedTable &anAttributeTable = findTable(mDb.schemaAttributes.DataRecordType); - for (uint32 anIndex = 0; anIndex < inNumberOfAttributes; anIndex++) - { - // Create an entry for the SchemaAttributes table. - aRecordBuilder.clear(); - aRecordBuilder.add(RelationID, inTableId); - // XXX What should this be? We set it to CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER for now - // since the AttributeID is always valid. - aRecordBuilder.add(AttributeNameFormat, uint32(CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER)); - aRecordBuilder.add(AttributeID, inAttributeInfo[anIndex].AttributeId); - if (inAttributeInfo[anIndex].AttributeName) - aRecordBuilder.add(AttributeName, inAttributeInfo[anIndex].AttributeName); - if (inAttributeInfo[anIndex].AttributeNameID.Length > 0) - aRecordBuilder.add(AttributeNameID, inAttributeInfo[anIndex].AttributeNameID); - aRecordBuilder.add(AttributeFormat, inAttributeInfo[anIndex].DataType); - - // Insert the record into the SchemaAttributes ModifiedTable - anAttributeTable.insertRecord(mVersionId, &aRecordBuilder, NULL); - } - - ModifiedTable &anIndexTable = findTable(mDb.schemaIndexes.DataRecordType); - for (uint32 anIndex = 0; anIndex < inNumberOfIndexes; anIndex++) - { - // Create an entry for the SchemaIndexes table. - aRecordBuilder.clear(); - aRecordBuilder.add(RelationID, inTableId); - aRecordBuilder.add(IndexID, inIndexInfo[anIndex].IndexId); - aRecordBuilder.add(AttributeID, inIndexInfo[anIndex].AttributeId); - aRecordBuilder.add(IndexType, inIndexInfo[anIndex].IndexType); - aRecordBuilder.add(IndexedDataLocation, inIndexInfo[anIndex].IndexedDataLocation); - - // Insert the record into the SchemaIndexes ModifiedTable - anIndexTable.insertRecord(mVersionId, &aRecordBuilder, NULL); - - // update the table's index objects - DbMutableIndex &index = aTable->findIndex(inIndexInfo[anIndex].IndexId, - aTable->getMetaRecord(), inIndexInfo[anIndex].IndexType == CSSM_DB_INDEX_UNIQUE); - index.appendAttribute(inIndexInfo[anIndex].AttributeId); - } -} - - - -bool DbModifier::hasTable(Table::Id inTableId) -{ - return getDbVersion(false)->hasTable(inTableId); -} - - - -ModifiedTable & -DbModifier::findTable(Table::Id inTableId) -{ - ModifiedTableMap::iterator it = mModifiedTableMap.find(inTableId); - if (it == mModifiedTableMap.end()) - CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE); - return *it->second; -} - - -// -// AppleDatabaseManager implementation -// - -AppleDatabaseManager::AppleDatabaseManager(const AppleDatabaseTableName *tableNames) - : DatabaseManager(), - mTableNames(tableNames) -{ - // make sure that a proper set of table ids and names has been provided - - if (!mTableNames) - CssmError::throwMe(CSSMERR_DL_INTERNAL_ERROR); - else { - uint32 i; - for (i = 0; mTableNames[i].mTableName; i++) {} - if (i < AppleDatabaseTableName::kNumRequiredTableNames) - CssmError::throwMe(CSSMERR_DL_INTERNAL_ERROR); - } -} - -Database * -AppleDatabaseManager::make(const DbName &inDbName) -{ - return new AppleDatabase(inDbName, mTableNames); -} - - -// -// AppleDbContext implementation -// - -/* This is the version 0 CSSM_APPLEDL_OPEN_PARAMETERS struct used up to 10.2.x. */ -extern "C" { - -typedef struct cssm_appledl_open_parameters_v0 -{ - uint32 length; /* Should be sizeof(CSSM_APPLEDL_OPEN_PARAMETERS_V0). */ - uint32 version; /* Should be 0. */ - CSSM_BOOL autoCommit; -} CSSM_APPLEDL_OPEN_PARAMETERS_V0; - -}; - -AppleDbContext::AppleDbContext(Database &inDatabase, - DatabaseSession &inDatabaseSession, - CSSM_DB_ACCESS_TYPE inAccessRequest, - const AccessCredentials *inAccessCred, - const void *inOpenParameters) : - DbContext(inDatabase, inDatabaseSession, inAccessRequest, inAccessCred), - mAutoCommit(true), - mMode(0666) -{ - const CSSM_APPLEDL_OPEN_PARAMETERS *anOpenParameters = - reinterpret_cast(inOpenParameters); - - if (anOpenParameters) - { - switch (anOpenParameters->version) - { - case 1: - if (anOpenParameters->length < sizeof(CSSM_APPLEDL_OPEN_PARAMETERS)) - CssmError::throwMe(CSSMERR_APPLEDL_INVALID_OPEN_PARAMETERS); - - if (anOpenParameters->mask & kCSSM_APPLEDL_MASK_MODE) - mMode = anOpenParameters->mode; - /*DROPTHROUGH*/ - case 0: - if (anOpenParameters->length < sizeof(CSSM_APPLEDL_OPEN_PARAMETERS_V0)) - CssmError::throwMe(CSSMERR_APPLEDL_INVALID_OPEN_PARAMETERS); - - mAutoCommit = anOpenParameters->autoCommit == CSSM_FALSE ? false : true; - break; - - default: - CssmError::throwMe(CSSMERR_APPLEDL_INVALID_OPEN_PARAMETERS); - } - } -} - -AppleDbContext::~AppleDbContext() -{ -} - -// -// AppleDatabase implementation -// -AppleDatabase::AppleDatabase(const DbName &inDbName, const AppleDatabaseTableName *tableNames) : - Database(inDbName), - schemaRelations(tableNames[AppleDatabaseTableName::kSchemaInfo].mTableId, - sizeof(AttrSchemaRelations) / sizeof(CSSM_DB_ATTRIBUTE_INFO), - const_cast(AttrSchemaRelations)), - schemaAttributes(tableNames[AppleDatabaseTableName::kSchemaAttributes].mTableId, - sizeof(AttrSchemaAttributes) / sizeof(CSSM_DB_ATTRIBUTE_INFO), - const_cast(AttrSchemaAttributes)), - schemaIndexes(tableNames[AppleDatabaseTableName::kSchemaIndexes].mTableId, - sizeof(AttrSchemaIndexes) / sizeof(CSSM_DB_ATTRIBUTE_INFO), - const_cast(AttrSchemaIndexes)), - schemaParsingModule(tableNames[AppleDatabaseTableName::kSchemaParsingModule].mTableId, - sizeof(AttrSchemaParsingModule) / sizeof(CSSM_DB_ATTRIBUTE_INFO), - const_cast(AttrSchemaParsingModule)), - mAtomicFile(mDbName.dbName()), - mDbModifier(mAtomicFile, *this), - mTableNames(tableNames) -{ - /* temp check for X509Anchors access - this should removed before Leopard GM */ - if(!strcmp(inDbName.dbName(), "/System/Library/Keychains/X509Anchors")) { - Syslog::alert("Warning: accessing obsolete X509Anchors."); - } -} - -AppleDatabase::~AppleDatabase() -{ -} - -// Return the name of a record type. This uses a table that maps record types -// to record names. The table is provided when the database is created. - -const char *AppleDatabase::recordName(CSSM_DB_RECORDTYPE inRecordType) const -{ - if (inRecordType == CSSM_DL_DB_RECORD_ANY || inRecordType == CSSM_DL_DB_RECORD_ALL_KEYS) - CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE); - - for (uint32 i = 0; mTableNames[i].mTableName; i++) - if (mTableNames[i].mTableId == inRecordType) - return mTableNames[i].mTableName; - - return ""; -} - -DbContext * -AppleDatabase::makeDbContext(DatabaseSession &inDatabaseSession, - CSSM_DB_ACCESS_TYPE inAccessRequest, - const AccessCredentials *inAccessCred, - const void *inOpenParameters) -{ - return new AppleDbContext(*this, inDatabaseSession, inAccessRequest, - inAccessCred, inOpenParameters); -} - -void -AppleDatabase::dbCreate(DbContext &inDbContext, const CSSM_DBINFO &inDBInfo, - const CSSM_ACL_ENTRY_INPUT *inInitialAclEntry) -{ - AppleDbContext &context = safer_cast(inDbContext); - try - { - StLock _(mWriteLock); - mDbModifier.createDatabase(inDBInfo, inInitialAclEntry, context.mode()); - } - catch(...) - { - mDbModifier.rollback(); - throw; - } - if (context.autoCommit()) - mDbModifier.commit(); -} - -void -AppleDatabase::dbOpen(DbContext &inDbContext) -{ - mDbModifier.openDatabase(); -} - -void -AppleDatabase::dbClose() -{ - StLock _(mWriteLock); - mDbModifier.closeDatabase(); -} - -void -AppleDatabase::dbDelete(DatabaseSession &inDatabaseSession, - const AccessCredentials *inAccessCred) -{ - StLock _(mWriteLock); - // XXX Check callers credentials. - mDbModifier.deleteDatabase(); -} - -void -AppleDatabase::createRelation(DbContext &inDbContext, - CSSM_DB_RECORDTYPE inRelationID, - const char *inRelationName, - uint32 inNumberOfAttributes, - const CSSM_DB_SCHEMA_ATTRIBUTE_INFO *inAttributeInfo, - uint32 inNumberOfIndexes, - const CSSM_DB_SCHEMA_INDEX_INFO &inIndexInfo) -{ - try - { - StLock _(mWriteLock); - // XXX Fix the refs here. - mDbModifier.insertTable(inRelationID, inRelationName, - inNumberOfAttributes, inAttributeInfo, - inNumberOfIndexes, &inIndexInfo); - } - catch(...) - { - if (safer_cast(inDbContext).autoCommit()) - mDbModifier.rollback(); - throw; - } - if (safer_cast(inDbContext).autoCommit()) - mDbModifier.commit(); -} - -void -AppleDatabase::destroyRelation(DbContext &inDbContext, - CSSM_DB_RECORDTYPE inRelationID) -{ - try - { - StLock _(mWriteLock); - mDbModifier.deleteTable(inRelationID); - } - catch(...) - { - if (safer_cast(inDbContext).autoCommit()) - mDbModifier.rollback(); - throw; - } - if (safer_cast(inDbContext).autoCommit()) - mDbModifier.commit(); -} - -void -AppleDatabase::authenticate(DbContext &inDbContext, - CSSM_DB_ACCESS_TYPE inAccessRequest, - const AccessCredentials &inAccessCred) -{ - CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED); -} - -void -AppleDatabase::getDbAcl(DbContext &inDbContext, - const CSSM_STRING *inSelectionTag, - uint32 &outNumberOfAclInfos, - CSSM_ACL_ENTRY_INFO_PTR &outAclInfos) -{ - CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED); -} - -void -AppleDatabase::changeDbAcl(DbContext &inDbContext, - const AccessCredentials &inAccessCred, - const CSSM_ACL_EDIT &inAclEdit) -{ - CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED); -} - -void -AppleDatabase::getDbOwner(DbContext &inDbContext, - CSSM_ACL_OWNER_PROTOTYPE &outOwner) -{ - CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED); -} - -void -AppleDatabase::changeDbOwner(DbContext &inDbContext, - const AccessCredentials &inAccessCred, - const CSSM_ACL_OWNER_PROTOTYPE &inNewOwner) -{ - CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED); -} - -char * -AppleDatabase::getDbNameFromHandle(const DbContext &inDbContext) const -{ - CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED); -} - -CSSM_DB_UNIQUE_RECORD_PTR -AppleDatabase::dataInsert(DbContext &inDbContext, - CSSM_DB_RECORDTYPE inRecordType, - const CSSM_DB_RECORD_ATTRIBUTE_DATA *inAttributes, - const CssmData *inData) -{ - CSSM_DB_UNIQUE_RECORD_PTR anUniqueRecordPtr = NULL; - try - { - StLock _(mWriteLock); - const RecordId aRecordId = - mDbModifier.insertRecord(inRecordType, inAttributes, inData); - - anUniqueRecordPtr = createUniqueRecord(inDbContext, inRecordType, - aRecordId); - if (safer_cast(inDbContext).autoCommit()) - mDbModifier.commit(); - } - catch(...) - { - if (anUniqueRecordPtr != NULL) - freeUniqueRecord(inDbContext, *anUniqueRecordPtr); - - if (safer_cast(inDbContext).autoCommit()) - mDbModifier.rollback(); - throw; - } - - return anUniqueRecordPtr; -} - -void -AppleDatabase::dataDelete(DbContext &inDbContext, - const CSSM_DB_UNIQUE_RECORD &inUniqueRecord) -{ - try - { - // syslog if it's the .Mac password - CSSM_DB_RECORD_ATTRIBUTE_DATA attrData; - // 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 - // an exception. - memset(&attrData, 0, sizeof(attrData)); - dataGetFromUniqueRecordId(inDbContext, inUniqueRecord, &attrData, NULL); - - if (attrData.DataRecordType == CSSM_DL_DB_RECORD_GENERIC_PASSWORD) - { - CSSM_DB_ATTRIBUTE_DATA attributes; - - // setup some attributes and see if we are indeed the .Mac password - attributes.Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER; - attributes.Info.Label.AttributeID = 'svce'; - attributes.Info.AttributeFormat = 0; - attributes.NumberOfValues = 1; - attributes.Value = NULL; - - attrData.NumberOfAttributes = 1; - attrData.AttributeData = &attributes; - - dataGetFromUniqueRecordId(inDbContext, inUniqueRecord, &attrData, NULL); - - // now check the results - std::string dataString((const char*) attrData.AttributeData[0].Value[0].Data, attrData.AttributeData[0].Value[0].Length); - if (dataString == "iTools") - { - syslog(LOG_WARNING, "Warning: Removed .Me password"); - } - - free(attrData.AttributeData[0].Value[0].Data); - free(attrData.AttributeData[0].Value); - } - - StLock _(mWriteLock); - Table::Id aTableId; - const RecordId aRecordId(parseUniqueRecord(inUniqueRecord, aTableId)); - mDbModifier.deleteRecord(aTableId, aRecordId); - } - catch(...) - { - if (safer_cast(inDbContext).autoCommit()) - mDbModifier.rollback(); - throw; - } - - if (safer_cast(inDbContext).autoCommit()) - mDbModifier.commit(); -} - -void -AppleDatabase::dataModify(DbContext &inDbContext, - CSSM_DB_RECORDTYPE inRecordType, - CSSM_DB_UNIQUE_RECORD &inoutUniqueRecord, - const CSSM_DB_RECORD_ATTRIBUTE_DATA *inAttributesToBeModified, - const CssmData *inDataToBeModified, - CSSM_DB_MODIFY_MODE inModifyMode) -{ - try - { - StLock _(mWriteLock); - Table::Id aTableId; - const RecordId oldRecordId = parseUniqueRecord(inoutUniqueRecord, - aTableId); -#if 1 - if (inRecordType != aTableId) -#else - if (inRecordType != aTableId && - inRecordType != CSSM_DL_DB_RECORD_ANY && - !(inRecordType == CSSM_DL_DB_RECORD_ALL_KEYS && - (aTableId == CSSM_DL_DB_RECORD_PUBLIC_KEY || - aTableId == CSSM_DL_DB_RECORD_PRIVATE_KEY || - aTableId == CSSM_DL_DB_RECORD_SYMMETRIC_KEY))) -#endif - { - CssmError::throwMe(CSSMERR_DL_INVALID_RECORD_UID); - } - - const RecordId newRecordId = - mDbModifier.updateRecord(aTableId, - oldRecordId, - inAttributesToBeModified, - inDataToBeModified, - inModifyMode); - updateUniqueRecord(inDbContext, aTableId, newRecordId, - inoutUniqueRecord); - } - catch(...) - { - if (safer_cast(inDbContext).autoCommit()) - mDbModifier.rollback(); - throw; - } - - if (safer_cast(inDbContext).autoCommit()) - mDbModifier.commit(); -} - -CSSM_HANDLE -AppleDatabase::dataGetFirst(DbContext &inDbContext, - const CssmQuery *inQuery, - CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes, - CssmData *inoutData, - CSSM_DB_UNIQUE_RECORD_PTR &outUniqueRecord) -{ - // XXX: register Cursor with DbContext and have DbContext call - // dataAbortQuery for all outstanding Query objects on close. - auto_ptr aCursor(mDbModifier.createCursor(inQuery)); - Table::Id aTableId; - RecordId aRecordId; - - if (!aCursor->next(aTableId, inoutAttributes, inoutData, - inDbContext.mDatabaseSession, aRecordId)) - // return a NULL handle, and implicitly delete the cursor - return CSSM_INVALID_HANDLE; - - outUniqueRecord = createUniqueRecord(inDbContext, aTableId, aRecordId); - return aCursor.release()->handle(); // We didn't throw so keep the Cursor around. -} - -bool -AppleDatabase::dataGetNext(DbContext &inDbContext, - CSSM_HANDLE inResultsHandle, - CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes, - CssmData *inoutData, - CSSM_DB_UNIQUE_RECORD_PTR &outUniqueRecord) -{ - auto_ptr aCursor(&HandleObject::find(inResultsHandle, CSSMERR_DL_INVALID_RESULTS_HANDLE)); - Table::Id aTableId; - RecordId aRecordId; - - if (!aCursor->next(aTableId, inoutAttributes, inoutData, inDbContext.mDatabaseSession, aRecordId)) - return false; - - outUniqueRecord = createUniqueRecord(inDbContext, aTableId, aRecordId); - - aCursor.release(); - return true; -} - -void -AppleDatabase::dataAbortQuery(DbContext &inDbContext, - CSSM_HANDLE inResultsHandle) -{ - delete &HandleObject::find(inResultsHandle, CSSMERR_DL_INVALID_RESULTS_HANDLE); -} - -void -AppleDatabase::dataGetFromUniqueRecordId(DbContext &inDbContext, - const CSSM_DB_UNIQUE_RECORD &inUniqueRecord, - CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes, - CssmData *inoutData) -{ - Table::Id aTableId; - const RecordId aRecordId(parseUniqueRecord(inUniqueRecord, aTableId)); - // XXX Change CDSA spec to use new RecordId returned by this function - mDbModifier.getRecord(aTableId, aRecordId, inoutAttributes, inoutData, - inDbContext.mDatabaseSession); -} - -void -AppleDatabase::freeUniqueRecord(DbContext &inDbContext, - CSSM_DB_UNIQUE_RECORD &inUniqueRecord) -{ - if (inUniqueRecord.RecordIdentifier.Length != 0 - && inUniqueRecord.RecordIdentifier.Data != NULL) - { - inUniqueRecord.RecordIdentifier.Length = 0; - inDbContext.mDatabaseSession.free(inUniqueRecord.RecordIdentifier.Data); - } - inDbContext.mDatabaseSession.free(&inUniqueRecord); -} - -void -AppleDatabase::updateUniqueRecord(DbContext &inDbContext, - CSSM_DB_RECORDTYPE inTableId, - const RecordId &inRecordId, - CSSM_DB_UNIQUE_RECORD &inoutUniqueRecord) -{ - uint32 *aBuffer = reinterpret_cast(inoutUniqueRecord.RecordIdentifier.Data); - aBuffer[0] = inTableId; - aBuffer[1] = inRecordId.mRecordNumber; - aBuffer[2] = inRecordId.mCreateVersion; - aBuffer[3] = inRecordId.mRecordVersion; -} - -CSSM_DB_UNIQUE_RECORD_PTR -AppleDatabase::createUniqueRecord(DbContext &inDbContext, - CSSM_DB_RECORDTYPE inTableId, - const RecordId &inRecordId) -{ - CSSM_DB_UNIQUE_RECORD_PTR aUniqueRecord = - inDbContext.mDatabaseSession.alloc(); - memset(aUniqueRecord, 0, sizeof(*aUniqueRecord)); - aUniqueRecord->RecordIdentifier.Length = sizeof(uint32) * 4; - try - { - aUniqueRecord->RecordIdentifier.Data = - inDbContext.mDatabaseSession.alloc(sizeof(uint32) * 4); - updateUniqueRecord(inDbContext, inTableId, inRecordId, *aUniqueRecord); - } - catch(...) - { - inDbContext.mDatabaseSession.free(aUniqueRecord); - throw; - } - - return aUniqueRecord; -} - -const RecordId -AppleDatabase::parseUniqueRecord(const CSSM_DB_UNIQUE_RECORD &inUniqueRecord, - CSSM_DB_RECORDTYPE &outTableId) -{ - if (inUniqueRecord.RecordIdentifier.Length != sizeof(uint32) * 4) - CssmError::throwMe(CSSMERR_DL_INVALID_RECORD_UID); - - uint32 *aBuffer = reinterpret_cast(inUniqueRecord.RecordIdentifier.Data); - outTableId = aBuffer[0]; - return RecordId(aBuffer[1], aBuffer[2], aBuffer[3]); -} - -void -AppleDatabase::passThrough(DbContext &dbContext, - uint32 passThroughId, - const void *inputParams, - void **outputParams) -{ - switch (passThroughId) - { - case CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT: - { - AppleDbContext &dbc = safer_cast(dbContext); - // Return the old state of the autoCommit flag if requested - if (outputParams) - *reinterpret_cast(outputParams) = dbc.autoCommit(); - dbc.autoCommit(inputParams ? CSSM_TRUE : CSSM_FALSE); - } - break; - - case CSSM_APPLEFILEDL_COMMIT: - mDbModifier.commit(); - break; - - case CSSM_APPLEFILEDL_ROLLBACK: - mDbModifier.rollback(); - break; - - case CSSM_APPLECSPDL_DB_RELATION_EXISTS: - { - CSSM_BOOL returnValue; - - CSSM_DB_RECORDTYPE recordType = *(CSSM_DB_RECORDTYPE*) inputParams; - if (recordType == CSSM_DL_DB_RECORD_ANY || recordType == CSSM_DL_DB_RECORD_ALL_KEYS) - { - returnValue = CSSM_TRUE; - } - else - { - returnValue = mDbModifier.hasTable(recordType); - } - - *(CSSM_BOOL*) outputParams = returnValue; - break; - } - - default: - CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED); - break; - } -}