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