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