#ifndef _H_APPLEDATABASE
#define _H_APPLEDATABASE
#include "MetaRecord.h"
#include "SelectionPredicate.h"
#include "DbIndex.h"
#include <security_filedb/AtomicFile.h>
#include <security_cdsa_plugin/Database.h>
#include <security_cdsa_plugin/DbContext.h>
#include <security_cdsa_utilities/handleobject.h>
#include <security_utilities/refcount.h>
#include <memory>
#include <vector>
#include <CoreFoundation/CFDate.h>
namespace Security
{
class Cursor;
class DbVersion;
class CssmAutoQuery;
struct AppleDatabaseTableName
{
uint32 mTableId;
const char *mTableName;
enum {
kSchemaInfo = 0,
kSchemaAttributes,
kSchemaIndexes,
kSchemaParsingModule,
kNumRequiredTableNames
};
};
class Table
{
NOCOPY(Table)
public:
typedef CSSM_DB_RECORDTYPE Id;
Table(const ReadSection &inTableSection);
~Table();
Cursor *createCursor(const CSSM_QUERY *inQuery, const DbVersion &inDbVersion) const;
const ReadSection getRecordSection(uint32 inRecordNumber) const;
const RecordId getRecord(const RecordId &inRecordId,
CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes,
CssmData *inoutData,
Allocator &inAllocator) const;
uint32 recordNumberCount() const { return mRecordNumbersCount; }
uint32 freeListHead() const { return mFreeListHead; }
uint32 popFreeList(uint32 &aFreeListHead) const;
MetaRecord &getMetaRecord() { return mMetaRecord; }
const MetaRecord &getMetaRecord() const { return mMetaRecord; }
uint32 getRecordsCount() const { return mRecordsCount; }
const ReadSection getRecordsSection() const;
const ReadSection &getTableSection() const { return mTableSection; }
bool matchesTableId(Id inTableId) const;
void readIndexSection();
enum
{
OffsetSize = AtomSize * 0,
OffsetId = AtomSize * 1,
OffsetRecordsCount = AtomSize * 2,
OffsetRecords = AtomSize * 3,
OffsetIndexesOffset = AtomSize * 4,
OffsetFreeListHead = AtomSize * 5,
OffsetRecordNumbersCount = AtomSize * 6,
OffsetRecordNumbers = AtomSize * 7
};
protected:
friend class ModifiedTable;
MetaRecord mMetaRecord;
const ReadSection mTableSection;
uint32 mRecordsCount;
uint32 mFreeListHead;
uint32 mRecordNumbersCount;
typedef map<uint32, DbConstIndex *> ConstIndexMap;
ConstIndexMap mIndexMap;
};
class ModifiedTable
{
NOCOPY(ModifiedTable)
public:
ModifiedTable(const Table *inTable);
ModifiedTable(MetaRecord *inMetaRecord); ~ModifiedTable();
void deleteRecord(const RecordId &inRecordId);
const RecordId insertRecord(uint32 inVersionId,
const CSSM_DB_RECORD_ATTRIBUTE_DATA *inAttributes,
const CssmData *inData);
const RecordId updateRecord(const RecordId &inRecordId,
const CSSM_DB_RECORD_ATTRIBUTE_DATA *inAttributes,
const CssmData *inData,
CSSM_DB_MODIFY_MODE inModifyMode);
const RecordId getRecord(const RecordId &inRecordId,
CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes,
CssmData *inoutData,
Allocator &inAllocator) const;
const MetaRecord &getMetaRecord() const;
DbMutableIndex &findIndex(uint32 indexId, const MetaRecord &metaRecord, bool isUniqueIndex);
uint32 writeTable(AtomicTempFile &inAtomicTempFile, uint32 inSectionOffset);
private:
uint32 nextRecordNumber();
uint32 recordNumberCount() const;
void modifyTable();
void createMutableIndexes();
uint32 writeIndexSection(WriteSection &tableSection, uint32 offset);
const Table *mTable;
const MetaRecord *mNewMetaRecord;
typedef set<uint32> DeletedSet;
DeletedSet mDeletedSet;
typedef map<uint32, WriteSection *> InsertedMap;
InsertedMap mInsertedMap;
uint32 mRecordNumberCount;
uint32 mFreeListHead;
bool mIsModified;
typedef map<uint32, DbMutableIndex *> MutableIndexMap;
MutableIndexMap mIndexMap;
};
class Metadata
{
NOCOPY(Metadata)
protected:
Metadata() {}
enum
{
HeaderOffset = 0, OffsetMagic = AtomSize * 0,
OffsetVersion = AtomSize * 1,
OffsetAuthOffset = AtomSize * 2,
OffsetSchemaOffset = AtomSize * 3,
HeaderSize = AtomSize * 4,
HeaderMagic = FOUR_CHAR_CODE('kych'),
HeaderVersion = 0x00010000
};
enum
{
OffsetSchemaSize = AtomSize * 0,
OffsetTablesCount = AtomSize * 1,
OffsetTables = AtomSize * 2
};
};
class DbVersion : public Metadata, public RefCount
{
NOCOPY(DbVersion)
public:
DbVersion(const class AppleDatabase &db, const RefPointer <AtomicBufferedFile> &inAtomicBufferedFile);
~DbVersion();
uint32 getVersionId() const { return mVersionId; }
const RecordId getRecord(Table::Id inTableId, const RecordId &inRecordId,
CSSM_DB_RECORD_ATTRIBUTE_DATA *inoutAttributes,
CssmData *inoutData, Allocator &inAllocator) const;
Cursor *createCursor(const CSSM_QUERY *inQuery) const;
protected:
const Table &findTable(Table::Id inTableId) const;
Table &findTable(Table::Id inTableId);
private:
void open();
ReadSection mDatabase;
uint32 mVersionId;
friend class DbModifier; typedef map<Table::Id, Table *> TableMap;
TableMap mTableMap;
const class AppleDatabase &mDb;
RefPointer<AtomicBufferedFile> mBufferedFile;
public:
typedef Table value_type;
typedef const Table &const_reference;
typedef const Table *const_pointer;
class const_iterator
{
public:
const_iterator(const TableMap::const_iterator &it) : mIterator(it) {}
const_reference operator*() const { return *mIterator->second; }
const_iterator &operator++() { mIterator.operator++(); return *this; }
const_iterator operator++(int i) { return const_iterator(mIterator.operator++(i)); }
bool operator!=(const const_iterator &other) const { return mIterator != other.mIterator; }
bool operator==(const const_iterator &other) const { return mIterator == other.mIterator; }
const_pointer operator->() const { return mIterator->second; }
private:
TableMap::const_iterator mIterator;
};
const_iterator begin() const { return const_iterator(mTableMap.begin()); }
const_iterator end() const { return const_iterator(mTableMap.end()); }
bool hasTable(Table::Id inTableId) const;
};
class Cursor : public HandleObject
{
public:
Cursor();
Cursor(const DbVersion &inDbVersion);
virtual ~Cursor();
virtual bool next(Table::Id &outTableId,
CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR outAttributes,
CssmData *outData,
Allocator &inAllocator,
RecordId &recordId);
protected:
const RefPointer<const DbVersion> mDbVersion;
};
class LinearCursor : public Cursor
{
NOCOPY(LinearCursor)
public:
LinearCursor(const CSSM_QUERY *inQuery, const DbVersion &inDbVersion,
const Table &inTable);
virtual ~LinearCursor();
virtual bool next(Table::Id &outTableId,
CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR outAttributes,
CssmData *outData,
Allocator &inAllocator,
RecordId &recordId);
private:
uint32 mRecordsCount;
uint32 mRecord;
const ReadSection mRecordsSection;
uint32 mReadOffset;
const MetaRecord &mMetaRecord;
CSSM_DB_CONJUNCTIVE mConjunctive;
CSSM_QUERY_FLAGS mQueryFlags; typedef vector<SelectionPredicate *> PredicateVector;
PredicateVector mPredicates;
};
class IndexCursor : public Cursor
{
NOCOPY(IndexCursor)
public:
IndexCursor(DbQueryKey *queryKey, const DbVersion &inDbVersion,
const Table &table, const DbConstIndex *index);
virtual ~IndexCursor();
virtual bool next(Table::Id &outTableId,
CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR outAttributes,
CssmData *outData,
Allocator &inAllocator,
RecordId &recordId);
private:
auto_ptr<DbQueryKey> mQueryKey;
const Table &mTable;
const DbConstIndex *mIndex;
DbIndexIterator mBegin, mEnd;
};
class MultiCursor : public Cursor
{
NOCOPY(MultiCursor)
public:
MultiCursor(const CSSM_QUERY *inQuery, const DbVersion &inDbVersion);
virtual ~MultiCursor();
virtual bool next(Table::Id &outTableId,
CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR outAttributes,
CssmData *outData,
Allocator &inAllocator,
RecordId &recordId);
private:
auto_ptr<CssmAutoQuery> mQuery;
DbVersion::const_iterator mTableIterator;
auto_ptr<Cursor> mCursor;
};
class DbModifier : public Metadata
{
NOCOPY(DbModifier)
public:
DbModifier(AtomicFile &inAtomicFile, const class AppleDatabase &db);
~DbModifier();
void createDatabase(const CSSM_DBINFO &inDbInfo,
const CSSM_ACL_ENTRY_INPUT *inInitialAclEntry,
mode_t mode);
void openDatabase(); void closeDatabase();
void deleteDatabase();
void commit();
void rollback() throw();
void deleteRecord(Table::Id inTableId, const RecordId &inRecordId);
const RecordId insertRecord(Table::Id inTableId,
const CSSM_DB_RECORD_ATTRIBUTE_DATA *inAttributes,
const CssmData *inData);
const RecordId updateRecord(Table::Id inTableId, const RecordId &inRecordId,
const CSSM_DB_RECORD_ATTRIBUTE_DATA *inAttributes,
const CssmData *inData,
CSSM_DB_MODIFY_MODE inModifyMode);
void 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);
void deleteTable(Table::Id inTableId);
const RecordId getRecord(Table::Id inTableId, const RecordId &inRecordId,
CSSM_DB_RECORD_ATTRIBUTE_DATA *inoutAttributes,
CssmData *inoutData, Allocator &inAllocator);
Cursor *createCursor(const CSSM_QUERY *inQuery);
bool hasTable(Table::Id inTableid);
protected:
void modifyDatabase();
const RefPointer<const DbVersion> getDbVersion(bool force);
ModifiedTable *createTable(MetaRecord *inMetaRecord);
void insertTableSchema(const CssmDbRecordAttributeInfo &inInfo,
const CSSM_DB_RECORD_INDEX_INFO *inIndexInfo = NULL);
void insertTable(const CssmDbRecordAttributeInfo &inInfo,
const CSSM_DB_RECORD_INDEX_INFO * inIndexInfo = NULL,
const CSSM_DB_PARSING_MODULE_INFO * inParsingModule = NULL);
ModifiedTable &findTable(Table::Id inTableId);
uint32 writeAuthSection(uint32 inSectionOffset);
uint32 writeSchemaSection(uint32 inSectionOffset);
private:
RefPointer<const DbVersion> mDbVersion;
int32_t mNotifyCount;
CFAbsoluteTime mDbLastRead;
Mutex mDbVersionLock;
AtomicFile &mAtomicFile;
uint32 mVersionId;
RefPointer<AtomicTempFile> mAtomicTempFile;
typedef map<Table::Id, ModifiedTable *> ModifiedTableMap;
ModifiedTableMap mModifiedTableMap;
const class AppleDatabase &mDb;
};
class AppleDatabaseManager : public DatabaseManager
{
public:
AppleDatabaseManager(const AppleDatabaseTableName *tableNames);
Database *make(const DbName &inDbName);
protected:
const AppleDatabaseTableName *mTableNames;
};
class AppleDbContext : public DbContext
{
public:
AppleDbContext(Database &inDatabase,
DatabaseSession &inDatabaseSession,
CSSM_DB_ACCESS_TYPE inAccessRequest,
const AccessCredentials *inAccessCred,
const void *inOpenParameters);
virtual ~AppleDbContext();
bool autoCommit() const { return mAutoCommit; }
void autoCommit(bool on) { mAutoCommit = on; }
mode_t mode() const { return mMode; }
private:
bool mAutoCommit;
mode_t mMode;
};
class AppleDatabase : public Database
{
public:
AppleDatabase(const DbName &inDbName, const AppleDatabaseTableName *tableNames);
virtual ~AppleDatabase();
virtual void
dbCreate(DbContext &inDbContext, const CSSM_DBINFO &inDBInfo,
const CSSM_ACL_ENTRY_INPUT *inInitialAclEntry);
virtual void
dbOpen(DbContext &inDbContext);
virtual void
dbClose();
virtual void
dbDelete(DatabaseSession &inDatabaseSession,
const AccessCredentials *inAccessCred);
virtual void
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);
virtual void
destroyRelation(DbContext &inDbContext,
CSSM_DB_RECORDTYPE inRelationID);
virtual void
authenticate(DbContext &inDbContext,
CSSM_DB_ACCESS_TYPE inAccessRequest,
const AccessCredentials &inAccessCred);
virtual void
getDbAcl(DbContext &inDbContext,
const CSSM_STRING *inSelectionTag,
uint32 &outNumberOfAclInfos,
CSSM_ACL_ENTRY_INFO_PTR &outAclInfos);
virtual void
changeDbAcl(DbContext &inDbContext,
const AccessCredentials &inAccessCred,
const CSSM_ACL_EDIT &inAclEdit);
virtual void
getDbOwner(DbContext &inDbContext, CSSM_ACL_OWNER_PROTOTYPE &outOwner);
virtual void
changeDbOwner(DbContext &inDbContext,
const AccessCredentials &inAccessCred,
const CSSM_ACL_OWNER_PROTOTYPE &inNewOwner);
virtual char *
getDbNameFromHandle(const DbContext &inDbContext) const;
virtual CSSM_DB_UNIQUE_RECORD_PTR
dataInsert(DbContext &inDbContext,
CSSM_DB_RECORDTYPE RecordType,
const CSSM_DB_RECORD_ATTRIBUTE_DATA *inAttributes,
const CssmData *inData);
virtual void
dataDelete(DbContext &inDbContext,
const CSSM_DB_UNIQUE_RECORD &inUniqueRecordIdentifier);
virtual void
dataModify(DbContext &inDbContext,
CSSM_DB_RECORDTYPE inRecordType,
CSSM_DB_UNIQUE_RECORD &inoutUniqueRecordIdentifier,
const CSSM_DB_RECORD_ATTRIBUTE_DATA *inAttributesToBeModified,
const CssmData *inDataToBeModified,
CSSM_DB_MODIFY_MODE inModifyMode);
virtual CSSM_HANDLE
dataGetFirst(DbContext &inDbContext,
const CssmQuery *inQuery,
CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes,
CssmData *inoutData,
CSSM_DB_UNIQUE_RECORD_PTR &outUniqueRecord);
virtual bool
dataGetNext(DbContext &inDbContext,
CSSM_HANDLE inResultsHandle,
CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes,
CssmData *inoutData,
CSSM_DB_UNIQUE_RECORD_PTR &outUniqueRecord);
virtual void
dataAbortQuery(DbContext &inDbContext,
CSSM_HANDLE inResultsHandle);
virtual void
dataGetFromUniqueRecordId(DbContext &inDbContext,
const CSSM_DB_UNIQUE_RECORD &inUniqueRecord,
CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes,
CssmData *inoutData);
virtual void
freeUniqueRecord(DbContext &inDbContext,
CSSM_DB_UNIQUE_RECORD &inUniqueRecord);
virtual void passThrough(DbContext &dbContext,
uint32 passThroughId,
const void *inputParams,
void **outputParams);
virtual DbContext *makeDbContext(DatabaseSession &inDatabaseSession,
CSSM_DB_ACCESS_TYPE inAccessRequest,
const AccessCredentials *inAccessCred,
const void *inOpenParameters);
const CssmDbRecordAttributeInfo schemaRelations;
const CssmDbRecordAttributeInfo schemaAttributes;
const CssmDbRecordAttributeInfo schemaIndexes;
const CssmDbRecordAttributeInfo schemaParsingModule;
const char *recordName(CSSM_DB_RECORDTYPE inRecordType) const;
private:
static void
updateUniqueRecord(DbContext &inDbContext,
CSSM_DB_RECORDTYPE inTableId,
const RecordId &inRecordId,
CSSM_DB_UNIQUE_RECORD &inoutUniqueRecord);
CSSM_DB_UNIQUE_RECORD_PTR
createUniqueRecord(DbContext &inDbContext, CSSM_DB_RECORDTYPE inTableId,
const RecordId &inRecordId);
const RecordId parseUniqueRecord(const CSSM_DB_UNIQUE_RECORD &inUniqueRecord,
CSSM_DB_RECORDTYPE &outTableId);
Mutex mWriteLock;
AtomicFile mAtomicFile;
DbModifier mDbModifier;
const AppleDatabaseTableName *mTableNames;
};
}
#endif //_H_APPLEDATABASE