1/*
2 * Copyright (c) 2000-2001,2003,2011-2014 Apple Inc. All Rights Reserved.
3 *
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please obtain
7 * a copy of the License at http://www.apple.com/publicsource and read it before
8 * using this file.
9 *
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
15 * specific language governing rights and limitations under the License.
16 */
17
18
19//
20//  AppleDatabase.cpp - Description t.b.d.
21//
22#include "AppleDatabase.h"
23#include <security_cdsa_plugin/DatabaseSession.h>
24#include <security_cdsa_plugin/DbContext.h>
25#include <security_cdsa_utilities/cssmdb.h>
26#include <Security/cssmapple.h>
27#include <security_utilities/trackingallocator.h>
28#include <security_utilities/logging.h>
29#include <fcntl.h>
30#include <memory>
31#include <libkern/OSAtomic.h>
32#include <stdlib.h>
33#include <sys/mman.h>
34#include <fcntl.h>
35#include <Security/cssmapplePriv.h>
36#include <syslog.h>
37
38static const char *kAppleDatabaseChanged = "com.apple.AppleDatabaseChanged";
39
40/* Number of seconds after which we open/pread/close a db to check it's
41   version number even if we didn't get any notifications.  Note that we always
42   check just after we take a write lock and whenever we get a notification
43   that any db on the system has changed. */
44static const CFTimeInterval kForceReReadTime = 15.0;
45
46/* Token on which we receive notifications and the pthread_once_t protecting
47   it's initialization. */
48pthread_once_t gCommonInitMutex = PTHREAD_ONCE_INIT;
49
50/* Global counter of how many notifications we have received and a lock to
51   protect the counter. */
52static int kSegmentSize = 4;
53int32_t* gSegment = NULL;
54
55/* Registration routine for notifcations. Called inside a pthread_once(). */
56static void initCommon(void)
57{
58	// open the file
59	int segmentDescriptor = shm_open (kAppleDatabaseChanged, O_RDWR | O_CREAT, S_IRWXU | S_IRWXG | S_IRWXO);
60	if (segmentDescriptor < 0)
61	{
62		return;
63	}
64
65	// set the segment size
66	ftruncate (segmentDescriptor, kSegmentSize);
67
68	// map it into memory
69	int32_t* tmp = (int32_t*) mmap (NULL, kSegmentSize, PROT_READ | PROT_WRITE, MAP_SHARED, segmentDescriptor, 0);
70	close (segmentDescriptor);
71
72	if (tmp == (int32_t*) -1) // can't map the memory?
73	{
74		gSegment = NULL;
75	}
76	else
77	{
78		gSegment = tmp;
79	}
80}
81
82//
83// Table
84//
85Table::Table(const ReadSection &inTableSection) :
86	mMetaRecord(inTableSection[OffsetId]),
87	mTableSection(inTableSection),
88	mRecordsCount(inTableSection[OffsetRecordsCount]),
89	mFreeListHead(inTableSection[OffsetFreeListHead]),
90	mRecordNumbersCount(inTableSection[OffsetRecordNumbersCount])
91{
92	// can't easily initialize indexes here, since meta record is incomplete
93	// until much later... see DbVersion::open()
94}
95
96Table::~Table()
97{
98	for_each_map_delete(mIndexMap.begin(), mIndexMap.end());
99}
100
101void
102Table::readIndexSection()
103{
104	uint32 indexSectionOffset = mTableSection.at(OffsetIndexesOffset);
105
106	uint32 numIndexes = mTableSection.at(indexSectionOffset + AtomSize);
107
108	for (uint32 i = 0; i < numIndexes; i++) {
109		uint32 indexOffset = mTableSection.at(indexSectionOffset + (i + 2) * AtomSize);
110		ReadSection indexSection(mTableSection.subsection(indexOffset));
111
112		auto_ptr<DbConstIndex> index(new DbConstIndex(*this, indexSection));
113		mIndexMap.insert(ConstIndexMap::value_type(index->indexId(), index.get()));
114		index.release();
115	}
116}
117
118Cursor *
119Table::createCursor(const CSSM_QUERY *inQuery, const DbVersion &inDbVersion) const
120{
121	// if an index matches the query, return a cursor which uses the index
122
123	ConstIndexMap::const_iterator it;
124	DbQueryKey *queryKey;
125
126	for (it = mIndexMap.begin(); it != mIndexMap.end(); it++)
127		if (it->second->matchesQuery(*inQuery, queryKey)) {
128			IndexCursor *cursor = new IndexCursor(queryKey, inDbVersion, *this, it->second);
129			return cursor;
130		}
131
132	// otherwise, return a cursor that iterates over all table records
133
134	return new LinearCursor(inQuery, inDbVersion, *this);
135}
136
137const ReadSection
138Table::getRecordSection(uint32 inRecordNumber) const
139{
140	if (inRecordNumber >= mRecordNumbersCount)
141		CssmError::throwMe(CSSMERR_DL_INVALID_RECORD_UID);
142
143	uint32 aRecordOffset = mTableSection[OffsetRecordNumbers + AtomSize
144										 * inRecordNumber];
145
146	// Check if this RecordNumber has been deleted.
147	if (aRecordOffset & 1 || aRecordOffset == 0)
148		CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND);
149
150	return MetaRecord::readSection(mTableSection, aRecordOffset);
151}
152
153const RecordId
154Table::getRecord(const RecordId &inRecordId,
155				 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes,
156				 CssmData *inoutData,
157				 Allocator &inAllocator) const
158{
159	const ReadSection aRecordSection = getRecordSection(inRecordId.mRecordNumber);
160	const RecordId aRecordId = MetaRecord::unpackRecordId(aRecordSection);
161
162	// Make sure the RecordNumber matches that in the RecordId we just retrived.
163	if (aRecordId.mRecordNumber != inRecordId.mRecordNumber)
164		CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
165
166	if (aRecordId.mCreateVersion != inRecordId.mCreateVersion)
167		CssmError::throwMe(CSSMERR_DL_INVALID_RECORD_UID);
168
169	// XXX Figure out which value to pass for inQueryFlags (5th) argument
170	mMetaRecord.unpackRecord(aRecordSection, inAllocator, inoutAttributes,
171							 inoutData, 0);
172	return aRecordId;
173}
174
175uint32
176Table::popFreeList(uint32 &aFreeListHead) const
177{
178	assert(aFreeListHead | 1);
179	uint32 anOffset = aFreeListHead ^ 1;
180	uint32 aRecordNumber = (anOffset - OffsetRecordNumbers) / AtomSize;
181	aFreeListHead = mTableSection[anOffset];
182	return aRecordNumber;
183}
184
185const ReadSection
186Table::getRecordsSection() const
187{
188	return mTableSection.subsection(mTableSection[OffsetRecords]);
189}
190
191bool
192Table::matchesTableId(Id inTableId) const
193{
194	Id anId = mMetaRecord.dataRecordType();
195	if (inTableId == CSSM_DL_DB_RECORD_ANY) // All non schema tables.
196		return !(CSSM_DB_RECORDTYPE_SCHEMA_START <= anId
197				 && anId < CSSM_DB_RECORDTYPE_SCHEMA_END);
198
199	if (inTableId == CSSM_DL_DB_RECORD_ALL_KEYS) // All key tables.
200		return (anId == CSSM_DL_DB_RECORD_PUBLIC_KEY
201				|| anId == CSSM_DL_DB_RECORD_PRIVATE_KEY
202				|| anId == CSSM_DL_DB_RECORD_SYMMETRIC_KEY);
203
204	return inTableId == anId; // Only if exact match.
205}
206
207
208//
209// ModifiedTable
210//
211ModifiedTable::ModifiedTable(const Table *inTable) :
212	mTable(inTable),
213	mNewMetaRecord(nil),
214	mRecordNumberCount(inTable->recordNumberCount()),
215	mFreeListHead(inTable->freeListHead()),
216	mIsModified(false)
217{
218}
219
220ModifiedTable::ModifiedTable(MetaRecord *inMetaRecord) :
221	mTable(nil),
222	mNewMetaRecord(inMetaRecord),
223	mRecordNumberCount(0),
224	mFreeListHead(0),
225	mIsModified(true)
226{
227}
228
229ModifiedTable::~ModifiedTable()
230{
231	for_each_map_delete(mIndexMap.begin(), mIndexMap.end());
232	for_each_map_delete(mInsertedMap.begin(), mInsertedMap.end());
233
234	delete mNewMetaRecord;
235}
236
237void
238ModifiedTable::deleteRecord(const RecordId &inRecordId)
239{
240	modifyTable();
241
242    uint32 aRecordNumber = inRecordId.mRecordNumber;
243
244	// remove the record from all the indexes
245	MutableIndexMap::iterator it;
246	for (it = mIndexMap.begin(); it != mIndexMap.end(); it++)
247		it->second->removeRecord(aRecordNumber);
248
249	InsertedMap::iterator anIt = mInsertedMap.find(aRecordNumber);
250	if (anIt == mInsertedMap.end())
251	{
252		// If we have no old table than this record can not exist yet.
253		if (!mTable)
254			CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND);
255
256#if RECORDVERSIONCHECK
257		const RecordId aRecordId = MetaRecord::unpackRecordId(mTable->getRecordSection(aRecordNumber));
258		if (aRecordId.mRecordVersion != inRecordId.mRecordVersion)
259			CssmError::throwMe(CSSMERR_DL_RECORD_MODIFIED);
260#endif
261
262		// Schedule the record for deletion
263		if (!mDeletedSet.insert(aRecordNumber).second)
264			CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND); // It was already deleted
265	}
266	else
267	{
268		const RecordId aRecordId = MetaRecord::unpackRecordId(*anIt->second);
269		if (aRecordId.mCreateVersion != inRecordId.mCreateVersion)
270			CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND);
271
272#if RECORDVERSIONCHECK
273		if (aRecordId.mRecordVersion != inRecordId.mRecordVersion)
274			CssmError::throwMe(CSSMERR_DL_RECORD_MODIFIED);
275#endif
276
277		// Remove the inserted (but uncommited) record.  It should already be in mDeletedSet
278		// if it existed previously in mTable.
279        delete anIt->second;
280		mInsertedMap.erase(anIt);
281	}
282}
283
284const RecordId
285ModifiedTable::insertRecord(uint32 inVersionId,
286							const CSSM_DB_RECORD_ATTRIBUTE_DATA *inAttributes,
287							const CssmData *inData)
288{
289	modifyTable();
290
291	auto_ptr<WriteSection> aWriteSection(new WriteSection());
292	getMetaRecord().packRecord(*aWriteSection, inAttributes, inData);
293    uint32 aRecordNumber = nextRecordNumber();
294
295	// add the record to all the indexes; this will throw if the new record
296	// violates a unique index
297	MutableIndexMap::iterator it;
298	for (it = mIndexMap.begin(); it != mIndexMap.end(); it++)
299		it->second->insertRecord(aRecordNumber, *(aWriteSection.get()));
300
301	// schedule the record for insertion
302	RecordId aRecordId(aRecordNumber, inVersionId);
303	MetaRecord::packRecordId(aRecordId, *aWriteSection);
304    mInsertedMap.insert(InsertedMap::value_type(aRecordNumber, aWriteSection.get()));
305
306    aWriteSection.release();
307
308    return aRecordId;
309}
310
311const RecordId
312ModifiedTable::updateRecord(const RecordId &inRecordId,
313							const CSSM_DB_RECORD_ATTRIBUTE_DATA *inAttributes,
314							const CssmData *inData,
315							CSSM_DB_MODIFY_MODE inModifyMode)
316{
317	modifyTable();
318
319    uint32 aRecordNumber = inRecordId.mRecordNumber;
320	InsertedMap::iterator anIt = mInsertedMap.find(aRecordNumber);
321
322	// aReUpdate is true iff we are updating an already updated record.
323	bool aReUpdate = anIt != mInsertedMap.end();
324
325	// If we are not re-updating and there is no old table than this record does not exist yet.
326	if (!aReUpdate && !mTable)
327		CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND);
328
329	const ReadSection &anOldDbRecord = aReUpdate ? *anIt->second : mTable->getRecordSection(aRecordNumber);
330	const RecordId aRecordId = MetaRecord::unpackRecordId(anOldDbRecord);
331
332	// Did someone else delete the record we are trying to update.
333	if (aRecordId.mCreateVersion != inRecordId.mCreateVersion)
334		CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND);
335
336#if RECORDVERSIONCHECK
337	// Is the record we that our update is based on current?
338	if (aRecordId.mRecordVersion != inRecordId.mRecordVersion)
339		CssmError::throwMe(CSSMERR_DL_STALE_UNIQUE_RECORD);
340#endif
341
342	// Update the actual packed record.
343    auto_ptr<WriteSection> aDbRecord(new WriteSection());
344	getMetaRecord().updateRecord(anOldDbRecord, *aDbRecord,
345		CssmDbRecordAttributeData::overlay(inAttributes), inData, inModifyMode);
346
347
348	// Bump the RecordVersion of this record.
349	RecordId aNewRecordId(aRecordNumber, inRecordId.mCreateVersion, inRecordId.mRecordVersion + 1);
350	// Store the RecordVersion in the packed aDbRecord.
351	MetaRecord::packRecordId(aNewRecordId, *aDbRecord);
352
353	if (!aReUpdate && !mDeletedSet.insert(aRecordNumber).second)
354		CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND); // Record was already in mDeletedSet
355
356	// remove the original record from all the indexes
357	MutableIndexMap::iterator it;
358	for (it = mIndexMap.begin(); it != mIndexMap.end(); it++)
359		it->second->removeRecord(aRecordNumber);
360
361	try
362	{
363		// Add the updated record to all the indexes; this will throw if the new record
364		// violates a unique index
365		for (it = mIndexMap.begin(); it != mIndexMap.end(); it++)
366			it->second->insertRecord(aRecordNumber, *(aDbRecord.get()));
367
368		if (aReUpdate)
369		{
370			// Get rid of anOldDbRecord from the inserted map and replace it
371			// with aDbRecord.
372			delete anIt->second;
373			anIt->second = aDbRecord.get();
374		}
375		else
376		{
377			// First time though so let's just put the new value in the map.
378			mInsertedMap.insert(InsertedMap::value_type(aRecordNumber, aDbRecord.get()));
379		}
380		aDbRecord.release();
381	}
382	catch(...)
383	{
384		// We only remove aRecordNumber from mDeletedSet if we added it above.
385		if (!aReUpdate)
386			mDeletedSet.erase(aRecordNumber);
387
388		// The 2 operations below are an attempt to preserve the indices when
389		// an insert fails.
390
391		// Remove the updated record from all the indexes
392		MutableIndexMap::iterator it;
393		for (it = mIndexMap.begin(); it != mIndexMap.end(); it++)
394			it->second->removeRecord(aRecordNumber);
395
396		// Add the original record back to all the indexes
397		for (it = mIndexMap.begin(); it != mIndexMap.end(); it++)
398			it->second->insertRecord(aRecordNumber, anOldDbRecord);
399
400		throw;
401	}
402
403	return aNewRecordId;
404}
405
406const RecordId
407ModifiedTable::getRecord(const RecordId &inRecordId,
408						 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes,
409						 CssmData *inoutData,
410						 Allocator &inAllocator) const
411{
412	if (mIsModified)
413	{
414		uint32 aRecordNumber = inRecordId.mRecordNumber;
415		InsertedMap::const_iterator anIt = mInsertedMap.find(aRecordNumber);
416		if (anIt != mInsertedMap.end())
417		{
418			// We found the record in mInsertedMap so we use the inserted
419			// record.
420			const ReadSection &aRecordSection = *(anIt->second);
421			const RecordId aRecordId = MetaRecord::unpackRecordId(aRecordSection);
422
423			// Make sure the RecordNumber matches that in the RecordId we just retrived.
424			if (aRecordId.mRecordNumber != aRecordNumber)
425				CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
426
427			if (aRecordId.mCreateVersion != inRecordId.mCreateVersion)
428				CssmError::throwMe(CSSMERR_DL_INVALID_RECORD_UID);
429
430			// XXX Figure out which value to pass for inQueryFlags (5th) argument
431			getMetaRecord().unpackRecord(aRecordSection, inAllocator,
432				inoutAttributes, inoutData, 0);
433
434			return aRecordId;
435		}
436		else if (mDeletedSet.find(aRecordNumber) != mDeletedSet.end())
437		{
438			 // If aRecordNumber was not in mInsertedMap but it was in
439			 // mDeletedSet then it was deleted but not yet commited.
440			CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND);
441		}
442	}
443
444	if (!mTable)
445		CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND);
446
447	// Either this table wasn't modified yet or we didn't find aRecordNumber in
448	// mInsertedMap nor mDeletedSet so just ask mTable for it.
449	return mTable->getRecord(inRecordId, inoutAttributes, inoutData,
450		inAllocator);
451}
452
453uint32
454ModifiedTable::nextRecordNumber()
455{
456	// If we still have unused free records in mTable get the next one.
457	if (mFreeListHead)
458		return mTable->popFreeList(mFreeListHead);
459
460	// Bump up the mRecordNumberCount so we don't reuse the same one.
461	return mRecordNumberCount++;
462}
463
464uint32
465ModifiedTable::recordNumberCount() const
466{
467	uint32 anOldMax = !mTable ? 0 : mTable->recordNumberCount() - 1;
468	uint32 anInsertedMax = mInsertedMap.empty() ? 0 : mInsertedMap.rbegin()->first;
469
470	DeletedSet::reverse_iterator anIt = mDeletedSet.rbegin();
471	DeletedSet::reverse_iterator anEnd = mDeletedSet.rend();
472	for (; anIt != anEnd; anIt++)
473	{
474		if (*anIt != anOldMax || anOldMax <= anInsertedMax)
475			break;
476		anOldMax--;
477 	}
478
479	return max(anOldMax,anInsertedMax) + 1;
480}
481
482const MetaRecord &
483ModifiedTable::getMetaRecord() const
484{
485	return mNewMetaRecord ? *mNewMetaRecord : mTable->getMetaRecord();
486}
487
488// prepare to modify the table
489
490void
491ModifiedTable::modifyTable()
492{
493	if (!mIsModified) {
494		createMutableIndexes();
495		mIsModified = true;
496	}
497}
498
499// create mutable indexes from the read-only indexes in the underlying table
500
501void
502ModifiedTable::createMutableIndexes()
503{
504	if (mTable == NULL)
505		return;
506
507	Table::ConstIndexMap::const_iterator it;
508	for (it = mTable->mIndexMap.begin(); it != mTable->mIndexMap.end(); it++) {
509		auto_ptr<DbMutableIndex> mutableIndex(new DbMutableIndex(*it->second));
510		mIndexMap.insert(MutableIndexMap::value_type(it->first, mutableIndex.get()));
511		mutableIndex.release();
512	}
513}
514
515// find, and create if needed, an index with the given id
516
517DbMutableIndex &
518ModifiedTable::findIndex(uint32 indexId, const MetaRecord &metaRecord, bool isUniqueIndex)
519{
520	MutableIndexMap::iterator it = mIndexMap.find(indexId);
521
522	if (it == mIndexMap.end()) {
523		// create the new index
524		auto_ptr<DbMutableIndex> index(new DbMutableIndex(metaRecord, indexId, isUniqueIndex));
525		it = mIndexMap.insert(MutableIndexMap::value_type(indexId, index.get())).first;
526		index.release();
527	}
528
529	return *it->second;
530}
531
532uint32
533ModifiedTable::writeIndexSection(WriteSection &tableSection, uint32 offset)
534{
535	MutableIndexMap::iterator it;
536
537	tableSection.put(Table::OffsetIndexesOffset, offset);
538
539	// leave room for the size, to be written later
540	uint32 indexSectionOffset = offset;
541	offset += AtomSize;
542
543	offset = tableSection.put(offset, (uint32)mIndexMap.size());
544
545	// leave room for the array of offsets to the indexes
546	uint32 indexOffsetOffset = offset;
547	offset += mIndexMap.size() * AtomSize;
548
549	// write the indexes
550	for (it = mIndexMap.begin(); it != mIndexMap.end(); it++) {
551		indexOffsetOffset = tableSection.put(indexOffsetOffset, offset);
552		offset = it->second->writeIndex(tableSection, offset);
553	}
554
555	// write the total index section size
556	tableSection.put(indexSectionOffset, offset - indexSectionOffset);
557
558	return offset;
559}
560
561uint32
562ModifiedTable::writeTable(AtomicTempFile &inAtomicTempFile, uint32 inSectionOffset)
563{
564	if (mTable && !mIsModified) {
565		// the table has not been modified, so we can just dump the old table
566		// section into the new database
567
568		const ReadSection &tableSection = mTable->getTableSection();
569		uint32 tableSize = tableSection.at(Table::OffsetSize);
570
571		inAtomicTempFile.write(AtomicFile::FromStart, inSectionOffset,
572			tableSection.range(Range(0, tableSize)), tableSize);
573
574		return inSectionOffset + tableSize;
575	}
576
577	// We should have an old mTable or a mNewMetaRecord but not both.
578	assert(mTable != nil ^ mNewMetaRecord != nil);
579	const MetaRecord &aNewMetaRecord = getMetaRecord();
580
581	uint32 aRecordsCount = 0;
582	uint32 aRecordNumbersCount = recordNumberCount();
583	uint32 aRecordsOffset = Table::OffsetRecordNumbers + AtomSize * aRecordNumbersCount;
584	WriteSection aTableSection(Allocator::standard(), aRecordsOffset);
585	aTableSection.size(aRecordsOffset);
586	aTableSection.put(Table::OffsetId, aNewMetaRecord.dataRecordType());
587	aTableSection.put(Table::OffsetRecords, aRecordsOffset);
588	aTableSection.put(Table::OffsetRecordNumbersCount, aRecordNumbersCount);
589
590	uint32 anOffset = inSectionOffset + aRecordsOffset;
591
592	if (mTable)
593	{
594		// XXX Handle schema changes in the future.
595		assert(mNewMetaRecord == nil);
596
597		// We have a modified old table so copy all non deleted records
598		// The code below is rather elaborate, but this is because it attempts
599		// to copy large ranges of non deleted records with single calls
600		// to AtomicFile::write()
601		uint32 anOldRecordsCount = mTable->getRecordsCount();
602		ReadSection aRecordsSection = mTable->getRecordsSection();
603		uint32 aReadOffset = 0;					// Offset of current record
604		uint32 aWriteOffset = aRecordsOffset;	// Offset for current write record
605		uint32 aBlockStart = aReadOffset;		// Starting point for read
606		uint32 aBlockSize = 0;					// Size of block to read
607		for (uint32 aRecord = 0; aRecord < anOldRecordsCount; aRecord++)
608		{
609			ReadSection aRecordSection = MetaRecord::readSection(aRecordsSection, aReadOffset);
610			uint32 aRecordNumber = MetaRecord::unpackRecordNumber(aRecordSection);
611			uint32 aRecordSize = aRecordSection.size();
612			aReadOffset += aRecordSize;
613			if (mDeletedSet.find(aRecordNumber) == mDeletedSet.end())
614			{
615				// This record has not been deleted.  Register the offset
616				// at which it will be in the new file in aTableSection.
617				aTableSection.put(Table::OffsetRecordNumbers
618								  + AtomSize * aRecordNumber,
619								  aWriteOffset);
620				aWriteOffset += aRecordSize;
621				aBlockSize += aRecordSize;
622				aRecordsCount++;
623				// XXX update all indexes being created.
624			}
625			else
626			{
627				// The current record has been deleted.  Copy all records up
628				// to but not including the current one to the new file.
629				if (aBlockSize > 0)
630				{
631					inAtomicTempFile.write(AtomicFile::FromStart, anOffset,
632									   aRecordsSection.range(Range(aBlockStart,
633																   aBlockSize)),
634									   aBlockSize);
635					anOffset += aBlockSize;
636				}
637
638				// Set the start of the next block to the start of the next
639				// record, and the size of the block to 0.
640				aBlockStart = aReadOffset;
641				aBlockSize = 0;
642			} // if (mDeletedSet..)
643		} // for (aRecord...)
644
645		// Copy all records that have not yet been copied to the new file.
646		if (aBlockSize > 0)
647		{
648			inAtomicTempFile.write(AtomicFile::FromStart, anOffset,
649							   aRecordsSection.range(Range(aBlockStart,
650														   aBlockSize)),
651							   aBlockSize);
652			anOffset += aBlockSize;
653		}
654	} // if (mTable)
655
656	// Now add all inserted records to the table.
657	InsertedMap::const_iterator anIt = mInsertedMap.begin();
658	InsertedMap::const_iterator anEnd = mInsertedMap.end();
659	// Iterate over all inserted objects.
660	for (; anIt != anEnd; anIt++)
661	{
662		// Write out each inserted/modified record
663		const WriteSection &aRecord = *anIt->second;
664		uint32 aRecordNumber = anIt->first;
665		// Put offset relative to start of this table in recordNumber array.
666		aTableSection.put(Table::OffsetRecordNumbers + AtomSize * aRecordNumber,
667						  anOffset - inSectionOffset);
668		inAtomicTempFile.write(AtomicFile::FromStart, anOffset,
669						   aRecord.address(), aRecord.size());
670		anOffset += aRecord.size();
671		aRecordsCount++;
672		// XXX update all indexes being created.
673	}
674
675	// Reconstruct the freelist (this is O(N) where N is the number of recordNumbers)
676	// We could implement it faster by using the old freelist and skipping the records
677	// that have been inserted.  However building the freelist for the newly used
678	// recordNumbers (not in mTable) would look like the code below anyway (starting
679	// from mTable->recordNumberCount()).
680	// The first part of this would be O(M Log(N))  (where M is the old number of
681	// free records, and N is the number of newly inserted records)
682	// The second part would be O(N) where N is the currently max RecordNumber
683	// in use - the old max RecordNumber in use.
684	uint32 aFreeListHead = 0;	// Link to previous free record
685	for (uint32 aRecordNumber = 0; aRecordNumber < aRecordNumbersCount; aRecordNumber++)
686	{
687		// Make the freelist a list of all records with 0 offset (non existing).
688		if (!aTableSection.at(Table::OffsetRecordNumbers + AtomSize * aRecordNumber))
689		{
690			aTableSection.put(Table::OffsetRecordNumbers
691								+ AtomSize * aRecordNumber,
692								aFreeListHead);
693			// Make aFreeListHead point to the previous free recordNumber slot in the table.
694			aFreeListHead = (Table::OffsetRecordNumbers + AtomSize * aRecordNumber) | 1;
695		}
696	}
697	aTableSection.put(Table::OffsetFreeListHead, aFreeListHead);
698
699	anOffset -= inSectionOffset;
700
701	// Write out indexes, which are part of the table section
702
703	{
704		uint32 indexOffset = anOffset;
705		anOffset = writeIndexSection(aTableSection, anOffset);
706		inAtomicTempFile.write(AtomicFile::FromStart, inSectionOffset + indexOffset,
707			aTableSection.address() + indexOffset, anOffset - indexOffset);
708	}
709
710	// Set the section size and recordCount.
711	aTableSection.put(Table::OffsetSize, anOffset);
712	aTableSection.put(Table::OffsetRecordsCount, aRecordsCount);
713
714	// Write out aTableSection header.
715	inAtomicTempFile.write(AtomicFile::FromStart, inSectionOffset,
716					   aTableSection.address(), aTableSection.size());
717
718    return anOffset + inSectionOffset;
719}
720
721
722#pragma clang diagnostic push
723#pragma clang diagnostic ignored "-Wunused-const-variable"
724
725//
726// Metadata
727//
728
729// Attribute definitions
730
731static const CSSM_DB_ATTRIBUTE_INFO RelationID =
732{
733    CSSM_DB_ATTRIBUTE_NAME_AS_STRING,
734    {(char*) "RelationID"},
735    CSSM_DB_ATTRIBUTE_FORMAT_UINT32
736};
737static const CSSM_DB_ATTRIBUTE_INFO RelationName =
738{
739    CSSM_DB_ATTRIBUTE_NAME_AS_STRING,
740    {(char*) "RelationName"},
741    CSSM_DB_ATTRIBUTE_FORMAT_STRING
742};
743static const CSSM_DB_ATTRIBUTE_INFO AttributeID =
744{
745    CSSM_DB_ATTRIBUTE_NAME_AS_STRING,
746    {(char*) "AttributeID"},
747    CSSM_DB_ATTRIBUTE_FORMAT_UINT32
748};
749static const CSSM_DB_ATTRIBUTE_INFO AttributeNameFormat =
750{
751    CSSM_DB_ATTRIBUTE_NAME_AS_STRING,
752    {(char*) "AttributeNameFormat"},
753    CSSM_DB_ATTRIBUTE_FORMAT_UINT32
754};
755static const CSSM_DB_ATTRIBUTE_INFO AttributeName =
756{
757    CSSM_DB_ATTRIBUTE_NAME_AS_STRING,
758    {(char*) "AttributeName"},
759    CSSM_DB_ATTRIBUTE_FORMAT_STRING
760};
761static const CSSM_DB_ATTRIBUTE_INFO AttributeNameID =
762{
763    CSSM_DB_ATTRIBUTE_NAME_AS_STRING,
764    {(char*) "AttributeNameID"},
765    CSSM_DB_ATTRIBUTE_FORMAT_BLOB
766};
767static const CSSM_DB_ATTRIBUTE_INFO AttributeFormat =
768{
769    CSSM_DB_ATTRIBUTE_NAME_AS_STRING,
770    {(char*) "AttributeFormat"},
771    CSSM_DB_ATTRIBUTE_FORMAT_UINT32
772};
773static const CSSM_DB_ATTRIBUTE_INFO IndexID =
774{
775    CSSM_DB_ATTRIBUTE_NAME_AS_STRING,
776    {(char*) "IndexID"},
777    CSSM_DB_ATTRIBUTE_FORMAT_UINT32
778};
779static const CSSM_DB_ATTRIBUTE_INFO IndexType =
780{
781    CSSM_DB_ATTRIBUTE_NAME_AS_STRING,
782    {(char*) "IndexType"},
783    CSSM_DB_ATTRIBUTE_FORMAT_UINT32
784};
785static const CSSM_DB_ATTRIBUTE_INFO IndexedDataLocation =
786{
787    CSSM_DB_ATTRIBUTE_NAME_AS_STRING,
788    {(char*) "IndexedDataLocation"},
789    CSSM_DB_ATTRIBUTE_FORMAT_UINT32
790};
791static const CSSM_DB_ATTRIBUTE_INFO ModuleID =
792{
793    CSSM_DB_ATTRIBUTE_NAME_AS_STRING,
794    {(char*) "ModuleID"},
795    CSSM_DB_ATTRIBUTE_FORMAT_BLOB
796};
797static const CSSM_DB_ATTRIBUTE_INFO AddinVersion =
798{
799    CSSM_DB_ATTRIBUTE_NAME_AS_STRING,
800    {(char*) "AddinVersion"},
801    CSSM_DB_ATTRIBUTE_FORMAT_STRING
802};
803static const CSSM_DB_ATTRIBUTE_INFO SSID =
804{
805    CSSM_DB_ATTRIBUTE_NAME_AS_STRING,
806    {(char*) "SSID"},
807    CSSM_DB_ATTRIBUTE_FORMAT_UINT32
808};
809static const CSSM_DB_ATTRIBUTE_INFO SubserviceType =
810{
811    CSSM_DB_ATTRIBUTE_NAME_AS_STRING,
812    {(char*) "SubserviceType"},
813    CSSM_DB_ATTRIBUTE_FORMAT_UINT32
814};
815
816#define ATTRIBUTE(type, name) \
817	{ CSSM_DB_ATTRIBUTE_NAME_AS_STRING, { (char*) #name }, CSSM_DB_ATTRIBUTE_FORMAT_ ## type }
818
819static const CSSM_DB_ATTRIBUTE_INFO AttrSchemaRelations[] =
820{
821	//RelationID, RelationName
822	ATTRIBUTE(UINT32, RelationID),
823	ATTRIBUTE(STRING, RelationName)
824};
825
826static const CSSM_DB_ATTRIBUTE_INFO AttrSchemaAttributes[] =
827{
828	//RelationID, AttributeID,
829    //AttributeNameFormat, AttributeName, AttributeNameID,
830    //AttributeFormat
831	ATTRIBUTE(UINT32, RelationID),
832	ATTRIBUTE(UINT32, AttributeID),
833	ATTRIBUTE(UINT32, AttributeNameFormat),
834	ATTRIBUTE(STRING, AttributeName),
835	ATTRIBUTE(BLOB, AttributeNameID),
836	ATTRIBUTE(UINT32, AttributeFormat)
837};
838
839static const CSSM_DB_ATTRIBUTE_INFO AttrSchemaIndexes[] =
840{
841	ATTRIBUTE(UINT32, RelationID),
842	ATTRIBUTE(UINT32, IndexID),
843	ATTRIBUTE(UINT32, AttributeID),
844	ATTRIBUTE(UINT32, IndexType),
845	ATTRIBUTE(UINT32, IndexedDataLocation)
846    //RelationID, IndexID, AttributeID,
847    //IndexType, IndexedDataLocation
848};
849
850static const CSSM_DB_ATTRIBUTE_INFO AttrSchemaParsingModule[] =
851{
852	ATTRIBUTE(UINT32, RelationID),
853	ATTRIBUTE(UINT32, AttributeID),
854	ATTRIBUTE(BLOB, ModuleID),
855	ATTRIBUTE(STRING, AddinVersion),
856	ATTRIBUTE(UINT32, SSID),
857	ATTRIBUTE(UINT32, SubserviceType)
858    //RelationID, AttributeID,
859    //ModuleID, AddinVersion, SSID, SubserviceType
860};
861
862#undef ATTRIBUTE
863#pragma clang diagnostic pop
864
865//
866// DbVersion
867//
868DbVersion::DbVersion(const AppleDatabase &db, const RefPointer <AtomicBufferedFile> &inAtomicBufferedFile) :
869	mDatabase(reinterpret_cast<const uint8 *>(NULL), 0),
870	mDb(db),
871	mBufferedFile(inAtomicBufferedFile)
872{
873	off_t aLength = mBufferedFile->length();
874	off_t bytesRead = 0;
875	const uint8 *ptr = mBufferedFile->read(0, aLength, bytesRead);
876	mBufferedFile->close();
877	mDatabase = ReadSection(ptr, (size_t)bytesRead);
878	open();
879}
880
881DbVersion::~DbVersion()
882{
883	try
884	{
885		for_each_map_delete(mTableMap.begin(), mTableMap.end());
886	}
887	catch(...) {}
888}
889
890void
891DbVersion::open()
892{
893	try
894	{
895		// This is the oposite of DbModifier::commit()
896		mVersionId = mDatabase[mDatabase.size() - AtomSize];
897
898		const ReadSection aHeaderSection = mDatabase.subsection(HeaderOffset,
899																HeaderSize);
900		if (aHeaderSection.at(OffsetMagic) != HeaderMagic)
901			CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
902
903		// We currently only support one version.  If we support additional
904		// file format versions in the future fix this.
905		uint32 aVersion = aHeaderSection.at(OffsetVersion);
906		if (aVersion != HeaderVersion)
907			CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
908
909		//const ReadSection anAuthSection =
910		//	mDatabase.subsection(HeaderOffset + aHeaderSection.at(OffsetAuthOffset));
911		// XXX Do something with anAuthSection.
912
913		uint32 aSchemaOffset = aHeaderSection.at(OffsetSchemaOffset);
914		const ReadSection aSchemaSection =
915			mDatabase.subsection(HeaderOffset + aSchemaOffset);
916
917		uint32 aSchemaSize = aSchemaSection[OffsetSchemaSize];
918		// Make sure that the given range exists.
919		aSchemaSection.subsection(0, aSchemaSize);
920		uint32 aTableCount = aSchemaSection[OffsetTablesCount];
921
922		// Assert that the size of this section is big enough.
923		if (aSchemaSize < OffsetTables + AtomSize * aTableCount)
924			CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
925
926		for (uint32 aTableNumber = 0; aTableNumber < aTableCount;
927			 aTableNumber++)
928		{
929			uint32 aTableOffset = aSchemaSection.at(OffsetTables + AtomSize
930													* aTableNumber);
931			// XXX Set the size boundary on aTableSection.
932			const ReadSection aTableSection =
933				aSchemaSection.subsection(aTableOffset);
934			auto_ptr<Table> aTable(new Table(aTableSection));
935			Table::Id aTableId = aTable->getMetaRecord().dataRecordType();
936			mTableMap.insert(TableMap::value_type(aTableId, aTable.get()));
937			aTable.release();
938		}
939
940		// Fill in the schema for the meta tables.
941
942		findTable(mDb.schemaRelations.DataRecordType).getMetaRecord().
943			setRecordAttributeInfo(mDb.schemaRelations);
944		findTable(mDb.schemaIndexes.DataRecordType).getMetaRecord().
945			setRecordAttributeInfo(mDb.schemaIndexes);
946		findTable(mDb.schemaParsingModule.DataRecordType).getMetaRecord().
947			setRecordAttributeInfo(mDb.schemaParsingModule);
948
949		// OK, we have created all the tables in the tableMap.  Now
950		// lets read the schema and proccess it accordingly.
951		// Iterate over all schema records.
952		Table &aTable = findTable(mDb.schemaAttributes.DataRecordType);
953		aTable.getMetaRecord().setRecordAttributeInfo(mDb.schemaAttributes);
954		uint32 aRecordsCount = aTable.getRecordsCount();
955		ReadSection aRecordsSection = aTable.getRecordsSection();
956		uint32 aReadOffset = 0;
957		const MetaRecord &aMetaRecord = aTable.getMetaRecord();
958
959		CSSM_DB_ATTRIBUTE_DATA aRelationIDData =
960		{
961			RelationID,
962			0,
963			NULL
964		};
965		CSSM_DB_ATTRIBUTE_DATA aAttributeIDData =
966		{
967			AttributeID,
968			0,
969			NULL
970		};
971		CSSM_DB_ATTRIBUTE_DATA aAttributeNameFormatData =
972		{
973			AttributeNameFormat,
974			0,
975			NULL
976		};
977		CSSM_DB_ATTRIBUTE_DATA aAttributeNameData =
978		{
979			AttributeName,
980			0,
981			NULL
982		};
983		CSSM_DB_ATTRIBUTE_DATA aAttributeNameIDData =
984		{
985			AttributeNameID,
986			0,
987			NULL
988		};
989		CSSM_DB_ATTRIBUTE_DATA aAttributeFormatData =
990		{
991			AttributeFormat,
992			0,
993			NULL
994		};
995		CSSM_DB_ATTRIBUTE_DATA aRecordAttributes[] =
996		{
997			aRelationIDData,
998			aAttributeIDData,
999			aAttributeNameFormatData,
1000			aAttributeNameData,
1001			aAttributeNameIDData,
1002			aAttributeFormatData
1003		};
1004		CSSM_DB_RECORD_ATTRIBUTE_DATA aRecordAttributeData =
1005		{
1006			aMetaRecord.dataRecordType(),
1007			0,
1008			sizeof(aRecordAttributes) / sizeof(CSSM_DB_ATTRIBUTE_DATA),
1009			aRecordAttributes
1010		};
1011		CssmDbRecordAttributeData &aRecordData = CssmDbRecordAttributeData::overlay(aRecordAttributeData);
1012
1013		TrackingAllocator recordAllocator(Allocator::standard());
1014		for (uint32 aRecord = 0; aRecord != aRecordsCount; aRecord++)
1015		{
1016			ReadSection aRecordSection = MetaRecord::readSection(aRecordsSection, aReadOffset);
1017			uint32 aRecordSize = aRecordSection.size();
1018			aReadOffset += aRecordSize;
1019			aMetaRecord.unpackRecord(aRecordSection, recordAllocator,
1020										&aRecordAttributeData, NULL, 0);
1021			// Create the attribute coresponding to this entry
1022			if (aRecordData[0].size() != 1 || aRecordData[0].format() != CSSM_DB_ATTRIBUTE_FORMAT_UINT32)
1023				CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
1024			uint32 aRelationId = aRecordData[0];
1025
1026			// Skip the schema relations for the meta tables themselves.
1027			// FIXME: this hard-wires the meta-table relation IDs to be
1028			// within {CSSM_DB_RECORDTYPE_SCHEMA_START...
1029			// CSSM_DB_RECORDTYPE_SCHEMA_END} (which is {0..4}).
1030			// Bogus - the MDS schema relation IDs start at
1031			// CSSM_DB_RELATIONID_MDS_START which is 0x40000000.
1032			// Ref. Radar 2817921.
1033			if (CSSM_DB_RECORDTYPE_SCHEMA_START <= aRelationId && aRelationId < CSSM_DB_RECORDTYPE_SCHEMA_END)
1034				continue;
1035
1036			// Get the MetaRecord corresponding to the specified RelationId
1037			MetaRecord &aMetaRecord = findTable(aRelationId).getMetaRecord();
1038
1039			if (aRecordData[1].size() != 1
1040				|| aRecordData[1].format() != CSSM_DB_ATTRIBUTE_FORMAT_UINT32
1041				|| aRecordData[2].size() != 1
1042				|| aRecordData[2].format() != CSSM_DB_ATTRIBUTE_FORMAT_UINT32
1043				|| aRecordData[5].size() != 1
1044				|| aRecordData[5].format() != CSSM_DB_ATTRIBUTE_FORMAT_UINT32)
1045				CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
1046
1047			uint32 anAttributeId = aRecordData[1];
1048			uint32 anAttributeNameFormat = aRecordData[2];
1049			uint32 anAttributeFormat = aRecordData[5];
1050			auto_ptr<string> aName;
1051			const CssmData *aNameID = NULL;
1052
1053			if (aRecordData[3].size() == 1)
1054			{
1055				if (aRecordData[3].format() != CSSM_DB_ATTRIBUTE_FORMAT_STRING)
1056					CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
1057
1058				auto_ptr<string> aName2(new string(static_cast<string>(aRecordData[3])));
1059				aName = aName2;
1060			}
1061
1062			if (aRecordData[4].size() == 1)
1063			{
1064				if (aRecordData[4].format() != CSSM_DB_ATTRIBUTE_FORMAT_BLOB)
1065					CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
1066
1067									// @@@ Invoking conversion operator to CssmData & on aRecordData[4]
1068									// And taking address of result.
1069				aNameID = &static_cast<const CssmData &>(aRecordData[4]);
1070			}
1071
1072			// Make sure that the attribute specified by anAttributeNameFormat is present.
1073			switch (anAttributeNameFormat)
1074			{
1075			case CSSM_DB_ATTRIBUTE_NAME_AS_STRING:
1076				if (aRecordData[3].size() != 1)
1077					CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
1078				break;
1079			case CSSM_DB_ATTRIBUTE_NAME_AS_OID:
1080				if (aRecordData[4].size() != 1)
1081					CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
1082				break;
1083			case CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER:
1084				break;
1085			default:
1086				CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
1087			}
1088
1089			// Create the attribute
1090			aMetaRecord.createAttribute(aName.get(), aNameID, anAttributeId, anAttributeFormat);
1091        }
1092
1093		// initialize the indexes associated with each table
1094		{
1095			TableMap::iterator it;
1096			for (it = mTableMap.begin(); it != mTableMap.end(); it++)
1097				it->second->readIndexSection();
1098		}
1099	}
1100	catch(...)
1101	{
1102		for_each_map_delete(mTableMap.begin(), mTableMap.end());
1103		mTableMap.clear();
1104		throw;
1105	}
1106}
1107
1108const RecordId
1109DbVersion::getRecord(Table::Id inTableId, const RecordId &inRecordId,
1110							CSSM_DB_RECORD_ATTRIBUTE_DATA *inoutAttributes,
1111							CssmData *inoutData,
1112							Allocator &inAllocator) const
1113{
1114	return findTable(inTableId).getRecord(inRecordId, inoutAttributes,
1115										  inoutData, inAllocator);
1116}
1117
1118Cursor *
1119DbVersion::createCursor(const CSSM_QUERY *inQuery) const
1120{
1121	// XXX We should add support for these special query types
1122	// By Creating a Cursor that iterates over multiple tables
1123	if (!inQuery || inQuery->RecordType == CSSM_DL_DB_RECORD_ANY
1124		|| inQuery->RecordType == CSSM_DL_DB_RECORD_ALL_KEYS)
1125	{
1126		return new MultiCursor(inQuery, *this);
1127	}
1128
1129	return findTable(inQuery->RecordType).createCursor(inQuery, *this);
1130}
1131
1132bool DbVersion::hasTable(Table::Id inTableId) const
1133{
1134    TableMap::const_iterator it = mTableMap.find(inTableId);
1135	return it != mTableMap.end();
1136}
1137
1138const Table &
1139DbVersion::findTable(Table::Id inTableId) const
1140{
1141    TableMap::const_iterator it = mTableMap.find(inTableId);
1142    if (it == mTableMap.end())
1143		CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE);
1144	return *it->second;
1145}
1146
1147Table &
1148DbVersion::findTable(Table::Id inTableId)
1149{
1150    TableMap::iterator it = mTableMap.find(inTableId);
1151    if (it == mTableMap.end())
1152		CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
1153	return *it->second;
1154}
1155
1156//
1157// Cursor implemetation
1158//
1159Cursor::Cursor()
1160{
1161}
1162
1163Cursor::Cursor(const DbVersion &inDbVersion) : mDbVersion(&inDbVersion)
1164{
1165}
1166
1167Cursor::~Cursor()
1168{
1169}
1170
1171bool
1172Cursor::next(Table::Id &outTableId,
1173			 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR outAttributes,
1174			 CssmData *outData,
1175			 Allocator &inAllocator,
1176			 RecordId &recordId)
1177{
1178	return false;
1179}
1180
1181//
1182// LinearCursor implemetation
1183//
1184LinearCursor::LinearCursor(const CSSM_QUERY *inQuery, const DbVersion &inDbVersion,
1185						   const Table &inTable) :
1186    Cursor(inDbVersion),
1187    mRecordsCount(inTable.getRecordsCount()),
1188    mRecord(0),
1189    mRecordsSection(inTable.getRecordsSection()),
1190    mReadOffset(0),
1191    mMetaRecord(inTable.getMetaRecord())
1192{
1193	if (inQuery)
1194	{
1195	    mConjunctive = inQuery->Conjunctive;
1196	    mQueryFlags = inQuery->QueryFlags;
1197	    // XXX Do something with inQuery->QueryLimits?
1198	    uint32 aPredicatesCount = inQuery->NumSelectionPredicates;
1199	    mPredicates.resize(aPredicatesCount);
1200		try
1201		{
1202			for (uint32 anIndex = 0; anIndex < aPredicatesCount; anIndex++)
1203			{
1204				CSSM_SELECTION_PREDICATE &aPredicate = inQuery->SelectionPredicate[anIndex];
1205				mPredicates[anIndex] = new SelectionPredicate(mMetaRecord, aPredicate);
1206			}
1207		}
1208		catch(...)
1209		{
1210			for_each_delete(mPredicates.begin(), mPredicates.end());
1211			throw;
1212		}
1213	}
1214}
1215
1216LinearCursor::~LinearCursor()
1217{
1218	for_each_delete(mPredicates.begin(), mPredicates.end());
1219}
1220
1221bool
1222LinearCursor::next(Table::Id &outTableId,
1223				   CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes,
1224				   CssmData *inoutData, Allocator &inAllocator, RecordId &recordId)
1225{
1226	while (mRecord++ < mRecordsCount)
1227	{
1228		ReadSection aRecordSection = MetaRecord::readSection(mRecordsSection, mReadOffset);
1229		uint32 aRecordSize = aRecordSection.size();
1230		mReadOffset += aRecordSize;
1231
1232        PredicateVector::const_iterator anIt = mPredicates.begin();
1233        PredicateVector::const_iterator anEnd = mPredicates.end();
1234		bool aMatch;
1235		if (anIt == anEnd)
1236		{
1237			// If there are no predicates we have a match.
1238			aMatch = true;
1239		}
1240		else if (mConjunctive == CSSM_DB_OR)
1241		{
1242			// If mConjunctive is OR, the first predicate that returns
1243			// true indicates a match. Dropthough means no match
1244			aMatch = false;
1245			for (; anIt != anEnd; anIt++)
1246			{
1247				if ((*anIt)->evaluate(aRecordSection))
1248				{
1249					aMatch = true;
1250                    break;
1251				}
1252			}
1253		}
1254		else if (mConjunctive == CSSM_DB_AND || mConjunctive == CSSM_DB_NONE)
1255		{
1256			// If mConjunctive is AND (or NONE), the first predicate that returns
1257			// false indicates a mismatch. Dropthough means a match
1258			aMatch = true;
1259			for (; anIt != anEnd; anIt++)
1260			{
1261				if (!(*anIt)->evaluate(aRecordSection))
1262				{
1263					aMatch = false;
1264                    break;
1265				}
1266			}
1267		}
1268		else
1269		{
1270			// XXX Should be CSSMERR_DL_INVALID_QUERY (or CSSMERR_DL_INVALID_CONJUNTIVE).
1271			CssmError::throwMe(CSSMERR_DL_UNSUPPORTED_QUERY);
1272		}
1273
1274        if (aMatch)
1275        {
1276            // Get the actual record.
1277            mMetaRecord.unpackRecord(aRecordSection, inAllocator,
1278									 inoutAttributes, inoutData,
1279									 mQueryFlags);
1280			outTableId = mMetaRecord.dataRecordType();
1281			recordId = MetaRecord::unpackRecordId(aRecordSection);
1282			return true;
1283        }
1284    }
1285
1286	return false;
1287}
1288
1289//
1290// IndexCursor
1291//
1292
1293IndexCursor::IndexCursor(DbQueryKey *queryKey, const DbVersion &inDbVersion,
1294	const Table &table, const DbConstIndex *index) :
1295	Cursor(inDbVersion), mQueryKey(queryKey), mTable(table), mIndex(index)
1296{
1297	index->performQuery(*queryKey, mBegin, mEnd);
1298}
1299
1300IndexCursor::~IndexCursor()
1301{
1302	// the query key will be deleted automatically, since it's an auto_ptr
1303}
1304
1305bool
1306IndexCursor::next(Table::Id &outTableId,
1307	CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR outAttributes,
1308	CssmData *outData,
1309	Allocator &inAllocator, RecordId &recordId)
1310{
1311	if (mBegin == mEnd)
1312		return false;
1313
1314	ReadSection rs = mIndex->getRecordSection(mBegin++);
1315	const MetaRecord &metaRecord = mTable.getMetaRecord();
1316
1317	outTableId = metaRecord.dataRecordType();
1318	metaRecord.unpackRecord(rs, inAllocator, outAttributes, outData, 0);
1319
1320	recordId = MetaRecord::unpackRecordId(rs);
1321	return true;
1322}
1323
1324//
1325// MultiCursor
1326//
1327MultiCursor::MultiCursor(const CSSM_QUERY *inQuery, const DbVersion &inDbVersion) :
1328    Cursor(inDbVersion), mTableIterator(inDbVersion.begin())
1329{
1330	if (inQuery)
1331		mQuery.reset(new CssmAutoQuery(*inQuery));
1332	else
1333	{
1334		mQuery.reset(new CssmAutoQuery());
1335		mQuery->recordType(CSSM_DL_DB_RECORD_ANY);
1336	}
1337}
1338
1339MultiCursor::~MultiCursor()
1340{
1341}
1342
1343bool
1344MultiCursor::next(Table::Id &outTableId,
1345				  CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes,
1346				  CssmData *inoutData, Allocator &inAllocator, RecordId &recordId)
1347{
1348	for (;;)
1349	{
1350		if (!mCursor.get())
1351		{
1352			if (mTableIterator == mDbVersion->end())
1353				return false;
1354
1355			const Table &aTable = *mTableIterator++;
1356			if (!aTable.matchesTableId(mQuery->recordType()))
1357				continue;
1358
1359			mCursor.reset(aTable.createCursor(mQuery.get(), *mDbVersion));
1360		}
1361
1362		if (mCursor->next(outTableId, inoutAttributes, inoutData, inAllocator, recordId))
1363			return true;
1364
1365		mCursor.reset(NULL);
1366	}
1367}
1368
1369
1370//
1371// DbModifier
1372//
1373DbModifier::DbModifier(AtomicFile &inAtomicFile, const AppleDatabase &db) :
1374	Metadata(),
1375	mDbVersion(),
1376    mAtomicFile(inAtomicFile),
1377	mDb(db)
1378{
1379}
1380
1381DbModifier::~DbModifier()
1382{
1383    try
1384    {
1385		for_each_map_delete(mModifiedTableMap.begin(), mModifiedTableMap.end());
1386		// mAtomicTempFile will do automatic rollback on destruction.
1387    }
1388    catch(...) {}
1389}
1390
1391const RefPointer<const DbVersion>
1392DbModifier::getDbVersion(bool force)
1393{
1394    StLock<Mutex> _(mDbVersionLock);
1395
1396    /* Initialize the shared memory file change mechanism */
1397    pthread_once(&gCommonInitMutex, initCommon);
1398
1399    /* If we don't have a mDbVersion yet, or we are force to re-read the file
1400       before a write transaction, or we have received any notifications after
1401       the last time we read the file, or more than kForceReReadTime seconds
1402       have passed since the last time we read the file, we open the file and
1403       check if it has changed. */
1404    if (!mDbVersion ||
1405        force ||
1406		gSegment == NULL ||
1407        mNotifyCount != *gSegment ||
1408        CFAbsoluteTimeGetCurrent() > mDbLastRead + kForceReReadTime)
1409    {
1410        RefPointer <AtomicBufferedFile> atomicBufferedFile(mAtomicFile.read());
1411        off_t length = atomicBufferedFile->open();
1412        /* Record the number of notifications we've seen and when we last
1413           opened the file.  */
1414		if (gSegment != NULL)
1415		{
1416			mNotifyCount = *gSegment;
1417		}
1418
1419        mDbLastRead = CFAbsoluteTimeGetCurrent();
1420
1421        /* If we already have a mDbVersion, let's check if we can reuse it. */
1422        if (mDbVersion)
1423        {
1424            if (length < AtomSize)
1425                CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
1426
1427            off_t bytesRead = 0;
1428            const uint8 *ptr = atomicBufferedFile->read(length - AtomSize,
1429                AtomSize, bytesRead);
1430            ReadSection aVersionSection(ptr, (size_t)bytesRead);
1431            uint32 aVersionId = aVersionSection[0];
1432
1433            /* If the version stamp hasn't changed the old mDbVersion is still
1434               current. */
1435            if (aVersionId == mDbVersion->getVersionId())
1436                return mDbVersion;
1437        }
1438
1439	mDbVersion = new DbVersion(mDb, atomicBufferedFile);
1440    }
1441
1442    return mDbVersion;
1443}
1444
1445void
1446DbModifier::createDatabase(const CSSM_DBINFO &inDbInfo,
1447						   const CSSM_ACL_ENTRY_INPUT *inInitialAclEntry,
1448						   mode_t mode)
1449{
1450	// XXX This needs better locking.  There is a possible race condition between
1451	// two concurrent creators.  Or a writer/creator or a close/create etc.
1452	if (mAtomicTempFile || !mModifiedTableMap.empty())
1453		CssmError::throwMe(CSSMERR_DL_DATASTORE_ALREADY_EXISTS);
1454
1455    mAtomicTempFile = mAtomicFile.create(mode);
1456	// Set mVersionId to one since this is the first version of the database.
1457	mVersionId = 1;
1458
1459	// we need to create the meta tables first, because inserting tables
1460	// (including the meta tables themselves) relies on them being there
1461    createTable(new MetaRecord(mDb.schemaRelations));
1462    createTable(new MetaRecord(mDb.schemaAttributes));
1463    createTable(new MetaRecord(mDb.schemaIndexes));
1464    createTable(new MetaRecord(mDb.schemaParsingModule));
1465
1466	// now add the meta-tables' schema to the meta tables themselves
1467	insertTableSchema(mDb.schemaRelations);
1468	insertTableSchema(mDb.schemaAttributes);
1469	insertTableSchema(mDb.schemaIndexes);
1470	insertTableSchema(mDb.schemaParsingModule);
1471
1472    if (inInitialAclEntry != NULL)
1473    {
1474        //createACL(*inInitialAclEntry);
1475    }
1476
1477    if (inDbInfo.NumberOfRecordTypes == 0)
1478        return;
1479    if (inDbInfo.RecordAttributeNames == NULL)
1480        CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE);
1481    if (inDbInfo.RecordIndexes == NULL)
1482        CssmError::throwMe(CSSMERR_DL_INVALID_RECORD_INDEX);
1483    if (inDbInfo.DefaultParsingModules == NULL)
1484        CssmError::throwMe(CSSMERR_DL_INVALID_PARSING_MODULE);
1485
1486    for (uint32 anIndex = 0; anIndex < inDbInfo.NumberOfRecordTypes; anIndex++)
1487    {
1488        insertTable(CssmDbRecordAttributeInfo::overlay(inDbInfo.RecordAttributeNames[anIndex]),
1489					&inDbInfo.RecordIndexes[anIndex],
1490					&inDbInfo.DefaultParsingModules[anIndex]);
1491    }
1492}
1493
1494void DbModifier::openDatabase()
1495{
1496	// No need to do anything on open if we are already writing the database.
1497	if (!mAtomicTempFile)
1498		getDbVersion(false);
1499}
1500
1501void DbModifier::closeDatabase()
1502{
1503	commit(); // XXX Requires write lock.
1504	StLock<Mutex> _(mDbVersionLock);
1505	mDbVersion = NULL;
1506}
1507
1508void DbModifier::deleteDatabase()
1509{
1510	bool isDirty = mAtomicTempFile;
1511	rollback(); // XXX Requires write lock.
1512	StLock<Mutex> _(mDbVersionLock);
1513
1514	// Clean up mModifiedTableMap in case this object gets reused again for
1515	// a new create.
1516	for_each_map_delete(mModifiedTableMap.begin(), mModifiedTableMap.end());
1517	mModifiedTableMap.clear();
1518
1519	// If the database was dirty and we had no mDbVersion yet then rollback()
1520	// would have deleted the db.
1521	if (!isDirty || mDbVersion)
1522	{
1523		mDbVersion = NULL;
1524		mAtomicFile.performDelete();
1525	}
1526}
1527
1528void
1529DbModifier::modifyDatabase()
1530{
1531	if (mAtomicTempFile)
1532		return;
1533
1534	try
1535	{
1536		mAtomicTempFile = mAtomicFile.write();
1537		// Now we are holding the write lock make sure we get the latest greatest version of the db.
1538		// Also set mVersionId to one more that that of the old database.
1539		mVersionId = getDbVersion(true)->getVersionId() + 1;
1540
1541		// Never make a database with mVersionId 0 since it makes bad things happen to Jaguar and older systems
1542		if (mVersionId == 0)
1543			mVersionId = 1;
1544
1545		// Remove all old modified tables
1546		for_each_map_delete(mModifiedTableMap.begin(), mModifiedTableMap.end());
1547		mModifiedTableMap.clear();
1548
1549		// Setup the new tables
1550		DbVersion::TableMap::const_iterator anIt =
1551			mDbVersion->mTableMap.begin();
1552		DbVersion::TableMap::const_iterator anEnd =
1553			mDbVersion->mTableMap.end();
1554		for (; anIt != anEnd; ++anIt)
1555		{
1556			auto_ptr<ModifiedTable> aTable(new ModifiedTable(anIt->second));
1557			mModifiedTableMap.insert(ModifiedTableMap::value_type(anIt->first,
1558																  aTable.get()));
1559			aTable.release();
1560		}
1561	}
1562	catch(...)
1563	{
1564		for_each_map_delete(mModifiedTableMap.begin(), mModifiedTableMap.end());
1565		mModifiedTableMap.clear();
1566		rollback();
1567		throw;
1568	}
1569}
1570
1571void
1572DbModifier::deleteRecord(Table::Id inTableId, const RecordId &inRecordId)
1573{
1574	modifyDatabase();
1575	findTable(inTableId).deleteRecord(inRecordId);
1576}
1577
1578const RecordId
1579DbModifier::insertRecord(Table::Id inTableId,
1580						 const CSSM_DB_RECORD_ATTRIBUTE_DATA *inAttributes,
1581						 const CssmData *inData)
1582{
1583	modifyDatabase();
1584	return findTable(inTableId).insertRecord(mVersionId, inAttributes, inData);
1585}
1586
1587const RecordId
1588DbModifier::updateRecord(Table::Id inTableId, const RecordId &inRecordId,
1589						 const CSSM_DB_RECORD_ATTRIBUTE_DATA *inAttributes,
1590						 const CssmData *inData,
1591						 CSSM_DB_MODIFY_MODE inModifyMode)
1592{
1593	modifyDatabase();
1594	return findTable(inTableId).updateRecord(inRecordId, inAttributes, inData, inModifyMode);
1595}
1596
1597// Create a table associated with a given metarecord, and add the table
1598// to the database.
1599
1600ModifiedTable *
1601DbModifier::createTable(MetaRecord *inMetaRecord)
1602{
1603	auto_ptr<MetaRecord> aMetaRecord(inMetaRecord);
1604	auto_ptr<ModifiedTable> aModifiedTable(new ModifiedTable(inMetaRecord));
1605	// Now that aModifiedTable is fully constructed it owns inMetaRecord
1606	aMetaRecord.release();
1607
1608	if (!mModifiedTableMap.insert
1609		(ModifiedTableMap::value_type(inMetaRecord->dataRecordType(),
1610									  aModifiedTable.get())).second)
1611	{
1612		// XXX Should be CSSMERR_DL_DUPLICATE_RECORDTYPE.  Since that
1613		// doesn't exist we report that the metatable's unique index would
1614		// no longer be valid
1615        CssmError::throwMe(CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA);
1616	}
1617
1618	return aModifiedTable.release();
1619}
1620
1621void
1622DbModifier::deleteTable(Table::Id inTableId)
1623{
1624	modifyDatabase();
1625    // Can't delete schema tables.
1626    if (CSSM_DB_RECORDTYPE_SCHEMA_START <= inTableId
1627		&& inTableId < CSSM_DB_RECORDTYPE_SCHEMA_END)
1628        CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE);
1629
1630	// Find the ModifiedTable and delete it
1631    ModifiedTableMap::iterator it = mModifiedTableMap.find(inTableId);
1632    if (it == mModifiedTableMap.end())
1633        CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE);
1634
1635    delete it->second;
1636    mModifiedTableMap.erase(it);
1637}
1638
1639uint32
1640DbModifier::writeAuthSection(uint32 inSectionOffset)
1641{
1642	WriteSection anAuthSection;
1643
1644    // XXX Put real data into the authsection.
1645	uint32 anOffset = anAuthSection.put(0, 0);
1646	anAuthSection.size(anOffset);
1647
1648	mAtomicTempFile->write(AtomicFile::FromStart, inSectionOffset,
1649					anAuthSection.address(), anAuthSection.size());
1650    return inSectionOffset + anOffset;
1651}
1652
1653uint32
1654DbModifier::writeSchemaSection(uint32 inSectionOffset)
1655{
1656	uint32 aTableCount = (uint32) mModifiedTableMap.size();
1657	WriteSection aTableSection(Allocator::standard(),
1658							   OffsetTables + AtomSize * aTableCount);
1659	// Set aTableSection to the correct size.
1660	aTableSection.size(OffsetTables + AtomSize * aTableCount);
1661	aTableSection.put(OffsetTablesCount, aTableCount);
1662
1663	uint32 anOffset = inSectionOffset + OffsetTables + AtomSize * aTableCount;
1664	ModifiedTableMap::const_iterator anIt = mModifiedTableMap.begin();
1665	ModifiedTableMap::const_iterator anEnd = mModifiedTableMap.end();
1666	for (uint32 aTableNumber = 0; anIt != anEnd; anIt++, aTableNumber++)
1667	{
1668		// Put the offset to the current table relative to the start of
1669		// this section into the tables array
1670		aTableSection.put(OffsetTables + AtomSize * aTableNumber,
1671						  anOffset - inSectionOffset);
1672		anOffset = anIt->second->writeTable(*mAtomicTempFile, anOffset);
1673	}
1674
1675	aTableSection.put(OffsetSchemaSize, anOffset - inSectionOffset);
1676	mAtomicTempFile->write(AtomicFile::FromStart, inSectionOffset,
1677					aTableSection.address(), aTableSection.size());
1678
1679	return anOffset;
1680}
1681
1682void
1683DbModifier::commit()
1684{
1685    if (!mAtomicTempFile)
1686        return;
1687    try
1688    {
1689		WriteSection aHeaderSection(Allocator::standard(), size_t(HeaderSize));
1690		// Set aHeaderSection to the correct size.
1691		aHeaderSection.size(HeaderSize);
1692
1693        // Start writing sections after the header
1694        uint32 anOffset = HeaderOffset + HeaderSize;
1695
1696        // Write auth section
1697		aHeaderSection.put(OffsetAuthOffset, anOffset);
1698        anOffset = writeAuthSection(anOffset);
1699        // Write schema section
1700		aHeaderSection.put(OffsetSchemaOffset, anOffset);
1701        anOffset = writeSchemaSection(anOffset);
1702
1703		// Write out the file header.
1704		aHeaderSection.put(OffsetMagic, HeaderMagic);
1705		aHeaderSection.put(OffsetVersion, HeaderVersion);
1706        mAtomicTempFile->write(AtomicFile::FromStart, HeaderOffset,
1707							   aHeaderSection.address(), aHeaderSection.size());
1708
1709		// Write out the versionId.
1710		WriteSection aVersionSection(Allocator::standard(), size_t(AtomSize));
1711		anOffset = aVersionSection.put(0, mVersionId);
1712		aVersionSection.size(anOffset);
1713
1714        mAtomicTempFile->write(AtomicFile::FromEnd, 0,
1715							   aVersionSection.address(), aVersionSection.size());
1716
1717		mAtomicTempFile->commit();
1718		mAtomicTempFile = NULL;
1719	   /* Initialize the shared memory file change mechanism */
1720	   pthread_once(&gCommonInitMutex, initCommon);
1721
1722	   if (gSegment != NULL)
1723		{
1724			/*
1725				PLEASE NOTE:
1726
1727				The following operation is endian safe because we are not looking
1728				for monotonic increase. I have tested every possible value of
1729				*gSegment, and there is no value for which alternating
1730				big and little endian increments will produce the original value.
1731			*/
1732
1733			OSAtomicIncrement32Barrier (gSegment);
1734		}
1735    }
1736    catch(...)
1737    {
1738		rollback();
1739		throw;
1740    }
1741}
1742
1743void
1744DbModifier::rollback() throw()
1745{
1746	// This will destroy the AtomicTempFile if we have one causing it to rollback.
1747	mAtomicTempFile = NULL;
1748}
1749
1750const RecordId
1751DbModifier::getRecord(Table::Id inTableId, const RecordId &inRecordId,
1752					  CSSM_DB_RECORD_ATTRIBUTE_DATA *inoutAttributes,
1753					  CssmData *inoutData, Allocator &inAllocator)
1754{
1755    if (mAtomicTempFile)
1756	{
1757		// We are in the midst of changing the database.
1758		return findTable(inTableId).getRecord(inRecordId, inoutAttributes,
1759			inoutData, inAllocator);
1760	}
1761	else
1762	{
1763		return getDbVersion(false)->getRecord(inTableId, inRecordId,
1764			inoutAttributes, inoutData, inAllocator);
1765	}
1766}
1767
1768Cursor *
1769DbModifier::createCursor(const CSSM_QUERY *inQuery)
1770{
1771	if (mAtomicTempFile)
1772	{
1773		// We are modifying this database.
1774
1775		// If we have a mDbVersion already then it's a snapshot of the database
1776		// right before the modifications started.  So return a cursor using
1777		// that.
1778		if (mDbVersion)
1779			return mDbVersion->createCursor(inQuery);
1780
1781		// This is a newly created but never commited database.  Return a
1782		// Cursor that will not return any matches.
1783		return new Cursor();
1784	}
1785
1786	// Get the latest and greatest version of the db and create the cursor
1787	// on that.
1788	return getDbVersion(false)->createCursor(inQuery);
1789}
1790
1791// Insert schema records for a new table into the metatables of the database. This gets
1792// called while a database is being created.
1793
1794void
1795DbModifier::insertTableSchema(const CssmDbRecordAttributeInfo &inInfo,
1796	const CSSM_DB_RECORD_INDEX_INFO *inIndexInfo /* = NULL */)
1797{
1798	ModifiedTable &aTable = findTable(inInfo.DataRecordType);
1799	const MetaRecord &aMetaRecord = aTable.getMetaRecord();
1800
1801	CssmAutoDbRecordAttributeData aRecordBuilder(5); // Set capacity to 5 so we don't need to grow
1802
1803	// Create the entry for the SchemaRelations table.
1804	aRecordBuilder.add(RelationID, inInfo.recordType());
1805	aRecordBuilder.add(RelationName, mDb.recordName(inInfo.recordType()));
1806
1807	// Insert the record into the SchemaRelations ModifiedTable
1808    findTable(mDb.schemaRelations.DataRecordType).insertRecord(mVersionId,
1809		&aRecordBuilder, NULL);
1810
1811	ModifiedTable &anAttributeTable = findTable(mDb.schemaAttributes.DataRecordType);
1812    for (uint32 anIndex = 0; anIndex < inInfo.size(); anIndex++)
1813    {
1814		// Create an entry for the SchemaAttributes table.
1815		aRecordBuilder.clear();
1816		aRecordBuilder.add(RelationID, inInfo.recordType());
1817		aRecordBuilder.add(AttributeNameFormat, inInfo.at(anIndex).nameFormat());
1818
1819		uint32 attributeId = aMetaRecord.metaAttribute(inInfo.at(anIndex)).attributeId();
1820
1821        switch (inInfo.at(anIndex).nameFormat())
1822        {
1823            case CSSM_DB_ATTRIBUTE_NAME_AS_STRING:
1824				aRecordBuilder.add(AttributeName, inInfo.at(anIndex).Label.AttributeName);
1825				break;
1826            case CSSM_DB_ATTRIBUTE_NAME_AS_OID:
1827				aRecordBuilder.add(AttributeNameID, inInfo.at(anIndex).Label.AttributeOID);
1828				break;
1829            case CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER:
1830                break;
1831            default:
1832                CssmError::throwMe(CSSMERR_DL_INVALID_FIELD_NAME);
1833		}
1834
1835		aRecordBuilder.add(AttributeID, attributeId);
1836		aRecordBuilder.add(AttributeFormat, inInfo.at(anIndex).format());
1837
1838		// Insert the record into the SchemaAttributes ModifiedTable
1839		anAttributeTable.insertRecord(mVersionId, &aRecordBuilder, NULL);
1840    }
1841
1842	if (inIndexInfo != NULL) {
1843
1844		if (inIndexInfo->DataRecordType != inInfo.DataRecordType &&
1845			inIndexInfo->NumberOfIndexes > 0)
1846			CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE);
1847
1848		ModifiedTable &indexMetaTable = findTable(mDb.schemaIndexes.DataRecordType);
1849		uint32 aNumberOfIndexes = inIndexInfo->NumberOfIndexes;
1850
1851		for (uint32 anIndex = 0; anIndex < aNumberOfIndexes; anIndex++)
1852		{
1853			const CssmDbIndexInfo &thisIndex = CssmDbIndexInfo::overlay(inIndexInfo->IndexInfo[anIndex]);
1854
1855			// make sure the index is supported
1856			if (thisIndex.dataLocation() != CSSM_DB_INDEX_ON_ATTRIBUTE)
1857				CssmError::throwMe(CSSMERR_DL_INVALID_INDEX_INFO);
1858
1859			// assign an index ID: the unique index is ID 0, all others are ID > 0
1860			uint32 indexId;
1861			if (thisIndex.IndexType == CSSM_DB_INDEX_UNIQUE)
1862				indexId = 0;
1863			else
1864				indexId = anIndex + 1;
1865
1866			// figure out the attribute ID
1867			uint32 attributeId =
1868				aMetaRecord.metaAttribute(thisIndex.Info).attributeId();
1869
1870			// Create an entry for the SchemaIndexes table.
1871			aRecordBuilder.clear();
1872			aRecordBuilder.add(RelationID, inInfo.DataRecordType);
1873			aRecordBuilder.add(IndexID, indexId);
1874			aRecordBuilder.add(AttributeID, attributeId);
1875			aRecordBuilder.add(IndexType, thisIndex.IndexType);
1876			aRecordBuilder.add(IndexedDataLocation, thisIndex.IndexedDataLocation);
1877
1878			// Insert the record into the SchemaIndexes ModifiedTable
1879			indexMetaTable.insertRecord(mVersionId, &aRecordBuilder, NULL);
1880
1881			// update the table's index objects
1882			DbMutableIndex &index = aTable.findIndex(indexId, aMetaRecord, indexId == 0);
1883			index.appendAttribute(attributeId);
1884		}
1885	}
1886}
1887
1888// Insert a new table. The attribute info is required; the index and parsing module
1889// descriptions are optional. This version gets called during the creation of a
1890// database.
1891
1892void
1893DbModifier::insertTable(const CssmDbRecordAttributeInfo &inInfo,
1894						const CSSM_DB_RECORD_INDEX_INFO *inIndexInfo /* = NULL */,
1895						const CSSM_DB_PARSING_MODULE_INFO *inParsingModule /* = NULL */)
1896{
1897	modifyDatabase();
1898	createTable(new MetaRecord(inInfo));
1899	insertTableSchema(inInfo, inIndexInfo);
1900}
1901
1902// Insert a new table. This is the version that gets called when a table is added
1903// after a database has been created.
1904
1905void
1906DbModifier::insertTable(Table::Id inTableId, const string &inTableName,
1907						uint32 inNumberOfAttributes,
1908						const CSSM_DB_SCHEMA_ATTRIBUTE_INFO *inAttributeInfo,
1909						uint32 inNumberOfIndexes,
1910						const CSSM_DB_SCHEMA_INDEX_INFO *inIndexInfo)
1911{
1912	modifyDatabase();
1913	ModifiedTable *aTable = createTable(new MetaRecord(inTableId, inNumberOfAttributes, inAttributeInfo));
1914
1915	CssmAutoDbRecordAttributeData aRecordBuilder(6); // Set capacity to 6 so we don't need to grow
1916
1917	// Create the entry for the SchemaRelations table.
1918	aRecordBuilder.add(RelationID, inTableId);
1919	aRecordBuilder.add(RelationName, inTableName);
1920
1921	// Insert the record into the SchemaRelations ModifiedTable
1922    findTable(mDb.schemaRelations.DataRecordType).insertRecord(mVersionId,
1923		&aRecordBuilder, NULL);
1924
1925	ModifiedTable &anAttributeTable = findTable(mDb.schemaAttributes.DataRecordType);
1926    for (uint32 anIndex = 0; anIndex < inNumberOfAttributes; anIndex++)
1927    {
1928		// Create an entry for the SchemaAttributes table.
1929		aRecordBuilder.clear();
1930		aRecordBuilder.add(RelationID, inTableId);
1931		// XXX What should this be?  We set it to CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER for now
1932		// since the AttributeID is always valid.
1933		aRecordBuilder.add(AttributeNameFormat, uint32(CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER));
1934		aRecordBuilder.add(AttributeID, inAttributeInfo[anIndex].AttributeId);
1935		if (inAttributeInfo[anIndex].AttributeName)
1936			aRecordBuilder.add(AttributeName, inAttributeInfo[anIndex].AttributeName);
1937		if (inAttributeInfo[anIndex].AttributeNameID.Length > 0)
1938			aRecordBuilder.add(AttributeNameID, inAttributeInfo[anIndex].AttributeNameID);
1939		aRecordBuilder.add(AttributeFormat, inAttributeInfo[anIndex].DataType);
1940
1941		// Insert the record into the SchemaAttributes ModifiedTable
1942		anAttributeTable.insertRecord(mVersionId, &aRecordBuilder, NULL);
1943    }
1944
1945	ModifiedTable &anIndexTable = findTable(mDb.schemaIndexes.DataRecordType);
1946    for (uint32 anIndex = 0; anIndex < inNumberOfIndexes; anIndex++)
1947    {
1948		// Create an entry for the SchemaIndexes table.
1949		aRecordBuilder.clear();
1950		aRecordBuilder.add(RelationID, inTableId);
1951		aRecordBuilder.add(IndexID, inIndexInfo[anIndex].IndexId);
1952		aRecordBuilder.add(AttributeID, inIndexInfo[anIndex].AttributeId);
1953		aRecordBuilder.add(IndexType, inIndexInfo[anIndex].IndexType);
1954		aRecordBuilder.add(IndexedDataLocation, inIndexInfo[anIndex].IndexedDataLocation);
1955
1956		// Insert the record into the SchemaIndexes ModifiedTable
1957		anIndexTable.insertRecord(mVersionId, &aRecordBuilder, NULL);
1958
1959		// update the table's index objects
1960		DbMutableIndex &index = aTable->findIndex(inIndexInfo[anIndex].IndexId,
1961			aTable->getMetaRecord(), inIndexInfo[anIndex].IndexType == CSSM_DB_INDEX_UNIQUE);
1962		index.appendAttribute(inIndexInfo[anIndex].AttributeId);
1963    }
1964}
1965
1966
1967
1968bool DbModifier::hasTable(Table::Id inTableId)
1969{
1970	return getDbVersion(false)->hasTable(inTableId);
1971}
1972
1973
1974
1975ModifiedTable &
1976DbModifier::findTable(Table::Id inTableId)
1977{
1978    ModifiedTableMap::iterator it = mModifiedTableMap.find(inTableId);
1979    if (it == mModifiedTableMap.end())
1980		CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE);
1981	return *it->second;
1982}
1983
1984
1985//
1986// AppleDatabaseManager implementation
1987//
1988
1989AppleDatabaseManager::AppleDatabaseManager(const AppleDatabaseTableName *tableNames)
1990	:	DatabaseManager(),
1991		mTableNames(tableNames)
1992{
1993	// make sure that a proper set of table ids and names has been provided
1994
1995	if (!mTableNames)
1996		CssmError::throwMe(CSSMERR_DL_INTERNAL_ERROR);
1997	else {
1998		uint32 i;
1999		for (i = 0; mTableNames[i].mTableName; i++) {}
2000		if (i < AppleDatabaseTableName::kNumRequiredTableNames)
2001			CssmError::throwMe(CSSMERR_DL_INTERNAL_ERROR);
2002	}
2003}
2004
2005Database *
2006AppleDatabaseManager::make(const DbName &inDbName)
2007{
2008    return new AppleDatabase(inDbName, mTableNames);
2009}
2010
2011
2012//
2013// AppleDbContext implementation
2014//
2015
2016/* This is the version 0 CSSM_APPLEDL_OPEN_PARAMETERS struct used up to 10.2.x. */
2017extern "C" {
2018
2019typedef struct cssm_appledl_open_parameters_v0
2020{
2021		uint32 length;  /* Should be sizeof(CSSM_APPLEDL_OPEN_PARAMETERS_V0). */
2022		uint32 version; /* Should be 0. */
2023		CSSM_BOOL autoCommit;
2024} CSSM_APPLEDL_OPEN_PARAMETERS_V0;
2025
2026};
2027
2028AppleDbContext::AppleDbContext(Database &inDatabase,
2029							   DatabaseSession &inDatabaseSession,
2030							   CSSM_DB_ACCESS_TYPE inAccessRequest,
2031							   const AccessCredentials *inAccessCred,
2032							   const void *inOpenParameters) :
2033	DbContext(inDatabase, inDatabaseSession, inAccessRequest, inAccessCred),
2034	mAutoCommit(true),
2035	mMode(0666)
2036{
2037	const CSSM_APPLEDL_OPEN_PARAMETERS *anOpenParameters =
2038		reinterpret_cast<const CSSM_APPLEDL_OPEN_PARAMETERS *>(inOpenParameters);
2039
2040	if (anOpenParameters)
2041	{
2042		switch (anOpenParameters->version)
2043		{
2044		case 1:
2045			if (anOpenParameters->length < sizeof(CSSM_APPLEDL_OPEN_PARAMETERS))
2046				CssmError::throwMe(CSSMERR_APPLEDL_INVALID_OPEN_PARAMETERS);
2047
2048			if (anOpenParameters->mask & kCSSM_APPLEDL_MASK_MODE)
2049				mMode = anOpenParameters->mode;
2050			/*DROPTHROUGH*/
2051		case 0:
2052			if (anOpenParameters->length < sizeof(CSSM_APPLEDL_OPEN_PARAMETERS_V0))
2053				CssmError::throwMe(CSSMERR_APPLEDL_INVALID_OPEN_PARAMETERS);
2054
2055			mAutoCommit = anOpenParameters->autoCommit == CSSM_FALSE ? false : true;
2056			break;
2057
2058		default:
2059			CssmError::throwMe(CSSMERR_APPLEDL_INVALID_OPEN_PARAMETERS);
2060		}
2061	}
2062}
2063
2064AppleDbContext::~AppleDbContext()
2065{
2066}
2067
2068//
2069// AppleDatabase implementation
2070//
2071AppleDatabase::AppleDatabase(const DbName &inDbName, const AppleDatabaseTableName *tableNames) :
2072    Database(inDbName),
2073	schemaRelations(tableNames[AppleDatabaseTableName::kSchemaInfo].mTableId,
2074		sizeof(AttrSchemaRelations) / sizeof(CSSM_DB_ATTRIBUTE_INFO),
2075		const_cast<CSSM_DB_ATTRIBUTE_INFO_PTR>(AttrSchemaRelations)),
2076	schemaAttributes(tableNames[AppleDatabaseTableName::kSchemaAttributes].mTableId,
2077		sizeof(AttrSchemaAttributes) / sizeof(CSSM_DB_ATTRIBUTE_INFO),
2078		const_cast<CSSM_DB_ATTRIBUTE_INFO_PTR>(AttrSchemaAttributes)),
2079	schemaIndexes(tableNames[AppleDatabaseTableName::kSchemaIndexes].mTableId,
2080		sizeof(AttrSchemaIndexes) / sizeof(CSSM_DB_ATTRIBUTE_INFO),
2081		const_cast<CSSM_DB_ATTRIBUTE_INFO_PTR>(AttrSchemaIndexes)),
2082	schemaParsingModule(tableNames[AppleDatabaseTableName::kSchemaParsingModule].mTableId,
2083		sizeof(AttrSchemaParsingModule) / sizeof(CSSM_DB_ATTRIBUTE_INFO),
2084		const_cast<CSSM_DB_ATTRIBUTE_INFO_PTR>(AttrSchemaParsingModule)),
2085    mAtomicFile(mDbName.dbName()),
2086	mDbModifier(mAtomicFile, *this),
2087	mTableNames(tableNames)
2088{
2089	/* temp check for X509Anchors access - this should removed before Leopard GM */
2090	if(!strcmp(inDbName.dbName(), "/System/Library/Keychains/X509Anchors")) {
2091		Syslog::alert("Warning: accessing obsolete X509Anchors.");
2092	}
2093}
2094
2095AppleDatabase::~AppleDatabase()
2096{
2097}
2098
2099// Return the name of a record type. This uses a table that maps record types
2100// to record names. The table is provided when the database is created.
2101
2102const char *AppleDatabase::recordName(CSSM_DB_RECORDTYPE inRecordType) const
2103{
2104	if (inRecordType == CSSM_DL_DB_RECORD_ANY || inRecordType == CSSM_DL_DB_RECORD_ALL_KEYS)
2105		CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE);
2106
2107	for (uint32 i = 0; mTableNames[i].mTableName; i++)
2108		if (mTableNames[i].mTableId == inRecordType)
2109			return mTableNames[i].mTableName;
2110
2111	return "";
2112}
2113
2114DbContext *
2115AppleDatabase::makeDbContext(DatabaseSession &inDatabaseSession,
2116                             CSSM_DB_ACCESS_TYPE inAccessRequest,
2117                             const AccessCredentials *inAccessCred,
2118                             const void *inOpenParameters)
2119{
2120    return new AppleDbContext(*this, inDatabaseSession, inAccessRequest,
2121							  inAccessCred, inOpenParameters);
2122}
2123
2124void
2125AppleDatabase::dbCreate(DbContext &inDbContext, const CSSM_DBINFO &inDBInfo,
2126                        const CSSM_ACL_ENTRY_INPUT *inInitialAclEntry)
2127{
2128	AppleDbContext &context = safer_cast<AppleDbContext &>(inDbContext);
2129    try
2130    {
2131		StLock<Mutex> _(mWriteLock);
2132        mDbModifier.createDatabase(inDBInfo, inInitialAclEntry, context.mode());
2133    }
2134    catch(...)
2135    {
2136        mDbModifier.rollback();
2137        throw;
2138    }
2139	if (context.autoCommit())
2140		mDbModifier.commit();
2141}
2142
2143void
2144AppleDatabase::dbOpen(DbContext &inDbContext)
2145{
2146	mDbModifier.openDatabase();
2147}
2148
2149void
2150AppleDatabase::dbClose()
2151{
2152	StLock<Mutex> _(mWriteLock);
2153	mDbModifier.closeDatabase();
2154}
2155
2156void
2157AppleDatabase::dbDelete(DatabaseSession &inDatabaseSession,
2158                        const AccessCredentials *inAccessCred)
2159{
2160	StLock<Mutex> _(mWriteLock);
2161    // XXX Check callers credentials.
2162	mDbModifier.deleteDatabase();
2163}
2164
2165void
2166AppleDatabase::createRelation(DbContext &inDbContext,
2167                              CSSM_DB_RECORDTYPE inRelationID,
2168                              const char *inRelationName,
2169                              uint32 inNumberOfAttributes,
2170                              const CSSM_DB_SCHEMA_ATTRIBUTE_INFO *inAttributeInfo,
2171                              uint32 inNumberOfIndexes,
2172                              const CSSM_DB_SCHEMA_INDEX_INFO &inIndexInfo)
2173{
2174	try
2175	{
2176		StLock<Mutex> _(mWriteLock);
2177		// XXX Fix the refs here.
2178		mDbModifier.insertTable(inRelationID, inRelationName,
2179								inNumberOfAttributes, inAttributeInfo,
2180								inNumberOfIndexes, &inIndexInfo);
2181	}
2182	catch(...)
2183	{
2184		if (safer_cast<AppleDbContext &>(inDbContext).autoCommit())
2185			mDbModifier.rollback();
2186		throw;
2187	}
2188	if (safer_cast<AppleDbContext &>(inDbContext).autoCommit())
2189		mDbModifier.commit();
2190}
2191
2192void
2193AppleDatabase::destroyRelation(DbContext &inDbContext,
2194                               CSSM_DB_RECORDTYPE inRelationID)
2195{
2196	try
2197	{
2198		StLock<Mutex> _(mWriteLock);
2199		mDbModifier.deleteTable(inRelationID);
2200	}
2201	catch(...)
2202	{
2203		if (safer_cast<AppleDbContext &>(inDbContext).autoCommit())
2204			mDbModifier.rollback();
2205		throw;
2206	}
2207	if (safer_cast<AppleDbContext &>(inDbContext).autoCommit())
2208		mDbModifier.commit();
2209}
2210
2211void
2212AppleDatabase::authenticate(DbContext &inDbContext,
2213                            CSSM_DB_ACCESS_TYPE inAccessRequest,
2214                            const AccessCredentials &inAccessCred)
2215{
2216    CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED);
2217}
2218
2219void
2220AppleDatabase::getDbAcl(DbContext &inDbContext,
2221                        const CSSM_STRING *inSelectionTag,
2222                        uint32 &outNumberOfAclInfos,
2223                        CSSM_ACL_ENTRY_INFO_PTR &outAclInfos)
2224{
2225    CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED);
2226}
2227
2228void
2229AppleDatabase::changeDbAcl(DbContext &inDbContext,
2230                           const AccessCredentials &inAccessCred,
2231                           const CSSM_ACL_EDIT &inAclEdit)
2232{
2233    CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED);
2234}
2235
2236void
2237AppleDatabase::getDbOwner(DbContext &inDbContext,
2238						  CSSM_ACL_OWNER_PROTOTYPE &outOwner)
2239{
2240    CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED);
2241}
2242
2243void
2244AppleDatabase::changeDbOwner(DbContext &inDbContext,
2245                             const AccessCredentials &inAccessCred,
2246                             const CSSM_ACL_OWNER_PROTOTYPE &inNewOwner)
2247{
2248    CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED);
2249}
2250
2251char *
2252AppleDatabase::getDbNameFromHandle(const DbContext &inDbContext) const
2253{
2254    CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED);
2255}
2256
2257CSSM_DB_UNIQUE_RECORD_PTR
2258AppleDatabase::dataInsert(DbContext &inDbContext,
2259                          CSSM_DB_RECORDTYPE inRecordType,
2260                          const CSSM_DB_RECORD_ATTRIBUTE_DATA *inAttributes,
2261                          const CssmData *inData)
2262{
2263	CSSM_DB_UNIQUE_RECORD_PTR anUniqueRecordPtr = NULL;
2264	try
2265	{
2266		StLock<Mutex> _(mWriteLock);
2267		const RecordId aRecordId =
2268			mDbModifier.insertRecord(inRecordType, inAttributes, inData);
2269
2270		anUniqueRecordPtr = createUniqueRecord(inDbContext, inRecordType,
2271											   aRecordId);
2272		if (safer_cast<AppleDbContext &>(inDbContext).autoCommit())
2273			mDbModifier.commit();
2274	}
2275	catch(...)
2276	{
2277		if (anUniqueRecordPtr != NULL)
2278			freeUniqueRecord(inDbContext, *anUniqueRecordPtr);
2279
2280		if (safer_cast<AppleDbContext &>(inDbContext).autoCommit())
2281			mDbModifier.rollback();
2282		throw;
2283	}
2284
2285	return anUniqueRecordPtr;
2286}
2287
2288void
2289AppleDatabase::dataDelete(DbContext &inDbContext,
2290                          const CSSM_DB_UNIQUE_RECORD &inUniqueRecord)
2291{
2292    try
2293    {
2294		// syslog if it's the .Mac password
2295		CSSM_DB_RECORD_ATTRIBUTE_DATA attrData;
2296		// we have to do this in two phases -- the first to get the record type, and the second to actually read the attributes.  Otherwise, we might get
2297		// an exception.
2298		memset(&attrData, 0, sizeof(attrData));
2299		dataGetFromUniqueRecordId(inDbContext, inUniqueRecord, &attrData, NULL);
2300
2301		if (attrData.DataRecordType == CSSM_DL_DB_RECORD_GENERIC_PASSWORD)
2302		{
2303			CSSM_DB_ATTRIBUTE_DATA attributes;
2304
2305			// setup some attributes and see if we are indeed the .Mac password
2306			attributes.Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER;
2307			attributes.Info.Label.AttributeID = 'svce';
2308			attributes.Info.AttributeFormat = 0;
2309			attributes.NumberOfValues = 1;
2310			attributes.Value = NULL;
2311
2312			attrData.NumberOfAttributes = 1;
2313			attrData.AttributeData = &attributes;
2314
2315			dataGetFromUniqueRecordId(inDbContext, inUniqueRecord, &attrData, NULL);
2316
2317			// now check the results
2318			std::string dataString((const char*) attrData.AttributeData[0].Value[0].Data, attrData.AttributeData[0].Value[0].Length);
2319			if (dataString == "iTools")
2320			{
2321				syslog(LOG_WARNING, "Warning: Removed .Me password");
2322			}
2323
2324			free(attrData.AttributeData[0].Value[0].Data);
2325			free(attrData.AttributeData[0].Value);
2326		}
2327
2328		StLock<Mutex> _(mWriteLock);
2329		Table::Id aTableId;
2330		const RecordId aRecordId(parseUniqueRecord(inUniqueRecord, aTableId));
2331		mDbModifier.deleteRecord(aTableId, aRecordId);
2332	}
2333    catch(...)
2334    {
2335		if (safer_cast<AppleDbContext &>(inDbContext).autoCommit())
2336			mDbModifier.rollback();
2337        throw;
2338    }
2339
2340	if (safer_cast<AppleDbContext &>(inDbContext).autoCommit())
2341		mDbModifier.commit();
2342}
2343
2344void
2345AppleDatabase::dataModify(DbContext &inDbContext,
2346                          CSSM_DB_RECORDTYPE inRecordType,
2347                          CSSM_DB_UNIQUE_RECORD &inoutUniqueRecord,
2348                          const CSSM_DB_RECORD_ATTRIBUTE_DATA *inAttributesToBeModified,
2349                          const CssmData *inDataToBeModified,
2350                          CSSM_DB_MODIFY_MODE inModifyMode)
2351{
2352    try
2353    {
2354		StLock<Mutex> _(mWriteLock);
2355		Table::Id aTableId;
2356		const RecordId oldRecordId = parseUniqueRecord(inoutUniqueRecord,
2357			aTableId);
2358#if 1
2359		if (inRecordType != aTableId)
2360#else
2361		if (inRecordType != aTableId &&
2362			inRecordType != CSSM_DL_DB_RECORD_ANY &&
2363			!(inRecordType == CSSM_DL_DB_RECORD_ALL_KEYS &&
2364			  (aTableId == CSSM_DL_DB_RECORD_PUBLIC_KEY ||
2365			   aTableId == CSSM_DL_DB_RECORD_PRIVATE_KEY ||
2366			   aTableId == CSSM_DL_DB_RECORD_SYMMETRIC_KEY)))
2367#endif
2368		{
2369			CssmError::throwMe(CSSMERR_DL_INVALID_RECORD_UID);
2370		}
2371
2372		const RecordId newRecordId =
2373			mDbModifier.updateRecord(aTableId,
2374									 oldRecordId,
2375									 inAttributesToBeModified,
2376									 inDataToBeModified,
2377									 inModifyMode);
2378		updateUniqueRecord(inDbContext, aTableId, newRecordId,
2379			inoutUniqueRecord);
2380	}
2381    catch(...)
2382    {
2383		if (safer_cast<AppleDbContext &>(inDbContext).autoCommit())
2384			mDbModifier.rollback();
2385        throw;
2386    }
2387
2388	if (safer_cast<AppleDbContext &>(inDbContext).autoCommit())
2389		mDbModifier.commit();
2390}
2391
2392CSSM_HANDLE
2393AppleDatabase::dataGetFirst(DbContext &inDbContext,
2394                            const CssmQuery *inQuery,
2395                            CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes,
2396                            CssmData *inoutData,
2397                            CSSM_DB_UNIQUE_RECORD_PTR &outUniqueRecord)
2398{
2399	// XXX: register Cursor with DbContext and have DbContext call
2400	// dataAbortQuery for all outstanding Query objects on close.
2401	auto_ptr<Cursor> aCursor(mDbModifier.createCursor(inQuery));
2402	Table::Id aTableId;
2403	RecordId aRecordId;
2404
2405	if (!aCursor->next(aTableId, inoutAttributes, inoutData,
2406					   inDbContext.mDatabaseSession, aRecordId))
2407		// return a NULL handle, and implicitly delete the cursor
2408		return CSSM_INVALID_HANDLE;
2409
2410	outUniqueRecord = createUniqueRecord(inDbContext, aTableId, aRecordId);
2411	return aCursor.release()->handle(); // We didn't throw so keep the Cursor around.
2412}
2413
2414bool
2415AppleDatabase::dataGetNext(DbContext &inDbContext,
2416                           CSSM_HANDLE inResultsHandle,
2417                           CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes,
2418                           CssmData *inoutData,
2419                           CSSM_DB_UNIQUE_RECORD_PTR &outUniqueRecord)
2420{
2421	auto_ptr<Cursor> aCursor(&HandleObject::find<Cursor>(inResultsHandle, CSSMERR_DL_INVALID_RESULTS_HANDLE));
2422	Table::Id aTableId;
2423	RecordId aRecordId;
2424
2425	if (!aCursor->next(aTableId, inoutAttributes, inoutData, inDbContext.mDatabaseSession, aRecordId))
2426		return false;
2427
2428	outUniqueRecord = createUniqueRecord(inDbContext, aTableId, aRecordId);
2429
2430	aCursor.release();
2431	return true;
2432}
2433
2434void
2435AppleDatabase::dataAbortQuery(DbContext &inDbContext,
2436                              CSSM_HANDLE inResultsHandle)
2437{
2438	delete &HandleObject::find<Cursor>(inResultsHandle, CSSMERR_DL_INVALID_RESULTS_HANDLE);
2439}
2440
2441void
2442AppleDatabase::dataGetFromUniqueRecordId(DbContext &inDbContext,
2443                                         const CSSM_DB_UNIQUE_RECORD &inUniqueRecord,
2444                                         CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes,
2445                                         CssmData *inoutData)
2446{
2447	Table::Id aTableId;
2448	const RecordId aRecordId(parseUniqueRecord(inUniqueRecord, aTableId));
2449	// XXX Change CDSA spec to use new RecordId returned by this function
2450	mDbModifier.getRecord(aTableId, aRecordId, inoutAttributes, inoutData,
2451						  inDbContext.mDatabaseSession);
2452}
2453
2454void
2455AppleDatabase::freeUniqueRecord(DbContext &inDbContext,
2456                                CSSM_DB_UNIQUE_RECORD &inUniqueRecord)
2457{
2458	if (inUniqueRecord.RecordIdentifier.Length != 0
2459		&& inUniqueRecord.RecordIdentifier.Data != NULL)
2460	{
2461		inUniqueRecord.RecordIdentifier.Length = 0;
2462		inDbContext.mDatabaseSession.free(inUniqueRecord.RecordIdentifier.Data);
2463	}
2464	inDbContext.mDatabaseSession.free(&inUniqueRecord);
2465}
2466
2467void
2468AppleDatabase::updateUniqueRecord(DbContext &inDbContext,
2469								  CSSM_DB_RECORDTYPE inTableId,
2470								  const RecordId &inRecordId,
2471								  CSSM_DB_UNIQUE_RECORD &inoutUniqueRecord)
2472{
2473	uint32 *aBuffer = reinterpret_cast<uint32 *>(inoutUniqueRecord.RecordIdentifier.Data);
2474	aBuffer[0] = inTableId;
2475	aBuffer[1] = inRecordId.mRecordNumber;
2476	aBuffer[2] = inRecordId.mCreateVersion;
2477	aBuffer[3] = inRecordId.mRecordVersion;
2478}
2479
2480CSSM_DB_UNIQUE_RECORD_PTR
2481AppleDatabase::createUniqueRecord(DbContext &inDbContext,
2482								  CSSM_DB_RECORDTYPE inTableId,
2483								  const RecordId &inRecordId)
2484{
2485	CSSM_DB_UNIQUE_RECORD_PTR aUniqueRecord =
2486		inDbContext.mDatabaseSession.alloc<CSSM_DB_UNIQUE_RECORD>();
2487	memset(aUniqueRecord, 0, sizeof(*aUniqueRecord));
2488	aUniqueRecord->RecordIdentifier.Length = sizeof(uint32) * 4;
2489	try
2490	{
2491		aUniqueRecord->RecordIdentifier.Data =
2492			inDbContext.mDatabaseSession.alloc<uint8>(sizeof(uint32) * 4);
2493		updateUniqueRecord(inDbContext, inTableId, inRecordId, *aUniqueRecord);
2494	}
2495	catch(...)
2496	{
2497		inDbContext.mDatabaseSession.free(aUniqueRecord);
2498		throw;
2499	}
2500
2501	return aUniqueRecord;
2502}
2503
2504const RecordId
2505AppleDatabase::parseUniqueRecord(const CSSM_DB_UNIQUE_RECORD &inUniqueRecord,
2506								 CSSM_DB_RECORDTYPE &outTableId)
2507{
2508	if (inUniqueRecord.RecordIdentifier.Length != sizeof(uint32) * 4)
2509		CssmError::throwMe(CSSMERR_DL_INVALID_RECORD_UID);
2510
2511	uint32 *aBuffer = reinterpret_cast<uint32 *>(inUniqueRecord.RecordIdentifier.Data);
2512	outTableId = aBuffer[0];
2513	return RecordId(aBuffer[1], aBuffer[2], aBuffer[3]);
2514}
2515
2516void
2517AppleDatabase::passThrough(DbContext &dbContext,
2518						   uint32 passThroughId,
2519						   const void *inputParams,
2520						   void **outputParams)
2521{
2522	switch (passThroughId)
2523	{
2524	case CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT:
2525		{
2526			AppleDbContext &dbc = safer_cast<AppleDbContext &>(dbContext);
2527			// Return the old state of the autoCommit flag if requested
2528			if (outputParams)
2529				*reinterpret_cast<CSSM_BOOL *>(outputParams) = dbc.autoCommit();
2530			dbc.autoCommit(inputParams ? CSSM_TRUE : CSSM_FALSE);
2531		}
2532		break;
2533
2534	case CSSM_APPLEFILEDL_COMMIT:
2535		mDbModifier.commit();
2536		break;
2537
2538	case CSSM_APPLEFILEDL_ROLLBACK:
2539		mDbModifier.rollback();
2540		break;
2541
2542	case CSSM_APPLECSPDL_DB_RELATION_EXISTS:
2543	{
2544		CSSM_BOOL returnValue;
2545
2546		CSSM_DB_RECORDTYPE recordType = *(CSSM_DB_RECORDTYPE*) inputParams;
2547		if (recordType == CSSM_DL_DB_RECORD_ANY || recordType == CSSM_DL_DB_RECORD_ALL_KEYS)
2548		{
2549			returnValue = CSSM_TRUE;
2550		}
2551		else
2552		{
2553			returnValue = mDbModifier.hasTable(recordType);
2554		}
2555
2556		*(CSSM_BOOL*) outputParams = returnValue;
2557		break;
2558	}
2559
2560	default:
2561		CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED);
2562		break;
2563	}
2564}
2565