#include "AppleDatabase.h"
#include <security_cdsa_plugin/DatabaseSession.h>
#include <security_cdsa_plugin/DbContext.h>
#include <security_cdsa_utilities/cssmdb.h>
#include <Security/cssmapple.h>
#include <security_utilities/trackingallocator.h>
#include <security_utilities/logging.h>
#include <fcntl.h>
#include <memory>
#include <libkern/OSAtomic.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <Security/cssmapplePriv.h>
#include <syslog.h>
#include <copyfile.h>
static const char *kAppleDatabaseChanged = "com.apple.AppleDatabaseChanged";
static const CFTimeInterval kForceReReadTime = 15.0;
pthread_once_t gCommonInitMutex = PTHREAD_ONCE_INIT;
static int kSegmentSize = 4;
int32_t* gSegment = NULL;
static void initCommon(void)
{
int segmentDescriptor = shm_open (kAppleDatabaseChanged, O_RDWR | O_CREAT, S_IRWXU | S_IRWXG | S_IRWXO);
if (segmentDescriptor < 0)
{
return;
}
ftruncate (segmentDescriptor, kSegmentSize);
int32_t* tmp = (int32_t*) mmap (NULL, kSegmentSize, PROT_READ | PROT_WRITE, MAP_SHARED, segmentDescriptor, 0);
close (segmentDescriptor);
if (tmp == (int32_t*) -1) {
gSegment = NULL;
}
else
{
gSegment = tmp;
}
}
Table::Table(const ReadSection &inTableSection) :
mMetaRecord(inTableSection[OffsetId]),
mTableSection(inTableSection),
mRecordsCount(inTableSection[OffsetRecordsCount]),
mFreeListHead(inTableSection[OffsetFreeListHead]),
mRecordNumbersCount(inTableSection[OffsetRecordNumbersCount])
{
}
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<DbConstIndex> 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
{
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;
}
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];
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);
if (aRecordId.mRecordNumber != inRecordId.mRecordNumber)
CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
if (aRecordId.mCreateVersion != inRecordId.mCreateVersion)
CssmError::throwMe(CSSMERR_DL_INVALID_RECORD_UID);
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) return !(CSSM_DB_RECORDTYPE_SCHEMA_START <= anId
&& anId < CSSM_DB_RECORDTYPE_SCHEMA_END);
if (inTableId == CSSM_DL_DB_RECORD_ALL_KEYS) 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; }
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;
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 (!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
if (!mDeletedSet.insert(aRecordNumber).second)
CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND); }
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
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<WriteSection> aWriteSection(new WriteSection());
getMetaRecord().packRecord(*aWriteSection, inAttributes, inData);
uint32 aRecordNumber = nextRecordNumber();
MutableIndexMap::iterator it;
for (it = mIndexMap.begin(); it != mIndexMap.end(); it++)
it->second->insertRecord(aRecordNumber, *(aWriteSection.get()));
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);
bool aReUpdate = anIt != mInsertedMap.end();
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);
if (aRecordId.mCreateVersion != inRecordId.mCreateVersion)
CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND);
#if RECORDVERSIONCHECK
if (aRecordId.mRecordVersion != inRecordId.mRecordVersion)
CssmError::throwMe(CSSMERR_DL_STALE_UNIQUE_RECORD);
#endif
auto_ptr<WriteSection> aDbRecord(new WriteSection());
getMetaRecord().updateRecord(anOldDbRecord, *aDbRecord,
CssmDbRecordAttributeData::overlay(inAttributes), inData, inModifyMode);
RecordId aNewRecordId(aRecordNumber, inRecordId.mCreateVersion, inRecordId.mRecordVersion + 1);
MetaRecord::packRecordId(aNewRecordId, *aDbRecord);
if (!aReUpdate && !mDeletedSet.insert(aRecordNumber).second)
CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND);
MutableIndexMap::iterator it;
for (it = mIndexMap.begin(); it != mIndexMap.end(); it++)
it->second->removeRecord(aRecordNumber);
try
{
for (it = mIndexMap.begin(); it != mIndexMap.end(); it++)
it->second->insertRecord(aRecordNumber, *(aDbRecord.get()));
if (aReUpdate)
{
delete anIt->second;
anIt->second = aDbRecord.get();
}
else
{
mInsertedMap.insert(InsertedMap::value_type(aRecordNumber, aDbRecord.get()));
}
aDbRecord.release();
}
catch(...)
{
if (!aReUpdate)
mDeletedSet.erase(aRecordNumber);
MutableIndexMap::iterator it;
for (it = mIndexMap.begin(); it != mIndexMap.end(); it++)
it->second->removeRecord(aRecordNumber);
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())
{
const ReadSection &aRecordSection = *(anIt->second);
const RecordId aRecordId = MetaRecord::unpackRecordId(aRecordSection);
if (aRecordId.mRecordNumber != aRecordNumber)
CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
if (aRecordId.mCreateVersion != inRecordId.mCreateVersion)
CssmError::throwMe(CSSMERR_DL_INVALID_RECORD_UID);
getMetaRecord().unpackRecord(aRecordSection, inAllocator,
inoutAttributes, inoutData, 0);
return aRecordId;
}
else if (mDeletedSet.find(aRecordNumber) != mDeletedSet.end())
{
CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND);
}
}
if (!mTable)
CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND);
return mTable->getRecord(inRecordId, inoutAttributes, inoutData,
inAllocator);
}
uint32
ModifiedTable::nextRecordNumber()
{
if (mFreeListHead)
return mTable->popFreeList(mFreeListHead);
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();
}
void
ModifiedTable::modifyTable()
{
if (!mIsModified) {
createMutableIndexes();
mIsModified = true;
}
}
void
ModifiedTable::createMutableIndexes()
{
if (mTable == NULL)
return;
Table::ConstIndexMap::const_iterator it;
for (it = mTable->mIndexMap.begin(); it != mTable->mIndexMap.end(); it++) {
auto_ptr<DbMutableIndex> mutableIndex(new DbMutableIndex(*it->second));
mIndexMap.insert(MutableIndexMap::value_type(it->first, mutableIndex.get()));
mutableIndex.release();
}
}
DbMutableIndex &
ModifiedTable::findIndex(uint32 indexId, const MetaRecord &metaRecord, bool isUniqueIndex)
{
MutableIndexMap::iterator it = mIndexMap.find(indexId);
if (it == mIndexMap.end()) {
auto_ptr<DbMutableIndex> 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);
uint32 indexSectionOffset = offset;
offset += AtomSize;
offset = tableSection.put(offset, (uint32)mIndexMap.size());
uint32 indexOffsetOffset = offset;
offset += mIndexMap.size() * AtomSize;
for (it = mIndexMap.begin(); it != mIndexMap.end(); it++) {
indexOffsetOffset = tableSection.put(indexOffsetOffset, offset);
offset = it->second->writeIndex(tableSection, offset);
}
tableSection.put(indexSectionOffset, offset - indexSectionOffset);
return offset;
}
uint32
ModifiedTable::writeTable(AtomicTempFile &inAtomicTempFile, uint32 inSectionOffset)
{
if (mTable && !mIsModified) {
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;
}
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)
{
assert(mNewMetaRecord == nil);
uint32 anOldRecordsCount = mTable->getRecordsCount();
ReadSection aRecordsSection = mTable->getRecordsSection();
uint32 aReadOffset = 0; uint32 aWriteOffset = aRecordsOffset; uint32 aBlockStart = aReadOffset; uint32 aBlockSize = 0; 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())
{
aTableSection.put(Table::OffsetRecordNumbers
+ AtomSize * aRecordNumber,
aWriteOffset);
aWriteOffset += aRecordSize;
aBlockSize += aRecordSize;
aRecordsCount++;
}
else
{
if (aBlockSize > 0)
{
inAtomicTempFile.write(AtomicFile::FromStart, anOffset,
aRecordsSection.range(Range(aBlockStart,
aBlockSize)),
aBlockSize);
anOffset += aBlockSize;
}
aBlockStart = aReadOffset;
aBlockSize = 0;
} }
if (aBlockSize > 0)
{
inAtomicTempFile.write(AtomicFile::FromStart, anOffset,
aRecordsSection.range(Range(aBlockStart,
aBlockSize)),
aBlockSize);
anOffset += aBlockSize;
}
}
InsertedMap::const_iterator anIt = mInsertedMap.begin();
InsertedMap::const_iterator anEnd = mInsertedMap.end();
for (; anIt != anEnd; anIt++)
{
const WriteSection &aRecord = *anIt->second;
uint32 aRecordNumber = anIt->first;
aTableSection.put(Table::OffsetRecordNumbers + AtomSize * aRecordNumber,
anOffset - inSectionOffset);
inAtomicTempFile.write(AtomicFile::FromStart, anOffset,
aRecord.address(), aRecord.size());
anOffset += aRecord.size();
aRecordsCount++;
}
uint32 aFreeListHead = 0; for (uint32 aRecordNumber = 0; aRecordNumber < aRecordNumbersCount; aRecordNumber++)
{
if (!aTableSection.at(Table::OffsetRecordNumbers + AtomSize * aRecordNumber))
{
aTableSection.put(Table::OffsetRecordNumbers
+ AtomSize * aRecordNumber,
aFreeListHead);
aFreeListHead = (Table::OffsetRecordNumbers + AtomSize * aRecordNumber) | 1;
}
}
aTableSection.put(Table::OffsetFreeListHead, aFreeListHead);
anOffset -= inSectionOffset;
{
uint32 indexOffset = anOffset;
anOffset = writeIndexSection(aTableSection, anOffset);
inAtomicTempFile.write(AtomicFile::FromStart, inSectionOffset + indexOffset,
aTableSection.address() + indexOffset, anOffset - indexOffset);
}
aTableSection.put(Table::OffsetSize, anOffset);
aTableSection.put(Table::OffsetRecordsCount, aRecordsCount);
inAtomicTempFile.write(AtomicFile::FromStart, inSectionOffset,
aTableSection.address(), aTableSection.size());
return anOffset + inSectionOffset;
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-const-variable"
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[] =
{
ATTRIBUTE(UINT32, RelationID),
ATTRIBUTE(STRING, RelationName)
};
static const CSSM_DB_ATTRIBUTE_INFO AttrSchemaAttributes[] =
{
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)
};
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)
};
#undef ATTRIBUTE
#pragma clang diagnostic pop
DbVersion::DbVersion(const AppleDatabase &db, const RefPointer <AtomicBufferedFile> &inAtomicBufferedFile) :
mDatabase(reinterpret_cast<const uint8 *>(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
{
mVersionId = mDatabase[mDatabase.size() - AtomSize];
const ReadSection aHeaderSection = mDatabase.subsection(HeaderOffset,
HeaderSize);
if (aHeaderSection.at(OffsetMagic) != HeaderMagic)
CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
uint32 aVersion = aHeaderSection.at(OffsetVersion);
if (aVersion != HeaderVersion)
CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
uint32 aSchemaOffset = aHeaderSection.at(OffsetSchemaOffset);
const ReadSection aSchemaSection =
mDatabase.subsection(HeaderOffset + aSchemaOffset);
uint32 aSchemaSize = aSchemaSection[OffsetSchemaSize];
aSchemaSection.subsection(0, aSchemaSize);
uint32 aTableCount = aSchemaSection[OffsetTablesCount];
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);
const ReadSection aTableSection =
aSchemaSection.subsection(aTableOffset);
auto_ptr<Table> aTable(new Table(aTableSection));
Table::Id aTableId = aTable->getMetaRecord().dataRecordType();
mTableMap.insert(TableMap::value_type(aTableId, aTable.get()));
aTable.release();
}
findTable(mDb.schemaRelations.DataRecordType).getMetaRecord().
setRecordAttributeInfo(mDb.schemaRelations);
findTable(mDb.schemaIndexes.DataRecordType).getMetaRecord().
setRecordAttributeInfo(mDb.schemaIndexes);
findTable(mDb.schemaParsingModule.DataRecordType).getMetaRecord().
setRecordAttributeInfo(mDb.schemaParsingModule);
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);
if (aRecordData[0].size() != 1 || aRecordData[0].format() != CSSM_DB_ATTRIBUTE_FORMAT_UINT32)
CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
uint32 aRelationId = aRecordData[0];
if (CSSM_DB_RECORDTYPE_SCHEMA_START <= aRelationId && aRelationId < CSSM_DB_RECORDTYPE_SCHEMA_END)
continue;
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<string> 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<string> aName2(new string(static_cast<string>(aRecordData[3])));
aName = aName2;
}
if (aRecordData[4].size() == 1)
{
if (aRecordData[4].format() != CSSM_DB_ATTRIBUTE_FORMAT_BLOB)
CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
aNameID = &static_cast<const CssmData &>(aRecordData[4]);
}
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);
}
aMetaRecord.createAttribute(aName.get(), aNameID, anAttributeId, anAttributeFormat);
}
{
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
{
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::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::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;
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)
{
aMatch = true;
}
else if (mConjunctive == CSSM_DB_OR)
{
aMatch = false;
for (; anIt != anEnd; anIt++)
{
if ((*anIt)->evaluate(aRecordSection))
{
aMatch = true;
break;
}
}
}
else if (mConjunctive == CSSM_DB_AND || mConjunctive == CSSM_DB_NONE)
{
aMatch = true;
for (; anIt != anEnd; anIt++)
{
if (!(*anIt)->evaluate(aRecordSection))
{
aMatch = false;
break;
}
}
}
else
{
CssmError::throwMe(CSSMERR_DL_UNSUPPORTED_QUERY);
}
if (aMatch)
{
mMetaRecord.unpackRecord(aRecordSection, inAllocator,
inoutAttributes, inoutData,
mQueryFlags);
outTableId = mMetaRecord.dataRecordType();
recordId = MetaRecord::unpackRecordId(aRecordSection);
return true;
}
}
return false;
}
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()
{
}
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(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(AtomicFile &inAtomicFile, const AppleDatabase &db) :
Metadata(),
mDbVersion(),
mAtomicFile(inAtomicFile),
mDb(db)
{
}
DbModifier::~DbModifier()
{
try
{
for_each_map_delete(mModifiedTableMap.begin(), mModifiedTableMap.end());
}
catch(...) {}
}
const RefPointer<const DbVersion>
DbModifier::getDbVersion(bool force)
{
StLock<Mutex> _(mDbVersionLock);
pthread_once(&gCommonInitMutex, initCommon);
if (!mDbVersion ||
force ||
gSegment == NULL ||
mNotifyCount != *gSegment ||
CFAbsoluteTimeGetCurrent() > mDbLastRead + kForceReReadTime)
{
RefPointer <AtomicBufferedFile> atomicBufferedFile(mAtomicFile.read());
off_t length = atomicBufferedFile->open();
if (gSegment != NULL)
{
mNotifyCount = *gSegment;
}
mDbLastRead = CFAbsoluteTimeGetCurrent();
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 (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)
{
if (mAtomicTempFile || !mModifiedTableMap.empty())
CssmError::throwMe(CSSMERR_DL_DATASTORE_ALREADY_EXISTS);
mAtomicTempFile = mAtomicFile.create(mode);
mVersionId = 1;
createTable(new MetaRecord(mDb.schemaRelations));
createTable(new MetaRecord(mDb.schemaAttributes));
createTable(new MetaRecord(mDb.schemaIndexes));
createTable(new MetaRecord(mDb.schemaParsingModule));
insertTableSchema(mDb.schemaRelations);
insertTableSchema(mDb.schemaAttributes);
insertTableSchema(mDb.schemaIndexes);
insertTableSchema(mDb.schemaParsingModule);
if (inInitialAclEntry != NULL)
{
}
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()
{
if (!mAtomicTempFile)
getDbVersion(false);
}
void DbModifier::closeDatabase()
{
commit(); StLock<Mutex> _(mDbVersionLock);
mDbVersion = NULL;
}
void DbModifier::deleteDatabase()
{
bool isDirty = mAtomicTempFile;
rollback(); StLock<Mutex> _(mDbVersionLock);
for_each_map_delete(mModifiedTableMap.begin(), mModifiedTableMap.end());
mModifiedTableMap.clear();
if (!isDirty || mDbVersion)
{
mDbVersion = NULL;
mAtomicFile.performDelete();
}
}
void
DbModifier::modifyDatabase()
{
if (mAtomicTempFile)
return;
try
{
mAtomicTempFile = mAtomicFile.write();
mVersionId = getDbVersion(true)->getVersionId() + 1;
if (mVersionId == 0)
mVersionId = 1;
for_each_map_delete(mModifiedTableMap.begin(), mModifiedTableMap.end());
mModifiedTableMap.clear();
DbVersion::TableMap::const_iterator anIt =
mDbVersion->mTableMap.begin();
DbVersion::TableMap::const_iterator anEnd =
mDbVersion->mTableMap.end();
for (; anIt != anEnd; ++anIt)
{
auto_ptr<ModifiedTable> 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);
}
ModifiedTable *
DbModifier::createTable(MetaRecord *inMetaRecord)
{
auto_ptr<MetaRecord> aMetaRecord(inMetaRecord);
auto_ptr<ModifiedTable> aModifiedTable(new ModifiedTable(inMetaRecord));
aMetaRecord.release();
if (!mModifiedTableMap.insert
(ModifiedTableMap::value_type(inMetaRecord->dataRecordType(),
aModifiedTable.get())).second)
{
CssmError::throwMe(CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA);
}
return aModifiedTable.release();
}
void
DbModifier::deleteTable(Table::Id inTableId)
{
modifyDatabase();
if (CSSM_DB_RECORDTYPE_SCHEMA_START <= inTableId
&& inTableId < CSSM_DB_RECORDTYPE_SCHEMA_END)
CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE);
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;
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);
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++)
{
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
{
secinfo("integrity", "committing to %s", mAtomicFile.path().c_str());
WriteSection aHeaderSection(Allocator::standard(), size_t(HeaderSize));
aHeaderSection.size(HeaderSize);
uint32 anOffset = HeaderOffset + HeaderSize;
aHeaderSection.put(OffsetAuthOffset, anOffset);
anOffset = writeAuthSection(anOffset);
aHeaderSection.put(OffsetSchemaOffset, anOffset);
anOffset = writeSchemaSection(anOffset);
aHeaderSection.put(OffsetMagic, HeaderMagic);
aHeaderSection.put(OffsetVersion, HeaderVersion);
mAtomicTempFile->write(AtomicFile::FromStart, HeaderOffset,
aHeaderSection.address(), aHeaderSection.size());
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;
pthread_once(&gCommonInitMutex, initCommon);
if (gSegment != NULL)
{
OSAtomicIncrement32Barrier (gSegment);
}
}
catch(...)
{
rollback();
throw;
}
}
void
DbModifier::rollback() throw()
{
mAtomicTempFile = NULL;
}
const RecordId
DbModifier::getRecord(Table::Id inTableId, const RecordId &inRecordId,
CSSM_DB_RECORD_ATTRIBUTE_DATA *inoutAttributes,
CssmData *inoutData, Allocator &inAllocator)
{
if (mAtomicTempFile)
{
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)
{
if (mDbVersion)
return mDbVersion->createCursor(inQuery);
return new Cursor();
}
return getDbVersion(false)->createCursor(inQuery);
}
void
DbModifier::insertTableSchema(const CssmDbRecordAttributeInfo &inInfo,
const CSSM_DB_RECORD_INDEX_INFO *inIndexInfo )
{
ModifiedTable &aTable = findTable(inInfo.DataRecordType);
const MetaRecord &aMetaRecord = aTable.getMetaRecord();
CssmAutoDbRecordAttributeData aRecordBuilder(5);
aRecordBuilder.add(RelationID, inInfo.recordType());
aRecordBuilder.add(RelationName, mDb.recordName(inInfo.recordType()));
findTable(mDb.schemaRelations.DataRecordType).insertRecord(mVersionId,
&aRecordBuilder, NULL);
ModifiedTable &anAttributeTable = findTable(mDb.schemaAttributes.DataRecordType);
for (uint32 anIndex = 0; anIndex < inInfo.size(); anIndex++)
{
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());
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]);
if (thisIndex.dataLocation() != CSSM_DB_INDEX_ON_ATTRIBUTE)
CssmError::throwMe(CSSMERR_DL_INVALID_INDEX_INFO);
uint32 indexId;
if (thisIndex.IndexType == CSSM_DB_INDEX_UNIQUE)
indexId = 0;
else
indexId = anIndex + 1;
uint32 attributeId =
aMetaRecord.metaAttribute(thisIndex.Info).attributeId();
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);
indexMetaTable.insertRecord(mVersionId, &aRecordBuilder, NULL);
DbMutableIndex &index = aTable.findIndex(indexId, aMetaRecord, indexId == 0);
index.appendAttribute(attributeId);
}
}
}
void
DbModifier::insertTable(const CssmDbRecordAttributeInfo &inInfo,
const CSSM_DB_RECORD_INDEX_INFO *inIndexInfo ,
const CSSM_DB_PARSING_MODULE_INFO *inParsingModule )
{
modifyDatabase();
createTable(new MetaRecord(inInfo));
insertTableSchema(inInfo, inIndexInfo);
}
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);
aRecordBuilder.add(RelationID, inTableId);
aRecordBuilder.add(RelationName, inTableName);
findTable(mDb.schemaRelations.DataRecordType).insertRecord(mVersionId,
&aRecordBuilder, NULL);
ModifiedTable &anAttributeTable = findTable(mDb.schemaAttributes.DataRecordType);
for (uint32 anIndex = 0; anIndex < inNumberOfAttributes; anIndex++)
{
aRecordBuilder.clear();
aRecordBuilder.add(RelationID, inTableId);
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);
anAttributeTable.insertRecord(mVersionId, &aRecordBuilder, NULL);
}
ModifiedTable &anIndexTable = findTable(mDb.schemaIndexes.DataRecordType);
for (uint32 anIndex = 0; anIndex < inNumberOfIndexes; anIndex++)
{
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);
anIndexTable.insertRecord(mVersionId, &aRecordBuilder, NULL);
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::AppleDatabaseManager(const AppleDatabaseTableName *tableNames)
: DatabaseManager(),
mTableNames(tableNames)
{
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);
}
extern "C" {
typedef struct cssm_appledl_open_parameters_v0
{
uint32 length;
uint32 version;
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<const CSSM_APPLEDL_OPEN_PARAMETERS *>(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;
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::AppleDatabase(const DbName &inDbName, const AppleDatabaseTableName *tableNames) :
Database(inDbName),
schemaRelations(tableNames[AppleDatabaseTableName::kSchemaInfo].mTableId,
sizeof(AttrSchemaRelations) / sizeof(CSSM_DB_ATTRIBUTE_INFO),
const_cast<CSSM_DB_ATTRIBUTE_INFO_PTR>(AttrSchemaRelations)),
schemaAttributes(tableNames[AppleDatabaseTableName::kSchemaAttributes].mTableId,
sizeof(AttrSchemaAttributes) / sizeof(CSSM_DB_ATTRIBUTE_INFO),
const_cast<CSSM_DB_ATTRIBUTE_INFO_PTR>(AttrSchemaAttributes)),
schemaIndexes(tableNames[AppleDatabaseTableName::kSchemaIndexes].mTableId,
sizeof(AttrSchemaIndexes) / sizeof(CSSM_DB_ATTRIBUTE_INFO),
const_cast<CSSM_DB_ATTRIBUTE_INFO_PTR>(AttrSchemaIndexes)),
schemaParsingModule(tableNames[AppleDatabaseTableName::kSchemaParsingModule].mTableId,
sizeof(AttrSchemaParsingModule) / sizeof(CSSM_DB_ATTRIBUTE_INFO),
const_cast<CSSM_DB_ATTRIBUTE_INFO_PTR>(AttrSchemaParsingModule)),
mAtomicFile(mDbName.dbName()),
mDbModifier(mAtomicFile, *this),
mTableNames(tableNames)
{
if(!strcmp(inDbName.dbName(), "/System/Library/Keychains/X509Anchors")) {
Syslog::alert("Warning: accessing obsolete X509Anchors.");
}
}
AppleDatabase::~AppleDatabase()
{
}
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<AppleDbContext &>(inDbContext);
try
{
StLock<Mutex> _(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<Mutex> _(mWriteLock);
mDbModifier.closeDatabase();
}
void
AppleDatabase::dbDelete(DatabaseSession &inDatabaseSession,
const AccessCredentials *inAccessCred)
{
StLock<Mutex> _(mWriteLock);
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<Mutex> _(mWriteLock);
mDbModifier.insertTable(inRelationID, inRelationName,
inNumberOfAttributes, inAttributeInfo,
inNumberOfIndexes, &inIndexInfo);
}
catch(...)
{
if (safer_cast<AppleDbContext &>(inDbContext).autoCommit())
mDbModifier.rollback();
throw;
}
if (safer_cast<AppleDbContext &>(inDbContext).autoCommit())
mDbModifier.commit();
}
void
AppleDatabase::destroyRelation(DbContext &inDbContext,
CSSM_DB_RECORDTYPE inRelationID)
{
try
{
StLock<Mutex> _(mWriteLock);
mDbModifier.deleteTable(inRelationID);
}
catch(...)
{
if (safer_cast<AppleDbContext &>(inDbContext).autoCommit())
mDbModifier.rollback();
throw;
}
if (safer_cast<AppleDbContext &>(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<Mutex> _(mWriteLock);
const RecordId aRecordId =
mDbModifier.insertRecord(inRecordType, inAttributes, inData);
anUniqueRecordPtr = createUniqueRecord(inDbContext, inRecordType,
aRecordId);
if (safer_cast<AppleDbContext &>(inDbContext).autoCommit())
mDbModifier.commit();
}
catch(...)
{
if (anUniqueRecordPtr != NULL)
freeUniqueRecord(inDbContext, *anUniqueRecordPtr);
if (safer_cast<AppleDbContext &>(inDbContext).autoCommit())
mDbModifier.rollback();
throw;
}
return anUniqueRecordPtr;
}
void
AppleDatabase::dataDelete(DbContext &inDbContext,
const CSSM_DB_UNIQUE_RECORD &inUniqueRecord)
{
try
{
StLock<Mutex> _(mWriteLock);
Table::Id aTableId;
const RecordId aRecordId(parseUniqueRecord(inUniqueRecord, aTableId));
mDbModifier.deleteRecord(aTableId, aRecordId);
}
catch(...)
{
if (safer_cast<AppleDbContext &>(inDbContext).autoCommit())
mDbModifier.rollback();
throw;
}
if (safer_cast<AppleDbContext &>(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<Mutex> _(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<AppleDbContext &>(inDbContext).autoCommit())
mDbModifier.rollback();
throw;
}
if (safer_cast<AppleDbContext &>(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)
{
auto_ptr<Cursor> aCursor(mDbModifier.createCursor(inQuery));
Table::Id aTableId;
RecordId aRecordId;
if (!aCursor->next(aTableId, inoutAttributes, inoutData,
inDbContext.mDatabaseSession, aRecordId))
return CSSM_INVALID_HANDLE;
outUniqueRecord = createUniqueRecord(inDbContext, aTableId, aRecordId);
return aCursor.release()->handle(); }
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<Cursor> aCursor(&HandleObject::find<Cursor>(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<Cursor>(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));
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<uint32 *>(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<CSSM_DB_UNIQUE_RECORD>();
memset(aUniqueRecord, 0, sizeof(*aUniqueRecord));
aUniqueRecord->RecordIdentifier.Length = sizeof(uint32) * 4;
try
{
aUniqueRecord->RecordIdentifier.Data =
inDbContext.mDatabaseSession.alloc<uint8>(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<uint32 *>(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<AppleDbContext &>(dbContext);
if (outputParams)
*reinterpret_cast<CSSM_BOOL *>(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_APPLEFILEDL_TAKE_FILE_LOCK:
mDbModifier.modifyDatabase();
break;
case CSSM_APPLEFILEDL_MAKE_BACKUP:
dbMakeBackup();
break;
case CSSM_APPLEFILEDL_MAKE_COPY:
dbMakeCopy((const char *) inputParams);
break;
case CSSM_APPLEFILEDL_DELETE_FILE:
dbDeleteFile();
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);
}
}
void
AppleDatabase::dbMakeBackup() {
char * filename_temp_cstr = tempnam( mAtomicFile.dir().c_str(), (mAtomicFile.file() + "_").c_str() );
string filename_temp(filename_temp_cstr);
filename_temp += "_backup";
free(filename_temp_cstr);
dbMakeCopy(filename_temp.c_str());
}
void
AppleDatabase::dbMakeCopy(const char* path) {
if(copyfile(mAtomicFile.path().c_str(), path, NULL, COPYFILE_UNLINK | COPYFILE_ALL) < 0) {
UnixError::throwMe(errno);
}
}
void AppleDatabase::dbDeleteFile() {
if(unlink(mAtomicFile.path().c_str()) < 0) {
UnixError::throwMe(errno);
}
}