1/*
2 * Copyright (c) 2000-2001, 2003 Apple Computer, 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
723//
724// Metadata
725//
726
727// Attribute definitions
728
729static const CSSM_DB_ATTRIBUTE_INFO RelationID =
730{
731    CSSM_DB_ATTRIBUTE_NAME_AS_STRING,
732    {(char*) "RelationID"},
733    CSSM_DB_ATTRIBUTE_FORMAT_UINT32
734};
735static const CSSM_DB_ATTRIBUTE_INFO RelationName =
736{
737    CSSM_DB_ATTRIBUTE_NAME_AS_STRING,
738    {(char*) "RelationName"},
739    CSSM_DB_ATTRIBUTE_FORMAT_STRING
740};
741static const CSSM_DB_ATTRIBUTE_INFO AttributeID =
742{
743    CSSM_DB_ATTRIBUTE_NAME_AS_STRING,
744    {(char*) "AttributeID"},
745    CSSM_DB_ATTRIBUTE_FORMAT_UINT32
746};
747static const CSSM_DB_ATTRIBUTE_INFO AttributeNameFormat =
748{
749    CSSM_DB_ATTRIBUTE_NAME_AS_STRING,
750    {(char*) "AttributeNameFormat"},
751    CSSM_DB_ATTRIBUTE_FORMAT_UINT32
752};
753static const CSSM_DB_ATTRIBUTE_INFO AttributeName =
754{
755    CSSM_DB_ATTRIBUTE_NAME_AS_STRING,
756    {(char*) "AttributeName"},
757    CSSM_DB_ATTRIBUTE_FORMAT_STRING
758};
759static const CSSM_DB_ATTRIBUTE_INFO AttributeNameID =
760{
761    CSSM_DB_ATTRIBUTE_NAME_AS_STRING,
762    {(char*) "AttributeNameID"},
763    CSSM_DB_ATTRIBUTE_FORMAT_BLOB
764};
765static const CSSM_DB_ATTRIBUTE_INFO AttributeFormat =
766{
767    CSSM_DB_ATTRIBUTE_NAME_AS_STRING,
768    {(char*) "AttributeFormat"},
769    CSSM_DB_ATTRIBUTE_FORMAT_UINT32
770};
771static const CSSM_DB_ATTRIBUTE_INFO IndexID =
772{
773    CSSM_DB_ATTRIBUTE_NAME_AS_STRING,
774    {(char*) "IndexID"},
775    CSSM_DB_ATTRIBUTE_FORMAT_UINT32
776};
777static const CSSM_DB_ATTRIBUTE_INFO IndexType =
778{
779    CSSM_DB_ATTRIBUTE_NAME_AS_STRING,
780    {(char*) "IndexType"},
781    CSSM_DB_ATTRIBUTE_FORMAT_UINT32
782};
783static const CSSM_DB_ATTRIBUTE_INFO IndexedDataLocation =
784{
785    CSSM_DB_ATTRIBUTE_NAME_AS_STRING,
786    {(char*) "IndexedDataLocation"},
787    CSSM_DB_ATTRIBUTE_FORMAT_UINT32
788};
789static const CSSM_DB_ATTRIBUTE_INFO ModuleID =
790{
791    CSSM_DB_ATTRIBUTE_NAME_AS_STRING,
792    {(char*) "ModuleID"},
793    CSSM_DB_ATTRIBUTE_FORMAT_BLOB
794};
795static const CSSM_DB_ATTRIBUTE_INFO AddinVersion =
796{
797    CSSM_DB_ATTRIBUTE_NAME_AS_STRING,
798    {(char*) "AddinVersion"},
799    CSSM_DB_ATTRIBUTE_FORMAT_STRING
800};
801static const CSSM_DB_ATTRIBUTE_INFO SSID =
802{
803    CSSM_DB_ATTRIBUTE_NAME_AS_STRING,
804    {(char*) "SSID"},
805    CSSM_DB_ATTRIBUTE_FORMAT_UINT32
806};
807static const CSSM_DB_ATTRIBUTE_INFO SubserviceType =
808{
809    CSSM_DB_ATTRIBUTE_NAME_AS_STRING,
810    {(char*) "SubserviceType"},
811    CSSM_DB_ATTRIBUTE_FORMAT_UINT32
812};
813
814#define ATTRIBUTE(type, name) \
815	{ CSSM_DB_ATTRIBUTE_NAME_AS_STRING, { (char*) #name }, CSSM_DB_ATTRIBUTE_FORMAT_ ## type }
816
817static const CSSM_DB_ATTRIBUTE_INFO AttrSchemaRelations[] =
818{
819	//RelationID, RelationName
820	ATTRIBUTE(UINT32, RelationID),
821	ATTRIBUTE(STRING, RelationName)
822};
823
824static const CSSM_DB_ATTRIBUTE_INFO AttrSchemaAttributes[] =
825{
826	//RelationID, AttributeID,
827    //AttributeNameFormat, AttributeName, AttributeNameID,
828    //AttributeFormat
829	ATTRIBUTE(UINT32, RelationID),
830	ATTRIBUTE(UINT32, AttributeID),
831	ATTRIBUTE(UINT32, AttributeNameFormat),
832	ATTRIBUTE(STRING, AttributeName),
833	ATTRIBUTE(BLOB, AttributeNameID),
834	ATTRIBUTE(UINT32, AttributeFormat)
835};
836
837static const CSSM_DB_ATTRIBUTE_INFO AttrSchemaIndexes[] =
838{
839	ATTRIBUTE(UINT32, RelationID),
840	ATTRIBUTE(UINT32, IndexID),
841	ATTRIBUTE(UINT32, AttributeID),
842	ATTRIBUTE(UINT32, IndexType),
843	ATTRIBUTE(UINT32, IndexedDataLocation)
844    //RelationID, IndexID, AttributeID,
845    //IndexType, IndexedDataLocation
846};
847
848static const CSSM_DB_ATTRIBUTE_INFO AttrSchemaParsingModule[] =
849{
850	ATTRIBUTE(UINT32, RelationID),
851	ATTRIBUTE(UINT32, AttributeID),
852	ATTRIBUTE(BLOB, ModuleID),
853	ATTRIBUTE(STRING, AddinVersion),
854	ATTRIBUTE(UINT32, SSID),
855	ATTRIBUTE(UINT32, SubserviceType)
856    //RelationID, AttributeID,
857    //ModuleID, AddinVersion, SSID, SubserviceType
858};
859
860#undef ATTRIBUTE
861
862//
863// DbVersion
864//
865DbVersion::DbVersion(const AppleDatabase &db, const RefPointer <AtomicBufferedFile> &inAtomicBufferedFile) :
866	mDatabase(reinterpret_cast<const uint8 *>(NULL), 0),
867	mDb(db),
868	mBufferedFile(inAtomicBufferedFile)
869{
870	off_t aLength = mBufferedFile->length();
871	off_t bytesRead = 0;
872	const uint8 *ptr = mBufferedFile->read(0, aLength, bytesRead);
873	mBufferedFile->close();
874	mDatabase = ReadSection(ptr, (size_t)bytesRead);
875	open();
876}
877
878DbVersion::~DbVersion()
879{
880	try
881	{
882		for_each_map_delete(mTableMap.begin(), mTableMap.end());
883	}
884	catch(...) {}
885}
886
887void
888DbVersion::open()
889{
890	try
891	{
892		// This is the oposite of DbModifier::commit()
893		mVersionId = mDatabase[mDatabase.size() - AtomSize];
894
895		const ReadSection aHeaderSection = mDatabase.subsection(HeaderOffset,
896																HeaderSize);
897		if (aHeaderSection.at(OffsetMagic) != HeaderMagic)
898			CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
899
900		// We currently only support one version.  If we support additional
901		// file format versions in the future fix this.
902		uint32 aVersion = aHeaderSection.at(OffsetVersion);
903		if (aVersion != HeaderVersion)
904			CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
905
906		//const ReadSection anAuthSection =
907		//	mDatabase.subsection(HeaderOffset + aHeaderSection.at(OffsetAuthOffset));
908		// XXX Do something with anAuthSection.
909
910		uint32 aSchemaOffset = aHeaderSection.at(OffsetSchemaOffset);
911		const ReadSection aSchemaSection =
912			mDatabase.subsection(HeaderOffset + aSchemaOffset);
913
914		uint32 aSchemaSize = aSchemaSection[OffsetSchemaSize];
915		// Make sure that the given range exists.
916		aSchemaSection.subsection(0, aSchemaSize);
917		uint32 aTableCount = aSchemaSection[OffsetTablesCount];
918
919		// Assert that the size of this section is big enough.
920		if (aSchemaSize < OffsetTables + AtomSize * aTableCount)
921			CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
922
923		for (uint32 aTableNumber = 0; aTableNumber < aTableCount;
924			 aTableNumber++)
925		{
926			uint32 aTableOffset = aSchemaSection.at(OffsetTables + AtomSize
927													* aTableNumber);
928			// XXX Set the size boundary on aTableSection.
929			const ReadSection aTableSection =
930				aSchemaSection.subsection(aTableOffset);
931			auto_ptr<Table> aTable(new Table(aTableSection));
932			Table::Id aTableId = aTable->getMetaRecord().dataRecordType();
933			mTableMap.insert(TableMap::value_type(aTableId, aTable.get()));
934			aTable.release();
935		}
936
937		// Fill in the schema for the meta tables.
938
939		findTable(mDb.schemaRelations.DataRecordType).getMetaRecord().
940			setRecordAttributeInfo(mDb.schemaRelations);
941		findTable(mDb.schemaIndexes.DataRecordType).getMetaRecord().
942			setRecordAttributeInfo(mDb.schemaIndexes);
943		findTable(mDb.schemaParsingModule.DataRecordType).getMetaRecord().
944			setRecordAttributeInfo(mDb.schemaParsingModule);
945
946		// OK, we have created all the tables in the tableMap.  Now
947		// lets read the schema and proccess it accordingly.
948		// Iterate over all schema records.
949		Table &aTable = findTable(mDb.schemaAttributes.DataRecordType);
950		aTable.getMetaRecord().setRecordAttributeInfo(mDb.schemaAttributes);
951		uint32 aRecordsCount = aTable.getRecordsCount();
952		ReadSection aRecordsSection = aTable.getRecordsSection();
953		uint32 aReadOffset = 0;
954		const MetaRecord &aMetaRecord = aTable.getMetaRecord();
955
956		CSSM_DB_ATTRIBUTE_DATA aRelationIDData =
957		{
958			RelationID,
959			0,
960			NULL
961		};
962		CSSM_DB_ATTRIBUTE_DATA aAttributeIDData =
963		{
964			AttributeID,
965			0,
966			NULL
967		};
968		CSSM_DB_ATTRIBUTE_DATA aAttributeNameFormatData =
969		{
970			AttributeNameFormat,
971			0,
972			NULL
973		};
974		CSSM_DB_ATTRIBUTE_DATA aAttributeNameData =
975		{
976			AttributeName,
977			0,
978			NULL
979		};
980		CSSM_DB_ATTRIBUTE_DATA aAttributeNameIDData =
981		{
982			AttributeNameID,
983			0,
984			NULL
985		};
986		CSSM_DB_ATTRIBUTE_DATA aAttributeFormatData =
987		{
988			AttributeFormat,
989			0,
990			NULL
991		};
992		CSSM_DB_ATTRIBUTE_DATA aRecordAttributes[] =
993		{
994			aRelationIDData,
995			aAttributeIDData,
996			aAttributeNameFormatData,
997			aAttributeNameData,
998			aAttributeNameIDData,
999			aAttributeFormatData
1000		};
1001		CSSM_DB_RECORD_ATTRIBUTE_DATA aRecordAttributeData =
1002		{
1003			aMetaRecord.dataRecordType(),
1004			0,
1005			sizeof(aRecordAttributes) / sizeof(CSSM_DB_ATTRIBUTE_DATA),
1006			aRecordAttributes
1007		};
1008		CssmDbRecordAttributeData &aRecordData = CssmDbRecordAttributeData::overlay(aRecordAttributeData);
1009
1010		TrackingAllocator recordAllocator(Allocator::standard());
1011		for (uint32 aRecord = 0; aRecord != aRecordsCount; aRecord++)
1012		{
1013			ReadSection aRecordSection = MetaRecord::readSection(aRecordsSection, aReadOffset);
1014			uint32 aRecordSize = aRecordSection.size();
1015			aReadOffset += aRecordSize;
1016			aMetaRecord.unpackRecord(aRecordSection, recordAllocator,
1017										&aRecordAttributeData, NULL, 0);
1018			// Create the attribute coresponding to this entry
1019			if (aRecordData[0].size() != 1 || aRecordData[0].format() != CSSM_DB_ATTRIBUTE_FORMAT_UINT32)
1020				CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
1021			uint32 aRelationId = aRecordData[0];
1022
1023			// Skip the schema relations for the meta tables themselves.
1024			// FIXME: this hard-wires the meta-table relation IDs to be
1025			// within {CSSM_DB_RECORDTYPE_SCHEMA_START...
1026			// CSSM_DB_RECORDTYPE_SCHEMA_END} (which is {0..4}).
1027			// Bogus - the MDS schema relation IDs start at
1028			// CSSM_DB_RELATIONID_MDS_START which is 0x40000000.
1029			// Ref. Radar 2817921.
1030			if (CSSM_DB_RECORDTYPE_SCHEMA_START <= aRelationId && aRelationId < CSSM_DB_RECORDTYPE_SCHEMA_END)
1031				continue;
1032
1033			// Get the MetaRecord corresponding to the specified RelationId
1034			MetaRecord &aMetaRecord = findTable(aRelationId).getMetaRecord();
1035
1036			if (aRecordData[1].size() != 1
1037				|| aRecordData[1].format() != CSSM_DB_ATTRIBUTE_FORMAT_UINT32
1038				|| aRecordData[2].size() != 1
1039				|| aRecordData[2].format() != CSSM_DB_ATTRIBUTE_FORMAT_UINT32
1040				|| aRecordData[5].size() != 1
1041				|| aRecordData[5].format() != CSSM_DB_ATTRIBUTE_FORMAT_UINT32)
1042				CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
1043
1044			uint32 anAttributeId = aRecordData[1];
1045			uint32 anAttributeNameFormat = aRecordData[2];
1046			uint32 anAttributeFormat = aRecordData[5];
1047			auto_ptr<string> aName;
1048			const CssmData *aNameID = NULL;
1049
1050			if (aRecordData[3].size() == 1)
1051			{
1052				if (aRecordData[3].format() != CSSM_DB_ATTRIBUTE_FORMAT_STRING)
1053					CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
1054
1055				auto_ptr<string> aName2(new string(static_cast<string>(aRecordData[3])));
1056				aName = aName2;
1057			}
1058
1059			if (aRecordData[4].size() == 1)
1060			{
1061				if (aRecordData[4].format() != CSSM_DB_ATTRIBUTE_FORMAT_BLOB)
1062					CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
1063
1064									// @@@ Invoking conversion operator to CssmData & on aRecordData[4]
1065									// And taking address of result.
1066				aNameID = &static_cast<const CssmData &>(aRecordData[4]);
1067			}
1068
1069			// Make sure that the attribute specified by anAttributeNameFormat is present.
1070			switch (anAttributeNameFormat)
1071			{
1072			case CSSM_DB_ATTRIBUTE_NAME_AS_STRING:
1073				if (aRecordData[3].size() != 1)
1074					CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
1075				break;
1076			case CSSM_DB_ATTRIBUTE_NAME_AS_OID:
1077				if (aRecordData[4].size() != 1)
1078					CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
1079				break;
1080			case CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER:
1081				break;
1082			default:
1083				CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
1084			}
1085
1086			// Create the attribute
1087			aMetaRecord.createAttribute(aName.get(), aNameID, anAttributeId, anAttributeFormat);
1088        }
1089
1090		// initialize the indexes associated with each table
1091		{
1092			TableMap::iterator it;
1093			for (it = mTableMap.begin(); it != mTableMap.end(); it++)
1094				it->second->readIndexSection();
1095		}
1096	}
1097	catch(...)
1098	{
1099		for_each_map_delete(mTableMap.begin(), mTableMap.end());
1100		mTableMap.clear();
1101		throw;
1102	}
1103}
1104
1105const RecordId
1106DbVersion::getRecord(Table::Id inTableId, const RecordId &inRecordId,
1107							CSSM_DB_RECORD_ATTRIBUTE_DATA *inoutAttributes,
1108							CssmData *inoutData,
1109							Allocator &inAllocator) const
1110{
1111	return findTable(inTableId).getRecord(inRecordId, inoutAttributes,
1112										  inoutData, inAllocator);
1113}
1114
1115Cursor *
1116DbVersion::createCursor(const CSSM_QUERY *inQuery) const
1117{
1118	// XXX We should add support for these special query types
1119	// By Creating a Cursor that iterates over multiple tables
1120	if (!inQuery || inQuery->RecordType == CSSM_DL_DB_RECORD_ANY
1121		|| inQuery->RecordType == CSSM_DL_DB_RECORD_ALL_KEYS)
1122	{
1123		return new MultiCursor(inQuery, *this);
1124	}
1125
1126	return findTable(inQuery->RecordType).createCursor(inQuery, *this);
1127}
1128
1129bool DbVersion::hasTable(Table::Id inTableId) const
1130{
1131    TableMap::const_iterator it = mTableMap.find(inTableId);
1132	return it != mTableMap.end();
1133}
1134
1135const Table &
1136DbVersion::findTable(Table::Id inTableId) const
1137{
1138    TableMap::const_iterator it = mTableMap.find(inTableId);
1139    if (it == mTableMap.end())
1140		CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE);
1141	return *it->second;
1142}
1143
1144Table &
1145DbVersion::findTable(Table::Id inTableId)
1146{
1147    TableMap::iterator it = mTableMap.find(inTableId);
1148    if (it == mTableMap.end())
1149		CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
1150	return *it->second;
1151}
1152
1153//
1154// Cursor implemetation
1155//
1156Cursor::Cursor()
1157{
1158}
1159
1160Cursor::Cursor(const DbVersion &inDbVersion) : mDbVersion(&inDbVersion)
1161{
1162}
1163
1164Cursor::~Cursor()
1165{
1166}
1167
1168bool
1169Cursor::next(Table::Id &outTableId,
1170			 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR outAttributes,
1171			 CssmData *outData,
1172			 Allocator &inAllocator,
1173			 RecordId &recordId)
1174{
1175	return false;
1176}
1177
1178//
1179// LinearCursor implemetation
1180//
1181LinearCursor::LinearCursor(const CSSM_QUERY *inQuery, const DbVersion &inDbVersion,
1182						   const Table &inTable) :
1183    Cursor(inDbVersion),
1184    mRecordsCount(inTable.getRecordsCount()),
1185    mRecord(0),
1186    mRecordsSection(inTable.getRecordsSection()),
1187    mReadOffset(0),
1188    mMetaRecord(inTable.getMetaRecord())
1189{
1190	if (inQuery)
1191	{
1192	    mConjunctive = inQuery->Conjunctive;
1193	    mQueryFlags = inQuery->QueryFlags;
1194	    // XXX Do something with inQuery->QueryLimits?
1195	    uint32 aPredicatesCount = inQuery->NumSelectionPredicates;
1196	    mPredicates.resize(aPredicatesCount);
1197		try
1198		{
1199			for (uint32 anIndex = 0; anIndex < aPredicatesCount; anIndex++)
1200			{
1201				CSSM_SELECTION_PREDICATE &aPredicate = inQuery->SelectionPredicate[anIndex];
1202				mPredicates[anIndex] = new SelectionPredicate(mMetaRecord, aPredicate);
1203			}
1204		}
1205		catch(...)
1206		{
1207			for_each_delete(mPredicates.begin(), mPredicates.end());
1208			throw;
1209		}
1210	}
1211}
1212
1213LinearCursor::~LinearCursor()
1214{
1215	for_each_delete(mPredicates.begin(), mPredicates.end());
1216}
1217
1218bool
1219LinearCursor::next(Table::Id &outTableId,
1220				   CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes,
1221				   CssmData *inoutData, Allocator &inAllocator, RecordId &recordId)
1222{
1223	while (mRecord++ < mRecordsCount)
1224	{
1225		ReadSection aRecordSection = MetaRecord::readSection(mRecordsSection, mReadOffset);
1226		uint32 aRecordSize = aRecordSection.size();
1227		mReadOffset += aRecordSize;
1228
1229        PredicateVector::const_iterator anIt = mPredicates.begin();
1230        PredicateVector::const_iterator anEnd = mPredicates.end();
1231		bool aMatch;
1232		if (anIt == anEnd)
1233		{
1234			// If there are no predicates we have a match.
1235			aMatch = true;
1236		}
1237		else if (mConjunctive == CSSM_DB_OR)
1238		{
1239			// If mConjunctive is OR, the first predicate that returns
1240			// true indicates a match. Dropthough means no match
1241			aMatch = false;
1242			for (; anIt != anEnd; anIt++)
1243			{
1244				if ((*anIt)->evaluate(aRecordSection))
1245				{
1246					aMatch = true;
1247                    break;
1248				}
1249			}
1250		}
1251		else if (mConjunctive == CSSM_DB_AND || mConjunctive == CSSM_DB_NONE)
1252		{
1253			// If mConjunctive is AND (or NONE), the first predicate that returns
1254			// false indicates a mismatch. Dropthough means a match
1255			aMatch = true;
1256			for (; anIt != anEnd; anIt++)
1257			{
1258				if (!(*anIt)->evaluate(aRecordSection))
1259				{
1260					aMatch = false;
1261                    break;
1262				}
1263			}
1264		}
1265		else
1266		{
1267			// XXX Should be CSSMERR_DL_INVALID_QUERY (or CSSMERR_DL_INVALID_CONJUNTIVE).
1268			CssmError::throwMe(CSSMERR_DL_UNSUPPORTED_QUERY);
1269		}
1270
1271        if (aMatch)
1272        {
1273            // Get the actual record.
1274            mMetaRecord.unpackRecord(aRecordSection, inAllocator,
1275									 inoutAttributes, inoutData,
1276									 mQueryFlags);
1277			outTableId = mMetaRecord.dataRecordType();
1278			recordId = MetaRecord::unpackRecordId(aRecordSection);
1279			return true;
1280        }
1281    }
1282
1283	return false;
1284}
1285
1286//
1287// IndexCursor
1288//
1289
1290IndexCursor::IndexCursor(DbQueryKey *queryKey, const DbVersion &inDbVersion,
1291	const Table &table, const DbConstIndex *index) :
1292	Cursor(inDbVersion), mQueryKey(queryKey), mTable(table), mIndex(index)
1293{
1294	index->performQuery(*queryKey, mBegin, mEnd);
1295}
1296
1297IndexCursor::~IndexCursor()
1298{
1299	// the query key will be deleted automatically, since it's an auto_ptr
1300}
1301
1302bool
1303IndexCursor::next(Table::Id &outTableId,
1304	CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR outAttributes,
1305	CssmData *outData,
1306	Allocator &inAllocator, RecordId &recordId)
1307{
1308	if (mBegin == mEnd)
1309		return false;
1310
1311	ReadSection rs = mIndex->getRecordSection(mBegin++);
1312	const MetaRecord &metaRecord = mTable.getMetaRecord();
1313
1314	outTableId = metaRecord.dataRecordType();
1315	metaRecord.unpackRecord(rs, inAllocator, outAttributes, outData, 0);
1316
1317	recordId = MetaRecord::unpackRecordId(rs);
1318	return true;
1319}
1320
1321//
1322// MultiCursor
1323//
1324MultiCursor::MultiCursor(const CSSM_QUERY *inQuery, const DbVersion &inDbVersion) :
1325    Cursor(inDbVersion), mTableIterator(inDbVersion.begin())
1326{
1327	if (inQuery)
1328		mQuery.reset(new CssmAutoQuery(*inQuery));
1329	else
1330	{
1331		mQuery.reset(new CssmAutoQuery());
1332		mQuery->recordType(CSSM_DL_DB_RECORD_ANY);
1333	}
1334}
1335
1336MultiCursor::~MultiCursor()
1337{
1338}
1339
1340bool
1341MultiCursor::next(Table::Id &outTableId,
1342				  CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes,
1343				  CssmData *inoutData, Allocator &inAllocator, RecordId &recordId)
1344{
1345	for (;;)
1346	{
1347		if (!mCursor.get())
1348		{
1349			if (mTableIterator == mDbVersion->end())
1350				return false;
1351
1352			const Table &aTable = *mTableIterator++;
1353			if (!aTable.matchesTableId(mQuery->recordType()))
1354				continue;
1355
1356			mCursor.reset(aTable.createCursor(mQuery.get(), *mDbVersion));
1357		}
1358
1359		if (mCursor->next(outTableId, inoutAttributes, inoutData, inAllocator, recordId))
1360			return true;
1361
1362		mCursor.reset(NULL);
1363	}
1364}
1365
1366
1367//
1368// DbModifier
1369//
1370DbModifier::DbModifier(AtomicFile &inAtomicFile, const AppleDatabase &db) :
1371	Metadata(),
1372	mDbVersion(),
1373    mAtomicFile(inAtomicFile),
1374	mDb(db)
1375{
1376}
1377
1378DbModifier::~DbModifier()
1379{
1380    try
1381    {
1382		for_each_map_delete(mModifiedTableMap.begin(), mModifiedTableMap.end());
1383		// mAtomicTempFile will do automatic rollback on destruction.
1384    }
1385    catch(...) {}
1386}
1387
1388const RefPointer<const DbVersion>
1389DbModifier::getDbVersion(bool force)
1390{
1391    StLock<Mutex> _(mDbVersionLock);
1392
1393    /* Initialize the shared memory file change mechanism */
1394    pthread_once(&gCommonInitMutex, initCommon);
1395
1396    /* If we don't have a mDbVersion yet, or we are force to re-read the file
1397       before a write transaction, or we have received any notifications after
1398       the last time we read the file, or more than kForceReReadTime seconds
1399       have passed since the last time we read the file, we open the file and
1400       check if it has changed. */
1401    if (!mDbVersion ||
1402        force ||
1403		gSegment == NULL ||
1404        mNotifyCount != *gSegment ||
1405        CFAbsoluteTimeGetCurrent() > mDbLastRead + kForceReReadTime)
1406    {
1407        RefPointer <AtomicBufferedFile> atomicBufferedFile(mAtomicFile.read());
1408        off_t length = atomicBufferedFile->open();
1409        /* Record the number of notifications we've seen and when we last
1410           opened the file.  */
1411		if (gSegment != NULL)
1412		{
1413			mNotifyCount = *gSegment;
1414		}
1415
1416        mDbLastRead = CFAbsoluteTimeGetCurrent();
1417
1418        /* If we already have a mDbVersion, let's check if we can reuse it. */
1419        if (mDbVersion)
1420        {
1421            if (length < AtomSize)
1422                CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
1423
1424            off_t bytesRead = 0;
1425            const uint8 *ptr = atomicBufferedFile->read(length - AtomSize,
1426                AtomSize, bytesRead);
1427            ReadSection aVersionSection(ptr, (size_t)bytesRead);
1428            uint32 aVersionId = aVersionSection[0];
1429
1430            /* If the version stamp hasn't changed the old mDbVersion is still
1431               current. */
1432            if (aVersionId == mDbVersion->getVersionId())
1433                return mDbVersion;
1434        }
1435
1436	mDbVersion = new DbVersion(mDb, atomicBufferedFile);
1437    }
1438
1439    return mDbVersion;
1440}
1441
1442void
1443DbModifier::createDatabase(const CSSM_DBINFO &inDbInfo,
1444						   const CSSM_ACL_ENTRY_INPUT *inInitialAclEntry,
1445						   mode_t mode)
1446{
1447	// XXX This needs better locking.  There is a possible race condition between
1448	// two concurrent creators.  Or a writer/creator or a close/create etc.
1449	if (mAtomicTempFile || !mModifiedTableMap.empty())
1450		CssmError::throwMe(CSSMERR_DL_DATASTORE_ALREADY_EXISTS);
1451
1452    mAtomicTempFile = mAtomicFile.create(mode);
1453	// Set mVersionId to one since this is the first version of the database.
1454	mVersionId = 1;
1455
1456	// we need to create the meta tables first, because inserting tables
1457	// (including the meta tables themselves) relies on them being there
1458    createTable(new MetaRecord(mDb.schemaRelations));
1459    createTable(new MetaRecord(mDb.schemaAttributes));
1460    createTable(new MetaRecord(mDb.schemaIndexes));
1461    createTable(new MetaRecord(mDb.schemaParsingModule));
1462
1463	// now add the meta-tables' schema to the meta tables themselves
1464	insertTableSchema(mDb.schemaRelations);
1465	insertTableSchema(mDb.schemaAttributes);
1466	insertTableSchema(mDb.schemaIndexes);
1467	insertTableSchema(mDb.schemaParsingModule);
1468
1469    if (inInitialAclEntry != NULL)
1470    {
1471        //createACL(*inInitialAclEntry);
1472    }
1473
1474    if (inDbInfo.NumberOfRecordTypes == 0)
1475        return;
1476    if (inDbInfo.RecordAttributeNames == NULL)
1477        CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE);
1478    if (inDbInfo.RecordIndexes == NULL)
1479        CssmError::throwMe(CSSMERR_DL_INVALID_RECORD_INDEX);
1480    if (inDbInfo.DefaultParsingModules == NULL)
1481        CssmError::throwMe(CSSMERR_DL_INVALID_PARSING_MODULE);
1482
1483    for (uint32 anIndex = 0; anIndex < inDbInfo.NumberOfRecordTypes; anIndex++)
1484    {
1485        insertTable(CssmDbRecordAttributeInfo::overlay(inDbInfo.RecordAttributeNames[anIndex]),
1486					&inDbInfo.RecordIndexes[anIndex],
1487					&inDbInfo.DefaultParsingModules[anIndex]);
1488    }
1489}
1490
1491void DbModifier::openDatabase()
1492{
1493	// No need to do anything on open if we are already writing the database.
1494	if (!mAtomicTempFile)
1495		getDbVersion(false);
1496}
1497
1498void DbModifier::closeDatabase()
1499{
1500	commit(); // XXX Requires write lock.
1501	StLock<Mutex> _(mDbVersionLock);
1502	mDbVersion = NULL;
1503}
1504
1505void DbModifier::deleteDatabase()
1506{
1507	bool isDirty = mAtomicTempFile;
1508	rollback(); // XXX Requires write lock.
1509	StLock<Mutex> _(mDbVersionLock);
1510
1511	// Clean up mModifiedTableMap in case this object gets reused again for
1512	// a new create.
1513	for_each_map_delete(mModifiedTableMap.begin(), mModifiedTableMap.end());
1514	mModifiedTableMap.clear();
1515
1516	// If the database was dirty and we had no mDbVersion yet then rollback()
1517	// would have deleted the db.
1518	if (!isDirty || mDbVersion)
1519	{
1520		mDbVersion = NULL;
1521		mAtomicFile.performDelete();
1522	}
1523}
1524
1525void
1526DbModifier::modifyDatabase()
1527{
1528	if (mAtomicTempFile)
1529		return;
1530
1531	try
1532	{
1533		mAtomicTempFile = mAtomicFile.write();
1534		// Now we are holding the write lock make sure we get the latest greatest version of the db.
1535		// Also set mVersionId to one more that that of the old database.
1536		mVersionId = getDbVersion(true)->getVersionId() + 1;
1537
1538		// Never make a database with mVersionId 0 since it makes bad things happen to Jaguar and older systems
1539		if (mVersionId == 0)
1540			mVersionId = 1;
1541
1542		// Remove all old modified tables
1543		for_each_map_delete(mModifiedTableMap.begin(), mModifiedTableMap.end());
1544		mModifiedTableMap.clear();
1545
1546		// Setup the new tables
1547		DbVersion::TableMap::const_iterator anIt =
1548			mDbVersion->mTableMap.begin();
1549		DbVersion::TableMap::const_iterator anEnd =
1550			mDbVersion->mTableMap.end();
1551		for (; anIt != anEnd; ++anIt)
1552		{
1553			auto_ptr<ModifiedTable> aTable(new ModifiedTable(anIt->second));
1554			mModifiedTableMap.insert(ModifiedTableMap::value_type(anIt->first,
1555																  aTable.get()));
1556			aTable.release();
1557		}
1558	}
1559	catch(...)
1560	{
1561		for_each_map_delete(mModifiedTableMap.begin(), mModifiedTableMap.end());
1562		mModifiedTableMap.clear();
1563		rollback();
1564		throw;
1565	}
1566}
1567
1568void
1569DbModifier::deleteRecord(Table::Id inTableId, const RecordId &inRecordId)
1570{
1571	modifyDatabase();
1572	findTable(inTableId).deleteRecord(inRecordId);
1573}
1574
1575const RecordId
1576DbModifier::insertRecord(Table::Id inTableId,
1577						 const CSSM_DB_RECORD_ATTRIBUTE_DATA *inAttributes,
1578						 const CssmData *inData)
1579{
1580	modifyDatabase();
1581	return findTable(inTableId).insertRecord(mVersionId, inAttributes, inData);
1582}
1583
1584const RecordId
1585DbModifier::updateRecord(Table::Id inTableId, const RecordId &inRecordId,
1586						 const CSSM_DB_RECORD_ATTRIBUTE_DATA *inAttributes,
1587						 const CssmData *inData,
1588						 CSSM_DB_MODIFY_MODE inModifyMode)
1589{
1590	modifyDatabase();
1591	return findTable(inTableId).updateRecord(inRecordId, inAttributes, inData, inModifyMode);
1592}
1593
1594// Create a table associated with a given metarecord, and add the table
1595// to the database.
1596
1597ModifiedTable *
1598DbModifier::createTable(MetaRecord *inMetaRecord)
1599{
1600	auto_ptr<MetaRecord> aMetaRecord(inMetaRecord);
1601	auto_ptr<ModifiedTable> aModifiedTable(new ModifiedTable(inMetaRecord));
1602	// Now that aModifiedTable is fully constructed it owns inMetaRecord
1603	aMetaRecord.release();
1604
1605	if (!mModifiedTableMap.insert
1606		(ModifiedTableMap::value_type(inMetaRecord->dataRecordType(),
1607									  aModifiedTable.get())).second)
1608	{
1609		// XXX Should be CSSMERR_DL_DUPLICATE_RECORDTYPE.  Since that
1610		// doesn't exist we report that the metatable's unique index would
1611		// no longer be valid
1612        CssmError::throwMe(CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA);
1613	}
1614
1615	return aModifiedTable.release();
1616}
1617
1618void
1619DbModifier::deleteTable(Table::Id inTableId)
1620{
1621	modifyDatabase();
1622    // Can't delete schema tables.
1623    if (CSSM_DB_RECORDTYPE_SCHEMA_START <= inTableId
1624		&& inTableId < CSSM_DB_RECORDTYPE_SCHEMA_END)
1625        CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE);
1626
1627	// Find the ModifiedTable and delete it
1628    ModifiedTableMap::iterator it = mModifiedTableMap.find(inTableId);
1629    if (it == mModifiedTableMap.end())
1630        CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE);
1631
1632    delete it->second;
1633    mModifiedTableMap.erase(it);
1634}
1635
1636uint32
1637DbModifier::writeAuthSection(uint32 inSectionOffset)
1638{
1639	WriteSection anAuthSection;
1640
1641    // XXX Put real data into the authsection.
1642	uint32 anOffset = anAuthSection.put(0, 0);
1643	anAuthSection.size(anOffset);
1644
1645	mAtomicTempFile->write(AtomicFile::FromStart, inSectionOffset,
1646					anAuthSection.address(), anAuthSection.size());
1647    return inSectionOffset + anOffset;
1648}
1649
1650uint32
1651DbModifier::writeSchemaSection(uint32 inSectionOffset)
1652{
1653	uint32 aTableCount = (uint32) mModifiedTableMap.size();
1654	WriteSection aTableSection(Allocator::standard(),
1655							   OffsetTables + AtomSize * aTableCount);
1656	// Set aTableSection to the correct size.
1657	aTableSection.size(OffsetTables + AtomSize * aTableCount);
1658	aTableSection.put(OffsetTablesCount, aTableCount);
1659
1660	uint32 anOffset = inSectionOffset + OffsetTables + AtomSize * aTableCount;
1661	ModifiedTableMap::const_iterator anIt = mModifiedTableMap.begin();
1662	ModifiedTableMap::const_iterator anEnd = mModifiedTableMap.end();
1663	for (uint32 aTableNumber = 0; anIt != anEnd; anIt++, aTableNumber++)
1664	{
1665		// Put the offset to the current table relative to the start of
1666		// this section into the tables array
1667		aTableSection.put(OffsetTables + AtomSize * aTableNumber,
1668						  anOffset - inSectionOffset);
1669		anOffset = anIt->second->writeTable(*mAtomicTempFile, anOffset);
1670	}
1671
1672	aTableSection.put(OffsetSchemaSize, anOffset - inSectionOffset);
1673	mAtomicTempFile->write(AtomicFile::FromStart, inSectionOffset,
1674					aTableSection.address(), aTableSection.size());
1675
1676	return anOffset;
1677}
1678
1679void
1680DbModifier::commit()
1681{
1682    if (!mAtomicTempFile)
1683        return;
1684    try
1685    {
1686		WriteSection aHeaderSection(Allocator::standard(), size_t(HeaderSize));
1687		// Set aHeaderSection to the correct size.
1688		aHeaderSection.size(HeaderSize);
1689
1690        // Start writing sections after the header
1691        uint32 anOffset = HeaderOffset + HeaderSize;
1692
1693        // Write auth section
1694		aHeaderSection.put(OffsetAuthOffset, anOffset);
1695        anOffset = writeAuthSection(anOffset);
1696        // Write schema section
1697		aHeaderSection.put(OffsetSchemaOffset, anOffset);
1698        anOffset = writeSchemaSection(anOffset);
1699
1700		// Write out the file header.
1701		aHeaderSection.put(OffsetMagic, HeaderMagic);
1702		aHeaderSection.put(OffsetVersion, HeaderVersion);
1703        mAtomicTempFile->write(AtomicFile::FromStart, HeaderOffset,
1704							   aHeaderSection.address(), aHeaderSection.size());
1705
1706		// Write out the versionId.
1707		WriteSection aVersionSection(Allocator::standard(), size_t(AtomSize));
1708		anOffset = aVersionSection.put(0, mVersionId);
1709		aVersionSection.size(anOffset);
1710
1711        mAtomicTempFile->write(AtomicFile::FromEnd, 0,
1712							   aVersionSection.address(), aVersionSection.size());
1713
1714		mAtomicTempFile->commit();
1715		mAtomicTempFile = NULL;
1716	   /* Initialize the shared memory file change mechanism */
1717	   pthread_once(&gCommonInitMutex, initCommon);
1718
1719	   if (gSegment != NULL)
1720		{
1721			/*
1722				PLEASE NOTE:
1723
1724				The following operation is endian safe because we are not looking
1725				for monotonic increase. I have tested every possible value of
1726				*gSegment, and there is no value for which alternating
1727				big and little endian increments will produce the original value.
1728			*/
1729
1730			OSAtomicIncrement32Barrier (gSegment);
1731		}
1732    }
1733    catch(...)
1734    {
1735		rollback();
1736		throw;
1737    }
1738}
1739
1740void
1741DbModifier::rollback() throw()
1742{
1743	// This will destroy the AtomicTempFile if we have one causing it to rollback.
1744	mAtomicTempFile = NULL;
1745}
1746
1747const RecordId
1748DbModifier::getRecord(Table::Id inTableId, const RecordId &inRecordId,
1749					  CSSM_DB_RECORD_ATTRIBUTE_DATA *inoutAttributes,
1750					  CssmData *inoutData, Allocator &inAllocator)
1751{
1752    if (mAtomicTempFile)
1753	{
1754		// We are in the midst of changing the database.
1755		return findTable(inTableId).getRecord(inRecordId, inoutAttributes,
1756			inoutData, inAllocator);
1757	}
1758	else
1759	{
1760		return getDbVersion(false)->getRecord(inTableId, inRecordId,
1761			inoutAttributes, inoutData, inAllocator);
1762	}
1763}
1764
1765Cursor *
1766DbModifier::createCursor(const CSSM_QUERY *inQuery)
1767{
1768	if (mAtomicTempFile)
1769	{
1770		// We are modifying this database.
1771
1772		// If we have a mDbVersion already then it's a snapshot of the database
1773		// right before the modifications started.  So return a cursor using
1774		// that.
1775		if (mDbVersion)
1776			return mDbVersion->createCursor(inQuery);
1777
1778		// This is a newly created but never commited database.  Return a
1779		// Cursor that will not return any matches.
1780		return new Cursor();
1781	}
1782
1783	// Get the latest and greatest version of the db and create the cursor
1784	// on that.
1785	return getDbVersion(false)->createCursor(inQuery);
1786}
1787
1788// Insert schema records for a new table into the metatables of the database. This gets
1789// called while a database is being created.
1790
1791void
1792DbModifier::insertTableSchema(const CssmDbRecordAttributeInfo &inInfo,
1793	const CSSM_DB_RECORD_INDEX_INFO *inIndexInfo /* = NULL */)
1794{
1795	ModifiedTable &aTable = findTable(inInfo.DataRecordType);
1796	const MetaRecord &aMetaRecord = aTable.getMetaRecord();
1797
1798	CssmAutoDbRecordAttributeData aRecordBuilder(5); // Set capacity to 5 so we don't need to grow
1799
1800	// Create the entry for the SchemaRelations table.
1801	aRecordBuilder.add(RelationID, inInfo.recordType());
1802	aRecordBuilder.add(RelationName, mDb.recordName(inInfo.recordType()));
1803
1804	// Insert the record into the SchemaRelations ModifiedTable
1805    findTable(mDb.schemaRelations.DataRecordType).insertRecord(mVersionId,
1806		&aRecordBuilder, NULL);
1807
1808	ModifiedTable &anAttributeTable = findTable(mDb.schemaAttributes.DataRecordType);
1809    for (uint32 anIndex = 0; anIndex < inInfo.size(); anIndex++)
1810    {
1811		// Create an entry for the SchemaAttributes table.
1812		aRecordBuilder.clear();
1813		aRecordBuilder.add(RelationID, inInfo.recordType());
1814		aRecordBuilder.add(AttributeNameFormat, inInfo.at(anIndex).nameFormat());
1815
1816		uint32 attributeId = aMetaRecord.metaAttribute(inInfo.at(anIndex)).attributeId();
1817
1818        switch (inInfo.at(anIndex).nameFormat())
1819        {
1820            case CSSM_DB_ATTRIBUTE_NAME_AS_STRING:
1821				aRecordBuilder.add(AttributeName, inInfo.at(anIndex).Label.AttributeName);
1822				break;
1823            case CSSM_DB_ATTRIBUTE_NAME_AS_OID:
1824				aRecordBuilder.add(AttributeNameID, inInfo.at(anIndex).Label.AttributeOID);
1825				break;
1826            case CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER:
1827                break;
1828            default:
1829                CssmError::throwMe(CSSMERR_DL_INVALID_FIELD_NAME);
1830		}
1831
1832		aRecordBuilder.add(AttributeID, attributeId);
1833		aRecordBuilder.add(AttributeFormat, inInfo.at(anIndex).format());
1834
1835		// Insert the record into the SchemaAttributes ModifiedTable
1836		anAttributeTable.insertRecord(mVersionId, &aRecordBuilder, NULL);
1837    }
1838
1839	if (inIndexInfo != NULL) {
1840
1841		if (inIndexInfo->DataRecordType != inInfo.DataRecordType &&
1842			inIndexInfo->NumberOfIndexes > 0)
1843			CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE);
1844
1845		ModifiedTable &indexMetaTable = findTable(mDb.schemaIndexes.DataRecordType);
1846		uint32 aNumberOfIndexes = inIndexInfo->NumberOfIndexes;
1847
1848		for (uint32 anIndex = 0; anIndex < aNumberOfIndexes; anIndex++)
1849		{
1850			const CssmDbIndexInfo &thisIndex = CssmDbIndexInfo::overlay(inIndexInfo->IndexInfo[anIndex]);
1851
1852			// make sure the index is supported
1853			if (thisIndex.dataLocation() != CSSM_DB_INDEX_ON_ATTRIBUTE)
1854				CssmError::throwMe(CSSMERR_DL_INVALID_INDEX_INFO);
1855
1856			// assign an index ID: the unique index is ID 0, all others are ID > 0
1857			uint32 indexId;
1858			if (thisIndex.IndexType == CSSM_DB_INDEX_UNIQUE)
1859				indexId = 0;
1860			else
1861				indexId = anIndex + 1;
1862
1863			// figure out the attribute ID
1864			uint32 attributeId =
1865				aMetaRecord.metaAttribute(thisIndex.Info).attributeId();
1866
1867			// Create an entry for the SchemaIndexes table.
1868			aRecordBuilder.clear();
1869			aRecordBuilder.add(RelationID, inInfo.DataRecordType);
1870			aRecordBuilder.add(IndexID, indexId);
1871			aRecordBuilder.add(AttributeID, attributeId);
1872			aRecordBuilder.add(IndexType, thisIndex.IndexType);
1873			aRecordBuilder.add(IndexedDataLocation, thisIndex.IndexedDataLocation);
1874
1875			// Insert the record into the SchemaIndexes ModifiedTable
1876			indexMetaTable.insertRecord(mVersionId, &aRecordBuilder, NULL);
1877
1878			// update the table's index objects
1879			DbMutableIndex &index = aTable.findIndex(indexId, aMetaRecord, indexId == 0);
1880			index.appendAttribute(attributeId);
1881		}
1882	}
1883}
1884
1885// Insert a new table. The attribute info is required; the index and parsing module
1886// descriptions are optional. This version gets called during the creation of a
1887// database.
1888
1889void
1890DbModifier::insertTable(const CssmDbRecordAttributeInfo &inInfo,
1891						const CSSM_DB_RECORD_INDEX_INFO *inIndexInfo /* = NULL */,
1892						const CSSM_DB_PARSING_MODULE_INFO *inParsingModule /* = NULL */)
1893{
1894	modifyDatabase();
1895	createTable(new MetaRecord(inInfo));
1896	insertTableSchema(inInfo, inIndexInfo);
1897}
1898
1899// Insert a new table. This is the version that gets called when a table is added
1900// after a database has been created.
1901
1902void
1903DbModifier::insertTable(Table::Id inTableId, const string &inTableName,
1904						uint32 inNumberOfAttributes,
1905						const CSSM_DB_SCHEMA_ATTRIBUTE_INFO *inAttributeInfo,
1906						uint32 inNumberOfIndexes,
1907						const CSSM_DB_SCHEMA_INDEX_INFO *inIndexInfo)
1908{
1909	modifyDatabase();
1910	ModifiedTable *aTable = createTable(new MetaRecord(inTableId, inNumberOfAttributes, inAttributeInfo));
1911
1912	CssmAutoDbRecordAttributeData aRecordBuilder(6); // Set capacity to 6 so we don't need to grow
1913
1914	// Create the entry for the SchemaRelations table.
1915	aRecordBuilder.add(RelationID, inTableId);
1916	aRecordBuilder.add(RelationName, inTableName);
1917
1918	// Insert the record into the SchemaRelations ModifiedTable
1919    findTable(mDb.schemaRelations.DataRecordType).insertRecord(mVersionId,
1920		&aRecordBuilder, NULL);
1921
1922	ModifiedTable &anAttributeTable = findTable(mDb.schemaAttributes.DataRecordType);
1923    for (uint32 anIndex = 0; anIndex < inNumberOfAttributes; anIndex++)
1924    {
1925		// Create an entry for the SchemaAttributes table.
1926		aRecordBuilder.clear();
1927		aRecordBuilder.add(RelationID, inTableId);
1928		// XXX What should this be?  We set it to CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER for now
1929		// since the AttributeID is always valid.
1930		aRecordBuilder.add(AttributeNameFormat, uint32(CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER));
1931		aRecordBuilder.add(AttributeID, inAttributeInfo[anIndex].AttributeId);
1932		if (inAttributeInfo[anIndex].AttributeName)
1933			aRecordBuilder.add(AttributeName, inAttributeInfo[anIndex].AttributeName);
1934		if (inAttributeInfo[anIndex].AttributeNameID.Length > 0)
1935			aRecordBuilder.add(AttributeNameID, inAttributeInfo[anIndex].AttributeNameID);
1936		aRecordBuilder.add(AttributeFormat, inAttributeInfo[anIndex].DataType);
1937
1938		// Insert the record into the SchemaAttributes ModifiedTable
1939		anAttributeTable.insertRecord(mVersionId, &aRecordBuilder, NULL);
1940    }
1941
1942	ModifiedTable &anIndexTable = findTable(mDb.schemaIndexes.DataRecordType);
1943    for (uint32 anIndex = 0; anIndex < inNumberOfIndexes; anIndex++)
1944    {
1945		// Create an entry for the SchemaIndexes table.
1946		aRecordBuilder.clear();
1947		aRecordBuilder.add(RelationID, inTableId);
1948		aRecordBuilder.add(IndexID, inIndexInfo[anIndex].IndexId);
1949		aRecordBuilder.add(AttributeID, inIndexInfo[anIndex].AttributeId);
1950		aRecordBuilder.add(IndexType, inIndexInfo[anIndex].IndexType);
1951		aRecordBuilder.add(IndexedDataLocation, inIndexInfo[anIndex].IndexedDataLocation);
1952
1953		// Insert the record into the SchemaIndexes ModifiedTable
1954		anIndexTable.insertRecord(mVersionId, &aRecordBuilder, NULL);
1955
1956		// update the table's index objects
1957		DbMutableIndex &index = aTable->findIndex(inIndexInfo[anIndex].IndexId,
1958			aTable->getMetaRecord(), inIndexInfo[anIndex].IndexType == CSSM_DB_INDEX_UNIQUE);
1959		index.appendAttribute(inIndexInfo[anIndex].AttributeId);
1960    }
1961}
1962
1963
1964
1965bool DbModifier::hasTable(Table::Id inTableId)
1966{
1967	return getDbVersion(false)->hasTable(inTableId);
1968}
1969
1970
1971
1972ModifiedTable &
1973DbModifier::findTable(Table::Id inTableId)
1974{
1975    ModifiedTableMap::iterator it = mModifiedTableMap.find(inTableId);
1976    if (it == mModifiedTableMap.end())
1977		CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE);
1978	return *it->second;
1979}
1980
1981
1982//
1983// AppleDatabaseManager implementation
1984//
1985
1986AppleDatabaseManager::AppleDatabaseManager(const AppleDatabaseTableName *tableNames)
1987	:	DatabaseManager(),
1988		mTableNames(tableNames)
1989{
1990	// make sure that a proper set of table ids and names has been provided
1991
1992	if (!mTableNames)
1993		CssmError::throwMe(CSSMERR_DL_INTERNAL_ERROR);
1994	else {
1995		uint32 i;
1996		for (i = 0; mTableNames[i].mTableName; i++) {}
1997		if (i < AppleDatabaseTableName::kNumRequiredTableNames)
1998			CssmError::throwMe(CSSMERR_DL_INTERNAL_ERROR);
1999	}
2000}
2001
2002Database *
2003AppleDatabaseManager::make(const DbName &inDbName)
2004{
2005    return new AppleDatabase(inDbName, mTableNames);
2006}
2007
2008
2009//
2010// AppleDbContext implementation
2011//
2012
2013/* This is the version 0 CSSM_APPLEDL_OPEN_PARAMETERS struct used up to 10.2.x. */
2014extern "C" {
2015
2016typedef struct cssm_appledl_open_parameters_v0
2017{
2018		uint32 length;  /* Should be sizeof(CSSM_APPLEDL_OPEN_PARAMETERS_V0). */
2019		uint32 version; /* Should be 0. */
2020		CSSM_BOOL autoCommit;
2021} CSSM_APPLEDL_OPEN_PARAMETERS_V0;
2022
2023};
2024
2025AppleDbContext::AppleDbContext(Database &inDatabase,
2026							   DatabaseSession &inDatabaseSession,
2027							   CSSM_DB_ACCESS_TYPE inAccessRequest,
2028							   const AccessCredentials *inAccessCred,
2029							   const void *inOpenParameters) :
2030	DbContext(inDatabase, inDatabaseSession, inAccessRequest, inAccessCred),
2031	mAutoCommit(true),
2032	mMode(0666)
2033{
2034	const CSSM_APPLEDL_OPEN_PARAMETERS *anOpenParameters =
2035		reinterpret_cast<const CSSM_APPLEDL_OPEN_PARAMETERS *>(inOpenParameters);
2036
2037	if (anOpenParameters)
2038	{
2039		switch (anOpenParameters->version)
2040		{
2041		case 1:
2042			if (anOpenParameters->length < sizeof(CSSM_APPLEDL_OPEN_PARAMETERS))
2043				CssmError::throwMe(CSSMERR_APPLEDL_INVALID_OPEN_PARAMETERS);
2044
2045			if (anOpenParameters->mask & kCSSM_APPLEDL_MASK_MODE)
2046				mMode = anOpenParameters->mode;
2047			/*DROPTHROUGH*/
2048		case 0:
2049			if (anOpenParameters->length < sizeof(CSSM_APPLEDL_OPEN_PARAMETERS_V0))
2050				CssmError::throwMe(CSSMERR_APPLEDL_INVALID_OPEN_PARAMETERS);
2051
2052			mAutoCommit = anOpenParameters->autoCommit == CSSM_FALSE ? false : true;
2053			break;
2054
2055		default:
2056			CssmError::throwMe(CSSMERR_APPLEDL_INVALID_OPEN_PARAMETERS);
2057		}
2058	}
2059}
2060
2061AppleDbContext::~AppleDbContext()
2062{
2063}
2064
2065//
2066// AppleDatabase implementation
2067//
2068AppleDatabase::AppleDatabase(const DbName &inDbName, const AppleDatabaseTableName *tableNames) :
2069    Database(inDbName),
2070	schemaRelations(tableNames[AppleDatabaseTableName::kSchemaInfo].mTableId,
2071		sizeof(AttrSchemaRelations) / sizeof(CSSM_DB_ATTRIBUTE_INFO),
2072		const_cast<CSSM_DB_ATTRIBUTE_INFO_PTR>(AttrSchemaRelations)),
2073	schemaAttributes(tableNames[AppleDatabaseTableName::kSchemaAttributes].mTableId,
2074		sizeof(AttrSchemaAttributes) / sizeof(CSSM_DB_ATTRIBUTE_INFO),
2075		const_cast<CSSM_DB_ATTRIBUTE_INFO_PTR>(AttrSchemaAttributes)),
2076	schemaIndexes(tableNames[AppleDatabaseTableName::kSchemaIndexes].mTableId,
2077		sizeof(AttrSchemaIndexes) / sizeof(CSSM_DB_ATTRIBUTE_INFO),
2078		const_cast<CSSM_DB_ATTRIBUTE_INFO_PTR>(AttrSchemaIndexes)),
2079	schemaParsingModule(tableNames[AppleDatabaseTableName::kSchemaParsingModule].mTableId,
2080		sizeof(AttrSchemaParsingModule) / sizeof(CSSM_DB_ATTRIBUTE_INFO),
2081		const_cast<CSSM_DB_ATTRIBUTE_INFO_PTR>(AttrSchemaParsingModule)),
2082    mAtomicFile(mDbName.dbName()),
2083	mDbModifier(mAtomicFile, *this),
2084	mTableNames(tableNames)
2085{
2086	/* temp check for X509Anchors access - this should removed before Leopard GM */
2087	if(!strcmp(inDbName.dbName(), "/System/Library/Keychains/X509Anchors")) {
2088		Syslog::alert("Warning: accessing obsolete X509Anchors.");
2089	}
2090}
2091
2092AppleDatabase::~AppleDatabase()
2093{
2094}
2095
2096// Return the name of a record type. This uses a table that maps record types
2097// to record names. The table is provided when the database is created.
2098
2099const char *AppleDatabase::recordName(CSSM_DB_RECORDTYPE inRecordType) const
2100{
2101	if (inRecordType == CSSM_DL_DB_RECORD_ANY || inRecordType == CSSM_DL_DB_RECORD_ALL_KEYS)
2102		CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE);
2103
2104	for (uint32 i = 0; mTableNames[i].mTableName; i++)
2105		if (mTableNames[i].mTableId == inRecordType)
2106			return mTableNames[i].mTableName;
2107
2108	return "";
2109}
2110
2111DbContext *
2112AppleDatabase::makeDbContext(DatabaseSession &inDatabaseSession,
2113                             CSSM_DB_ACCESS_TYPE inAccessRequest,
2114                             const AccessCredentials *inAccessCred,
2115                             const void *inOpenParameters)
2116{
2117    return new AppleDbContext(*this, inDatabaseSession, inAccessRequest,
2118							  inAccessCred, inOpenParameters);
2119}
2120
2121void
2122AppleDatabase::dbCreate(DbContext &inDbContext, const CSSM_DBINFO &inDBInfo,
2123                        const CSSM_ACL_ENTRY_INPUT *inInitialAclEntry)
2124{
2125	AppleDbContext &context = safer_cast<AppleDbContext &>(inDbContext);
2126    try
2127    {
2128		StLock<Mutex> _(mWriteLock);
2129        mDbModifier.createDatabase(inDBInfo, inInitialAclEntry, context.mode());
2130    }
2131    catch(...)
2132    {
2133        mDbModifier.rollback();
2134        throw;
2135    }
2136	if (context.autoCommit())
2137		mDbModifier.commit();
2138}
2139
2140void
2141AppleDatabase::dbOpen(DbContext &inDbContext)
2142{
2143	mDbModifier.openDatabase();
2144}
2145
2146void
2147AppleDatabase::dbClose()
2148{
2149	StLock<Mutex> _(mWriteLock);
2150	mDbModifier.closeDatabase();
2151}
2152
2153void
2154AppleDatabase::dbDelete(DatabaseSession &inDatabaseSession,
2155                        const AccessCredentials *inAccessCred)
2156{
2157	StLock<Mutex> _(mWriteLock);
2158    // XXX Check callers credentials.
2159	mDbModifier.deleteDatabase();
2160}
2161
2162void
2163AppleDatabase::createRelation(DbContext &inDbContext,
2164                              CSSM_DB_RECORDTYPE inRelationID,
2165                              const char *inRelationName,
2166                              uint32 inNumberOfAttributes,
2167                              const CSSM_DB_SCHEMA_ATTRIBUTE_INFO *inAttributeInfo,
2168                              uint32 inNumberOfIndexes,
2169                              const CSSM_DB_SCHEMA_INDEX_INFO &inIndexInfo)
2170{
2171	try
2172	{
2173		StLock<Mutex> _(mWriteLock);
2174		// XXX Fix the refs here.
2175		mDbModifier.insertTable(inRelationID, inRelationName,
2176								inNumberOfAttributes, inAttributeInfo,
2177								inNumberOfIndexes, &inIndexInfo);
2178	}
2179	catch(...)
2180	{
2181		if (safer_cast<AppleDbContext &>(inDbContext).autoCommit())
2182			mDbModifier.rollback();
2183		throw;
2184	}
2185	if (safer_cast<AppleDbContext &>(inDbContext).autoCommit())
2186		mDbModifier.commit();
2187}
2188
2189void
2190AppleDatabase::destroyRelation(DbContext &inDbContext,
2191                               CSSM_DB_RECORDTYPE inRelationID)
2192{
2193	try
2194	{
2195		StLock<Mutex> _(mWriteLock);
2196		mDbModifier.deleteTable(inRelationID);
2197	}
2198	catch(...)
2199	{
2200		if (safer_cast<AppleDbContext &>(inDbContext).autoCommit())
2201			mDbModifier.rollback();
2202		throw;
2203	}
2204	if (safer_cast<AppleDbContext &>(inDbContext).autoCommit())
2205		mDbModifier.commit();
2206}
2207
2208void
2209AppleDatabase::authenticate(DbContext &inDbContext,
2210                            CSSM_DB_ACCESS_TYPE inAccessRequest,
2211                            const AccessCredentials &inAccessCred)
2212{
2213    CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED);
2214}
2215
2216void
2217AppleDatabase::getDbAcl(DbContext &inDbContext,
2218                        const CSSM_STRING *inSelectionTag,
2219                        uint32 &outNumberOfAclInfos,
2220                        CSSM_ACL_ENTRY_INFO_PTR &outAclInfos)
2221{
2222    CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED);
2223}
2224
2225void
2226AppleDatabase::changeDbAcl(DbContext &inDbContext,
2227                           const AccessCredentials &inAccessCred,
2228                           const CSSM_ACL_EDIT &inAclEdit)
2229{
2230    CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED);
2231}
2232
2233void
2234AppleDatabase::getDbOwner(DbContext &inDbContext,
2235						  CSSM_ACL_OWNER_PROTOTYPE &outOwner)
2236{
2237    CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED);
2238}
2239
2240void
2241AppleDatabase::changeDbOwner(DbContext &inDbContext,
2242                             const AccessCredentials &inAccessCred,
2243                             const CSSM_ACL_OWNER_PROTOTYPE &inNewOwner)
2244{
2245    CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED);
2246}
2247
2248char *
2249AppleDatabase::getDbNameFromHandle(const DbContext &inDbContext) const
2250{
2251    CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED);
2252}
2253
2254CSSM_DB_UNIQUE_RECORD_PTR
2255AppleDatabase::dataInsert(DbContext &inDbContext,
2256                          CSSM_DB_RECORDTYPE inRecordType,
2257                          const CSSM_DB_RECORD_ATTRIBUTE_DATA *inAttributes,
2258                          const CssmData *inData)
2259{
2260	CSSM_DB_UNIQUE_RECORD_PTR anUniqueRecordPtr = NULL;
2261	try
2262	{
2263		StLock<Mutex> _(mWriteLock);
2264		const RecordId aRecordId =
2265			mDbModifier.insertRecord(inRecordType, inAttributes, inData);
2266
2267		anUniqueRecordPtr = createUniqueRecord(inDbContext, inRecordType,
2268											   aRecordId);
2269		if (safer_cast<AppleDbContext &>(inDbContext).autoCommit())
2270			mDbModifier.commit();
2271	}
2272	catch(...)
2273	{
2274		if (anUniqueRecordPtr != NULL)
2275			freeUniqueRecord(inDbContext, *anUniqueRecordPtr);
2276
2277		if (safer_cast<AppleDbContext &>(inDbContext).autoCommit())
2278			mDbModifier.rollback();
2279		throw;
2280	}
2281
2282	return anUniqueRecordPtr;
2283}
2284
2285void
2286AppleDatabase::dataDelete(DbContext &inDbContext,
2287                          const CSSM_DB_UNIQUE_RECORD &inUniqueRecord)
2288{
2289    try
2290    {
2291		// syslog if it's the .Mac password
2292		CSSM_DB_RECORD_ATTRIBUTE_DATA attrData;
2293		// we have to do this in two phases -- the first to get the record type, and the second to actually read the attributes.  Otherwise, we might get
2294		// an exception.
2295		memset(&attrData, 0, sizeof(attrData));
2296		dataGetFromUniqueRecordId(inDbContext, inUniqueRecord, &attrData, NULL);
2297
2298		if (attrData.DataRecordType == CSSM_DL_DB_RECORD_GENERIC_PASSWORD)
2299		{
2300			CSSM_DB_ATTRIBUTE_DATA attributes;
2301
2302			// setup some attributes and see if we are indeed the .Mac password
2303			attributes.Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER;
2304			attributes.Info.Label.AttributeID = 'svce';
2305			attributes.Info.AttributeFormat = 0;
2306			attributes.NumberOfValues = 1;
2307			attributes.Value = NULL;
2308
2309			attrData.NumberOfAttributes = 1;
2310			attrData.AttributeData = &attributes;
2311
2312			dataGetFromUniqueRecordId(inDbContext, inUniqueRecord, &attrData, NULL);
2313
2314			// now check the results
2315			std::string dataString((const char*) attrData.AttributeData[0].Value[0].Data, attrData.AttributeData[0].Value[0].Length);
2316			if (dataString == "iTools")
2317			{
2318				syslog(LOG_WARNING, "Warning: Removed .Me password");
2319			}
2320
2321			free(attrData.AttributeData[0].Value[0].Data);
2322			free(attrData.AttributeData[0].Value);
2323		}
2324
2325		StLock<Mutex> _(mWriteLock);
2326		Table::Id aTableId;
2327		const RecordId aRecordId(parseUniqueRecord(inUniqueRecord, aTableId));
2328		mDbModifier.deleteRecord(aTableId, aRecordId);
2329	}
2330    catch(...)
2331    {
2332		if (safer_cast<AppleDbContext &>(inDbContext).autoCommit())
2333			mDbModifier.rollback();
2334        throw;
2335    }
2336
2337	if (safer_cast<AppleDbContext &>(inDbContext).autoCommit())
2338		mDbModifier.commit();
2339}
2340
2341void
2342AppleDatabase::dataModify(DbContext &inDbContext,
2343                          CSSM_DB_RECORDTYPE inRecordType,
2344                          CSSM_DB_UNIQUE_RECORD &inoutUniqueRecord,
2345                          const CSSM_DB_RECORD_ATTRIBUTE_DATA *inAttributesToBeModified,
2346                          const CssmData *inDataToBeModified,
2347                          CSSM_DB_MODIFY_MODE inModifyMode)
2348{
2349    try
2350    {
2351		StLock<Mutex> _(mWriteLock);
2352		Table::Id aTableId;
2353		const RecordId oldRecordId = parseUniqueRecord(inoutUniqueRecord,
2354			aTableId);
2355#if 1
2356		if (inRecordType != aTableId)
2357#else
2358		if (inRecordType != aTableId &&
2359			inRecordType != CSSM_DL_DB_RECORD_ANY &&
2360			!(inRecordType == CSSM_DL_DB_RECORD_ALL_KEYS &&
2361			  (aTableId == CSSM_DL_DB_RECORD_PUBLIC_KEY ||
2362			   aTableId == CSSM_DL_DB_RECORD_PRIVATE_KEY ||
2363			   aTableId == CSSM_DL_DB_RECORD_SYMMETRIC_KEY)))
2364#endif
2365		{
2366			CssmError::throwMe(CSSMERR_DL_INVALID_RECORD_UID);
2367		}
2368
2369		const RecordId newRecordId =
2370			mDbModifier.updateRecord(aTableId,
2371									 oldRecordId,
2372									 inAttributesToBeModified,
2373									 inDataToBeModified,
2374									 inModifyMode);
2375		updateUniqueRecord(inDbContext, aTableId, newRecordId,
2376			inoutUniqueRecord);
2377	}
2378    catch(...)
2379    {
2380		if (safer_cast<AppleDbContext &>(inDbContext).autoCommit())
2381			mDbModifier.rollback();
2382        throw;
2383    }
2384
2385	if (safer_cast<AppleDbContext &>(inDbContext).autoCommit())
2386		mDbModifier.commit();
2387}
2388
2389CSSM_HANDLE
2390AppleDatabase::dataGetFirst(DbContext &inDbContext,
2391                            const CssmQuery *inQuery,
2392                            CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes,
2393                            CssmData *inoutData,
2394                            CSSM_DB_UNIQUE_RECORD_PTR &outUniqueRecord)
2395{
2396	// XXX: register Cursor with DbContext and have DbContext call
2397	// dataAbortQuery for all outstanding Query objects on close.
2398	auto_ptr<Cursor> aCursor(mDbModifier.createCursor(inQuery));
2399	Table::Id aTableId;
2400	RecordId aRecordId;
2401
2402	if (!aCursor->next(aTableId, inoutAttributes, inoutData,
2403					   inDbContext.mDatabaseSession, aRecordId))
2404		// return a NULL handle, and implicitly delete the cursor
2405		return CSSM_INVALID_HANDLE;
2406
2407	outUniqueRecord = createUniqueRecord(inDbContext, aTableId, aRecordId);
2408	return aCursor.release()->handle(); // We didn't throw so keep the Cursor around.
2409}
2410
2411bool
2412AppleDatabase::dataGetNext(DbContext &inDbContext,
2413                           CSSM_HANDLE inResultsHandle,
2414                           CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes,
2415                           CssmData *inoutData,
2416                           CSSM_DB_UNIQUE_RECORD_PTR &outUniqueRecord)
2417{
2418	auto_ptr<Cursor> aCursor(&HandleObject::find<Cursor>(inResultsHandle, CSSMERR_DL_INVALID_RESULTS_HANDLE));
2419	Table::Id aTableId;
2420	RecordId aRecordId;
2421
2422	if (!aCursor->next(aTableId, inoutAttributes, inoutData, inDbContext.mDatabaseSession, aRecordId))
2423		return false;
2424
2425	outUniqueRecord = createUniqueRecord(inDbContext, aTableId, aRecordId);
2426
2427	aCursor.release();
2428	return true;
2429}
2430
2431void
2432AppleDatabase::dataAbortQuery(DbContext &inDbContext,
2433                              CSSM_HANDLE inResultsHandle)
2434{
2435	delete &HandleObject::find<Cursor>(inResultsHandle, CSSMERR_DL_INVALID_RESULTS_HANDLE);
2436}
2437
2438void
2439AppleDatabase::dataGetFromUniqueRecordId(DbContext &inDbContext,
2440                                         const CSSM_DB_UNIQUE_RECORD &inUniqueRecord,
2441                                         CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes,
2442                                         CssmData *inoutData)
2443{
2444	Table::Id aTableId;
2445	const RecordId aRecordId(parseUniqueRecord(inUniqueRecord, aTableId));
2446	// XXX Change CDSA spec to use new RecordId returned by this function
2447	mDbModifier.getRecord(aTableId, aRecordId, inoutAttributes, inoutData,
2448						  inDbContext.mDatabaseSession);
2449}
2450
2451void
2452AppleDatabase::freeUniqueRecord(DbContext &inDbContext,
2453                                CSSM_DB_UNIQUE_RECORD &inUniqueRecord)
2454{
2455	if (inUniqueRecord.RecordIdentifier.Length != 0
2456		&& inUniqueRecord.RecordIdentifier.Data != NULL)
2457	{
2458		inUniqueRecord.RecordIdentifier.Length = 0;
2459		inDbContext.mDatabaseSession.free(inUniqueRecord.RecordIdentifier.Data);
2460	}
2461	inDbContext.mDatabaseSession.free(&inUniqueRecord);
2462}
2463
2464void
2465AppleDatabase::updateUniqueRecord(DbContext &inDbContext,
2466								  CSSM_DB_RECORDTYPE inTableId,
2467								  const RecordId &inRecordId,
2468								  CSSM_DB_UNIQUE_RECORD &inoutUniqueRecord)
2469{
2470	uint32 *aBuffer = reinterpret_cast<uint32 *>(inoutUniqueRecord.RecordIdentifier.Data);
2471	aBuffer[0] = inTableId;
2472	aBuffer[1] = inRecordId.mRecordNumber;
2473	aBuffer[2] = inRecordId.mCreateVersion;
2474	aBuffer[3] = inRecordId.mRecordVersion;
2475}
2476
2477CSSM_DB_UNIQUE_RECORD_PTR
2478AppleDatabase::createUniqueRecord(DbContext &inDbContext,
2479								  CSSM_DB_RECORDTYPE inTableId,
2480								  const RecordId &inRecordId)
2481{
2482	CSSM_DB_UNIQUE_RECORD_PTR aUniqueRecord =
2483		inDbContext.mDatabaseSession.alloc<CSSM_DB_UNIQUE_RECORD>();
2484	memset(aUniqueRecord, 0, sizeof(*aUniqueRecord));
2485	aUniqueRecord->RecordIdentifier.Length = sizeof(uint32) * 4;
2486	try
2487	{
2488		aUniqueRecord->RecordIdentifier.Data =
2489			inDbContext.mDatabaseSession.alloc<uint8>(sizeof(uint32) * 4);
2490		updateUniqueRecord(inDbContext, inTableId, inRecordId, *aUniqueRecord);
2491	}
2492	catch(...)
2493	{
2494		inDbContext.mDatabaseSession.free(aUniqueRecord);
2495		throw;
2496	}
2497
2498	return aUniqueRecord;
2499}
2500
2501const RecordId
2502AppleDatabase::parseUniqueRecord(const CSSM_DB_UNIQUE_RECORD &inUniqueRecord,
2503								 CSSM_DB_RECORDTYPE &outTableId)
2504{
2505	if (inUniqueRecord.RecordIdentifier.Length != sizeof(uint32) * 4)
2506		CssmError::throwMe(CSSMERR_DL_INVALID_RECORD_UID);
2507
2508	uint32 *aBuffer = reinterpret_cast<uint32 *>(inUniqueRecord.RecordIdentifier.Data);
2509	outTableId = aBuffer[0];
2510	return RecordId(aBuffer[1], aBuffer[2], aBuffer[3]);
2511}
2512
2513void
2514AppleDatabase::passThrough(DbContext &dbContext,
2515						   uint32 passThroughId,
2516						   const void *inputParams,
2517						   void **outputParams)
2518{
2519	switch (passThroughId)
2520	{
2521	case CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT:
2522		{
2523			AppleDbContext &dbc = safer_cast<AppleDbContext &>(dbContext);
2524			// Return the old state of the autoCommit flag if requested
2525			if (outputParams)
2526				*reinterpret_cast<CSSM_BOOL *>(outputParams) = dbc.autoCommit();
2527			dbc.autoCommit(inputParams ? CSSM_TRUE : CSSM_FALSE);
2528		}
2529		break;
2530
2531	case CSSM_APPLEFILEDL_COMMIT:
2532		mDbModifier.commit();
2533		break;
2534
2535	case CSSM_APPLEFILEDL_ROLLBACK:
2536		mDbModifier.rollback();
2537		break;
2538
2539	case CSSM_APPLECSPDL_DB_RELATION_EXISTS:
2540	{
2541		CSSM_BOOL returnValue;
2542
2543		CSSM_DB_RECORDTYPE recordType = *(CSSM_DB_RECORDTYPE*) inputParams;
2544		if (recordType == CSSM_DL_DB_RECORD_ANY || recordType == CSSM_DL_DB_RECORD_ALL_KEYS)
2545		{
2546			returnValue = CSSM_TRUE;
2547		}
2548		else
2549		{
2550			returnValue = mDbModifier.hasTable(recordType);
2551		}
2552
2553		*(CSSM_BOOL*) outputParams = returnValue;
2554		break;
2555	}
2556
2557	default:
2558		CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED);
2559		break;
2560	}
2561}
2562