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