/ OSX / utilities / SecDb.c
SecDb.c
   1  /*
   2   * Copyright (c) 2012-2017 Apple Inc. All Rights Reserved.
   3   *
   4   * @APPLE_LICENSE_HEADER_START@
   5   *
   6   * This file contains Original Code and/or Modifications of Original Code
   7   * as defined in and that are subject to the Apple Public Source License
   8   * Version 2.0 (the 'License'). You may not use this file except in
   9   * compliance with the License. Please obtain a copy of the License at
  10   * http://www.opensource.apple.com/apsl/ and read it before using this
  11   * file.
  12   *
  13   * The Original Code and all software distributed under the License are
  14   * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
  15   * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
  16   * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
  17   * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
  18   * Please see the License for the specific language governing rights and
  19   * limitations under the License.
  20   *
  21   * @APPLE_LICENSE_HEADER_END@
  22   */
  23  
  24  
  25  #include "SecDb.h"
  26  #include "SecDbInternal.h"
  27  #include "debugging.h"
  28  
  29  #include <sqlite3.h>
  30  #include <sqlite3_private.h>
  31  #include <CoreFoundation/CoreFoundation.h>
  32  #include <libgen.h>
  33  #include <sys/csr.h>
  34  #include <sys/stat.h>
  35  #include <AssertMacros.h>
  36  #include "SecCFWrappers.h"
  37  #include "SecCFError.h"
  38  #include "SecIOFormat.h"
  39  #include <stdio.h>
  40  #include "Security/SecBase.h"
  41  #include "SecAutorelease.h"
  42  #include <os/assumes.h>
  43  #include <xpc/private.h>    // xpc_transaction_exit_clean()
  44  
  45  #ifdef DARLING
  46  #include <pthread.h>
  47  #endif
  48  
  49  //
  50  // Architecturally inverted files
  51  // These are in SecureObjectSync but utilities depends on them
  52  // <rdar://problem/20802079> Fix layer violation (SOSDigestVector, SOSManifest, SecDB.c)
  53  //
  54  #include "keychain/SecureObjectSync/SOSDigestVector.h"
  55  #include "keychain/SecureObjectSync/SOSManifest.h"
  56  
  57  #define SECDB_DEBUGGING 0
  58  
  59  struct __OpaqueSecDbStatement {
  60      CFRuntimeBase _base;
  61  
  62      SecDbConnectionRef dbconn;
  63      sqlite3_stmt *stmt;
  64  };
  65  
  66  struct __OpaqueSecDbConnection {
  67      CFRuntimeBase _base;
  68  
  69      //CFMutableDictionaryRef statements;
  70  
  71      SecDbRef db;     // NONRETAINED, since db or block retains us
  72      bool readOnly;
  73      bool inTransaction;
  74      SecDbTransactionSource source;
  75      bool isCorrupted;
  76      int maybeCorruptedCode;
  77      bool hasIOFailure;
  78      CFErrorRef corruptionError;
  79      sqlite3 *handle;
  80      // Pending deletions and additions for the current transaction
  81      // Entires are either:
  82      // 1) a CFArrayRef of 1 element representing a deletion,
  83      // 2) a CFArrayRef of 2 elements representing the element 0 having been replaced with element 1
  84      // 3) a CFTypeRef that is not a CFArrayRef, representing an add of the element in question.
  85      CFMutableArrayRef changes;
  86  };
  87  
  88  struct __OpaqueSecDb {
  89      CFRuntimeBase _base;
  90  
  91      CFStringRef db_path;
  92      dispatch_queue_t queue;
  93      dispatch_queue_t commitQueue;
  94  
  95      CFMutableArrayRef idleWriteConnections;     // up to kSecDbMaxWriters of them (currently 1, requires locking change for >1)
  96      CFMutableArrayRef idleReadConnections;      // up to kSecDbMaxReaders of them
  97      pthread_mutex_t writeMutex;
  98      pthread_mutexattr_t writeMutexAttrs;
  99      // TODO: Replace after we have rdar://problem/60961964
 100      dispatch_semaphore_t readSemaphore;
 101  
 102      bool didFirstOpen;
 103      bool (^opened)(SecDbRef db, SecDbConnectionRef dbconn, bool didCreate, bool *callMeAgainForNextConnection, CFErrorRef *error);
 104      bool callOpenedHandlerForNextConnection;
 105      CFMutableArrayRef notifyPhase; /* array of SecDBNotifyBlock */
 106      mode_t mode; /* database file permissions */
 107      bool readWrite; /* open database read-write */
 108      bool allowRepair; /* allow database repair */
 109      bool useWAL; /* use WAL mode */
 110      bool useRobotVacuum; /* use if SecDB should manage vacuum behind your back */
 111      uint8_t maxIdleHandles;
 112      void (^corruptionReset)(void);
 113  };
 114  
 115  // MARK: Error domains and error helper functions
 116  
 117  CFStringRef kSecDbErrorDomain = CFSTR("com.apple.utilities.sqlite3");
 118  
 119  bool SecDbError(int sql_code, CFErrorRef *error, CFStringRef format, ...) {
 120      if (sql_code == SQLITE_OK) return true;
 121  
 122      if (error) {
 123          va_list args;
 124          CFIndex code = sql_code;
 125          CFErrorRef previousError = *error;
 126  
 127          *error = NULL;
 128          va_start(args, format);
 129          SecCFCreateErrorWithFormatAndArguments(code, kSecDbErrorDomain, previousError, error, NULL, format, args);
 130          va_end(args);
 131      }
 132      return false;
 133  }
 134  
 135  bool SecDbErrorWithDb(int sql_code, sqlite3 *db, CFErrorRef *error, CFStringRef format, ...) {
 136      if (sql_code == SQLITE_OK) return true;
 137      if (error) {
 138          va_list args;
 139          va_start(args, format);
 140          CFStringRef message = CFStringCreateWithFormatAndArguments(kCFAllocatorDefault, NULL, format, args);
 141          va_end(args);
 142          CFStringRef errno_code = NULL;
 143  
 144          if (sql_code == SQLITE_CANTOPEN) {
 145              int errno_number = sqlite3_system_errno(db);
 146              errno_code = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), errno_number);
 147          } else {
 148              errno_code = CFRetain(CFSTR(""));
 149          }
 150  
 151          int extended_code = sqlite3_extended_errcode(db);
 152          if (sql_code == extended_code)
 153              SecDbError(sql_code, error, CFSTR("%@: [%d]%@ %s"), message, sql_code, errno_code, sqlite3_errmsg(db));
 154          else
 155              SecDbError(sql_code, error, CFSTR("%@: [%d->%d]%@ %s"), message, sql_code, extended_code, errno_code, sqlite3_errmsg(db));
 156          CFReleaseSafe(message);
 157          CFReleaseSafe(errno_code);
 158      }
 159      return false;
 160  }
 161  
 162  bool SecDbErrorWithStmt(int sql_code, sqlite3_stmt *stmt, CFErrorRef *error, CFStringRef format, ...) {
 163      if (sql_code == SQLITE_OK) return true;
 164      if (error) {
 165          va_list args;
 166          va_start(args, format);
 167          CFStringRef message = CFStringCreateWithFormatAndArguments(kCFAllocatorDefault, NULL, format, args);
 168          va_end(args);
 169  
 170          sqlite3 *db = sqlite3_db_handle(stmt);
 171          const char *sql = sqlite3_sql(stmt);
 172          int extended_code = sqlite3_extended_errcode(db);
 173          if (sql_code == extended_code)
 174              SecDbError(sql_code, error, CFSTR("%@: [%d] %s sql: %s"), message, sql_code, sqlite3_errmsg(db), sql);
 175          else
 176              SecDbError(sql_code, error, CFSTR("%@: [%d->%d] %s sql: %s"), message, sql_code, extended_code, sqlite3_errmsg(db), sql);
 177          CFReleaseSafe(message);
 178      }
 179      return false;
 180  }
 181  
 182  // A callback for the sqlite3_log() interface.
 183  static void sqlite3Log(void *pArg, int iErrCode, const char *zMsg){
 184      secdebug("sqlite3", "(%d) %s", iErrCode, zMsg);
 185  }
 186  
 187  void _SecDbServerSetup(void)
 188  {
 189      static dispatch_once_t onceToken;
 190      dispatch_once(&onceToken, ^{
 191          int rx = sqlite3_config(SQLITE_CONFIG_LOG, sqlite3Log, NULL);
 192          if (SQLITE_OK != rx) {
 193              secwarning("Could not set up sqlite global error logging to syslog: %d", rx);
 194          }
 195      });
 196  }
 197  
 198  
 199  // MARK: -
 200  // MARK: Static helper functions
 201  
 202  static bool SecDbOpenHandle(SecDbConnectionRef dbconn, bool *created, CFErrorRef *error);
 203  static bool SecDbHandleCorrupt(SecDbConnectionRef dbconn, int rc, CFErrorRef *error);
 204  
 205  #pragma mark -
 206  #pragma mark SecDbRef
 207  
 208  static CFStringRef
 209  SecDbCopyFormatDescription(CFTypeRef value, CFDictionaryRef formatOptions)
 210  {
 211      SecDbRef db = (SecDbRef)value;
 212      return CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("<SecDb path:%@ connections: %@>"), db->db_path, db->idleReadConnections);
 213  }
 214  
 215  
 216  static void
 217  SecDbDestroy(CFTypeRef value)
 218  {
 219      SecDbRef db = (SecDbRef)value;
 220  
 221      CFReleaseNull(db->db_path);
 222      dispatch_sync(db->queue, ^{
 223          CFReleaseNull(db->idleWriteConnections);
 224          CFReleaseNull(db->idleReadConnections);
 225      });
 226  
 227      if (db->queue) {
 228          dispatch_release(db->queue);
 229          db->queue = NULL;
 230      }
 231      if (db->commitQueue) {
 232          dispatch_release(db->commitQueue);
 233          db->commitQueue = NULL;
 234      }
 235  
 236      pthread_mutex_destroy(&(db->writeMutex));
 237  
 238      if (db->readSemaphore) {
 239          dispatch_release(db->readSemaphore);
 240          db->readSemaphore = NULL;
 241      }
 242  
 243      if (db->opened) {
 244          Block_release(db->opened);
 245          db->opened = NULL;
 246      }
 247      CFReleaseNull(db->notifyPhase);
 248  }
 249  
 250  CFGiblisFor(SecDb)
 251  
 252  SecDbRef
 253  SecDbCreate(CFStringRef dbName, mode_t mode, bool readWrite, bool allowRepair, bool useWAL, bool useRobotVacuum, uint8_t maxIdleHandles,
 254                         bool (^opened)(SecDbRef db, SecDbConnectionRef dbconn, bool didCreate, bool *callMeAgainForNextConnection, CFErrorRef *error))
 255  {
 256      SecDbRef db = NULL;
 257  
 258      db = CFTypeAllocate(SecDb, struct __OpaqueSecDb, kCFAllocatorDefault);
 259      require(db != NULL, done);
 260  
 261      CFStringPerformWithCString(dbName, ^(const char *dbNameStr) {
 262          db->queue = dispatch_queue_create(dbNameStr, DISPATCH_QUEUE_SERIAL);
 263      });
 264      CFStringRef commitQueueStr = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@-commit"), dbName);
 265      CFStringPerformWithCString(commitQueueStr, ^(const char *cqNameStr) {
 266          db->commitQueue = dispatch_queue_create(cqNameStr, DISPATCH_QUEUE_CONCURRENT);
 267      });
 268      CFReleaseNull(commitQueueStr);
 269      db->readSemaphore = dispatch_semaphore_create(kSecDbMaxReaders);
 270  
 271      bool mutexAttrSuccess =  (0 == pthread_mutexattr_init(&(db->writeMutexAttrs)));
 272      if(mutexAttrSuccess) {
 273          mutexAttrSuccess = (0 == pthread_mutexattr_setpolicy_np(&(db->writeMutexAttrs), PTHREAD_MUTEX_POLICY_FAIRSHARE_NP));
 274      }
 275  
 276      if(!mutexAttrSuccess) {
 277          seccritical("SecDb: SecDbCreate failed to create attributes for the write mutex; fairness properties are no longer present");
 278      }
 279  
 280      if (pthread_mutex_init(&(db->writeMutex), (mutexAttrSuccess ? &(db->writeMutexAttrs) : NULL)) != 0) {
 281          seccritical("SecDb: SecDbCreate failed to init the write mutex, this will end badly");
 282      }
 283  
 284      db->idleWriteConnections = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
 285      db->idleReadConnections = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
 286      db->opened = opened ? Block_copy(opened) : NULL;
 287      if (getenv("__OSINSTALL_ENVIRONMENT") != NULL) {
 288          // TODO: Move this code out of this layer
 289          secinfo("#SecDB", "SecDB: running from installer");
 290  
 291          db->db_path = CFSTR("file::memory:?cache=shared");
 292      } else {
 293          db->db_path = CFStringCreateCopy(kCFAllocatorDefault, dbName);
 294      }
 295      db->mode = mode;
 296      db->readWrite = readWrite;
 297      db->allowRepair = allowRepair;
 298      db->useWAL = useWAL;
 299      db->useRobotVacuum = useRobotVacuum;
 300      db->maxIdleHandles = maxIdleHandles;
 301      db->corruptionReset = NULL;
 302  
 303  done:
 304      return db;
 305  }
 306  
 307  CFIndex
 308  SecDbIdleConnectionCount(SecDbRef db) {
 309      __block CFIndex count = 0;
 310      dispatch_sync(db->queue, ^{
 311          count = CFArrayGetCount(db->idleReadConnections);
 312          count += CFArrayGetCount(db->idleWriteConnections);
 313      });
 314      return count;
 315  }
 316  
 317  void SecDbAddNotifyPhaseBlock(SecDbRef db, SecDBNotifyBlock notifyPhase)
 318  {
 319  #if !TARGET_OS_BRIDGE
 320      // SecDbNotifyPhase seems to mostly be called on the db's commitQueue, and not the db's queue. Therefore, protect the array with that queue.
 321      dispatch_sync(db->commitQueue, ^{
 322          SecDBNotifyBlock block = Block_copy(notifyPhase); /* Force the block off the stack */
 323          if (db->notifyPhase == NULL) {
 324              db->notifyPhase = CFArrayCreateMutableForCFTypes(NULL);
 325          }
 326          CFArrayAppendValue(db->notifyPhase, block);
 327          Block_release(block);
 328      });
 329  #endif
 330  }
 331  
 332  static void SecDbNotifyPhase(SecDbConnectionRef dbconn, SecDbTransactionPhase phase) {
 333  #if !TARGET_OS_BRIDGE
 334      if (CFArrayGetCount(dbconn->changes)) {
 335          CFArrayRef changes = dbconn->changes;
 336          dbconn->changes = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
 337          if (dbconn->db->notifyPhase) {
 338              CFArrayForEach(dbconn->db->notifyPhase, ^(const void *value) {
 339                  SecDBNotifyBlock notifyBlock = (SecDBNotifyBlock)value;
 340                  notifyBlock(dbconn, phase, dbconn->source, changes);
 341              });
 342          }
 343          CFReleaseSafe(changes);
 344      }
 345  #endif
 346  }
 347  
 348  static void SecDbOnNotify(SecDbConnectionRef dbconn, void (^perform)(void)) {
 349      perform();
 350  }
 351  
 352  CFStringRef SecDbGetPath(SecDbRef db) {
 353      if(!db) {
 354          return NULL;
 355      }
 356      return db->db_path;
 357  }
 358  
 359  
 360  #pragma mark -
 361  #pragma mark SecDbConnectionRef
 362  
 363  static bool SecDbCheckCorrupted(SecDbConnectionRef dbconn)
 364  {
 365      __block bool checkDidRun = false;
 366      __block bool isCorrupted = false;
 367      __block CFErrorRef error = NULL;
 368      SecDbPrepare(dbconn, CFSTR("PRAGMA integrity_check"), &error, ^(sqlite3_stmt *stmt) {
 369          SecDbStep(dbconn, stmt, &error, ^(bool *stop) {
 370              const char * result = (const char*)sqlite3_column_text(stmt, 0);
 371              if (!result || strncasecmp(result, "ok", 3) != 0) {
 372                  isCorrupted = true;
 373                  secerror("SecDBCheckCorrupted integrity_check returned %s", (result) ? result : "NULL");
 374              }
 375              checkDidRun = true;
 376          });
 377      });
 378      if (!checkDidRun) {
 379          // An error occurred in SecDbPrepare before we could run the block.
 380          if (error) {
 381              CFIndex code = CFErrorGetCode(error);
 382              if (SQLITE_CORRUPT == code || SQLITE_NOTADB == code) {
 383                  isCorrupted = true;
 384              }
 385              secinfo("#SecDB", "#SecDB warning error %{public}@ when running integrity check", error);
 386          } else {
 387              // We don't have an error ref if SecDbPrepare has called SecDbConnectionCheckCode,
 388              // which then called SecDbHandleCorrupt. That code path is only entered when the
 389              // original error was SQLITE_CORRUPT or SQLITE_NOTADB. On other errors, the
 390              // CFErrorRef is not cleared and we can just check the code above.
 391              isCorrupted = true;
 392              secinfo("#SecDB", "#SecDB warning: failed to run integrity check due to corruption");
 393          }
 394      }
 395      if (isCorrupted) {
 396          if (checkDidRun) {
 397              secerror("SecDBCheckCorrupted ran integrity_check, and that didn't return ok");
 398          } else {
 399              secerror("SecDBCheckCorrupted failed to run integrity check");
 400          }
 401      }
 402      CFReleaseNull(error);
 403  
 404      return isCorrupted;
 405  }
 406  
 407  static bool SecDbDidCreateFirstConnection(SecDbConnectionRef dbconn, bool didCreate, CFErrorRef *error)
 408  {
 409      secinfo("#SecDB", "#SecDB starting maintenance");
 410      bool ok = true;
 411  
 412      // Historical note: this used to check for integrity but that became too slow and caused panics at boot.
 413      // Now, just react to SQLite errors when doing an operation. If file on disk is borked it'll tell us right away.
 414  
 415      if (!dbconn->isCorrupted && dbconn->db->opened) {
 416          CFErrorRef localError = NULL;
 417  
 418          dbconn->db->callOpenedHandlerForNextConnection = false;
 419          ok = dbconn->db->opened(dbconn->db, dbconn, didCreate, &dbconn->db->callOpenedHandlerForNextConnection, &localError);
 420  
 421          if (!ok)
 422              secerror("opened block failed: %@", localError);
 423  
 424          if (!dbconn->isCorrupted && error && *error == NULL) {
 425              *error = localError;
 426              localError = NULL;
 427          } else {
 428              if (localError)
 429                  secerror("opened block failed: error (%@) is being released and lost", localError);
 430              CFReleaseNull(localError);
 431          }
 432      }
 433  
 434      if (dbconn->isCorrupted) {
 435          ok = SecDbHandleCorrupt(dbconn, 0, error);
 436      }
 437  
 438      secinfo("#SecDB", "#SecDB starting maintenance");
 439      return ok;
 440  }
 441  
 442  void SecDbCorrupt(SecDbConnectionRef dbconn, CFErrorRef error)
 443  {
 444      if (__security_simulatecrash_enabled()) {
 445          os_log_fault(secLogObjForScope("SecEmergency"), "SecDBCorrupt: %@", error);
 446      }
 447      dbconn->isCorrupted = true;
 448      CFRetainAssign(dbconn->corruptionError, error);
 449  }
 450  
 451  
 452  static uint8_t knownDbPathIndex(SecDbConnectionRef dbconn)
 453  {
 454  
 455      if(CFEqual(dbconn->db->db_path, CFSTR("/Library/Keychains/keychain-2.db")))
 456          return 1;
 457      if(CFEqual(dbconn->db->db_path, CFSTR("/Library/Keychains/ocspcache.sqlite3")))
 458          return 2;
 459      if(CFEqual(dbconn->db->db_path, CFSTR("/Library/Keychains/TrustStore.sqlite3")))
 460          return 3;
 461      if(CFEqual(dbconn->db->db_path, CFSTR("/Library/Keychains/caissuercache.sqlite3")))
 462          return 4;
 463  
 464      /* Unknown DB path */
 465      return 0;
 466  }
 467  
 468  static bool SecDbConnectionCheckCode(SecDbConnectionRef dbconn, int code, CFErrorRef *error, CFStringRef desc, ...)
 469      CF_FORMAT_FUNCTION(4, 5);
 470  
 471  // Return true if there was no error, returns false otherwise and set *error to an appropriate CFErrorRef.
 472  static bool SecDbConnectionCheckCode(SecDbConnectionRef dbconn, int code, CFErrorRef *error, CFStringRef desc, ...) {
 473      if (code == SQLITE_OK || code == SQLITE_DONE)
 474          return true;
 475  
 476      if (error) {
 477          va_list args;
 478          va_start(args, desc);
 479          CFStringRef msg = CFStringCreateWithFormatAndArguments(kCFAllocatorDefault, NULL, desc, args);
 480          va_end(args);
 481          SecDbErrorWithDb(code, dbconn->handle, error, CFSTR("%@"), msg);
 482          CFRelease(msg);
 483      }
 484  
 485      dbconn->hasIOFailure |= (SQLITE_IOERR == code);
 486  
 487      /* If it's already corrupted, don't try to recover */
 488      if (dbconn->isCorrupted) {
 489          CFStringRef reason = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
 490                                                        CFSTR("SQL DB %@ is corrupted already. Corruption error was: %d (previously %d)"),
 491                                                        dbconn->db->db_path, code, dbconn->maybeCorruptedCode);
 492          secerror("%@",reason);
 493          __security_simulatecrash(reason, __sec_exception_code_TwiceCorruptDb(knownDbPathIndex(dbconn)));
 494          CFReleaseSafe(reason);
 495          // We can't fall through to the checking case because it eventually calls SecDbConnectionCheckCode again.
 496          // However, this is the second time we're seeing corruption so let's take the ultimate measure.
 497          if ((SQLITE_CORRUPT == code) || (SQLITE_NOTADB == code)) {
 498              secerror("SecDbConnectionCheckCode detected corruption twice: going to handle corrupt DB");
 499              (void)SecDbHandleCorrupt(dbconn, code, error);
 500          }
 501          return false;
 502      }
 503  
 504      // NOTADB means file is garbage, so it's functionally equivalent to corruption
 505      dbconn->isCorrupted = (SQLITE_CORRUPT == code) || (SQLITE_NOTADB == code);
 506      if (dbconn->isCorrupted) {
 507          /* Run integrity check and only make dbconn->isCorrupted true and
 508             run the corruption handler if the integrity check conclusively fails. */
 509          dbconn->maybeCorruptedCode = code;
 510          dbconn->isCorrupted = SecDbCheckCorrupted(dbconn);
 511          if (dbconn->isCorrupted) {
 512              secerror("operation returned code: %d integrity check=fail", code);
 513              (void)SecDbHandleCorrupt(dbconn, code, error);
 514          } else {
 515              secerror("operation returned code: %d: integrity check=pass", code);
 516          }
 517      }
 518  
 519      return false;
 520  }
 521  
 522  #define BUSY_TIMEOUT_MS (5 * 60 * 1000)  /* 5 minutes */
 523  
 524  static int sleepBackoff[] = { 10, 20, 50, 100, 250 };
 525  static int sumBackoff[]   = { 10, 30, 80, 180, 430 };
 526  static int NumberOfSleepBackoff = sizeof(sleepBackoff)/sizeof(sleepBackoff[0]);
 527  
 528  // Use these as silly hacks to encode the SQLite return code in the backtrace, for hang debugging purposes
 529  static void __attribute__((noinline)) SecDbLockSleep(int ms) {
 530      sqlite3_sleep(ms);
 531  }
 532  
 533  static void __attribute__((noinline)) SecDbBusySleep(int ms) {
 534      sqlite3_sleep(ms);
 535  }
 536  
 537  // Return true causes the operation to be tried again.
 538  // Note that we set sqlite3_busy_timeout on the connection, so anytime you're in here, it's likely due to SQLITE_LOCKED.
 539  static bool SecDbWaitIfNeeded(SecDbConnectionRef dbconn, int s3e, sqlite3_stmt *stmt, CFStringRef desc, int nTries, CFErrorRef *error) {
 540      if (((0xFF & s3e) == SQLITE_BUSY) || ((0xFF & s3e) == SQLITE_LOCKED)) {
 541          int totaltimeout, timeout;
 542  
 543          _Static_assert(sizeof(sumBackoff) == sizeof(sleepBackoff), "matching arrays not matching");
 544          _Static_assert(sizeof(sumBackoff[0]) == sizeof(sleepBackoff[0]), "matching arrays not matching");
 545  
 546          if (nTries < NumberOfSleepBackoff) {
 547              timeout = sleepBackoff[nTries];
 548              totaltimeout = sumBackoff[nTries];
 549          } else {
 550              timeout = sleepBackoff[NumberOfSleepBackoff - 1];
 551              totaltimeout = sumBackoff[NumberOfSleepBackoff - 1] + (timeout * (nTries - NumberOfSleepBackoff));
 552          }
 553          if (totaltimeout < BUSY_TIMEOUT_MS) {
 554              secinfo("#SecDB", "sqlite busy/locked: %d ntries: %d totaltimeout: %d", s3e, nTries, totaltimeout);
 555              if(((0xFF & s3e) == SQLITE_LOCKED)) {
 556                  SecDbLockSleep(timeout);
 557              } else {
 558                  SecDbBusySleep(timeout);
 559              }
 560              return true;
 561          } else {
 562              secinfo("#SecDB", "sqlite busy/locked: too long: %d ms, giving up", totaltimeout);
 563          }
 564      }
 565  
 566      return SecDbConnectionCheckCode(dbconn, s3e, error, CFSTR("%@"), desc);
 567  }
 568  
 569  enum SecDbStepResult {
 570      kSecDbErrorStep = 0,
 571      kSecDbRowStep = 1,
 572      kSecDbDoneStep = 2,
 573  };
 574  typedef enum SecDbStepResult SecDbStepResult;
 575  
 576  static SecDbStepResult _SecDbStep(SecDbConnectionRef dbconn, sqlite3_stmt *stmt, CFErrorRef *error) {
 577      assert(stmt != NULL);
 578      int s3e;
 579      int ntries = 0;
 580      for (;;) {
 581          if (SecDbConnectionIsReadOnly(dbconn) && !sqlite3_stmt_readonly(stmt)) {
 582              secerror("_SecDbStep: SecDbConnection is readonly but we're about to write: %s", sqlite3_sql(stmt));
 583          }
 584          s3e = sqlite3_step(stmt);
 585          if (s3e == SQLITE_ROW) {
 586              return kSecDbRowStep;
 587          } else if (s3e == SQLITE_DONE) {
 588              /*
 589               ** ^[SQLITE_DONE] means that the statement has finished executing
 590               ** successfully.  sqlite3_step() should not be called again on this virtual
 591               ** machine without first calling [] to reset the virtual
 592               ** machine back to its initial state.
 593               */
 594              sqlite3_reset(stmt);
 595              return kSecDbDoneStep;
 596          } else if (!SecDbWaitIfNeeded(dbconn, s3e, stmt, CFSTR("step"), ntries, error)) {
 597              return kSecDbErrorStep;
 598          }
 599          ntries++;
 600      };
 601  }
 602  
 603  bool
 604  SecDbExec(SecDbConnectionRef dbconn, CFStringRef sql, CFErrorRef *error)
 605  {
 606      bool ok = true;
 607      CFRetain(sql);
 608      while (sql) {
 609          CFStringRef tail = NULL;
 610          if (ok) {
 611              sqlite3_stmt *stmt = SecDbCopyStmt(dbconn, sql, &tail, error);
 612              ok = stmt != NULL;
 613              if (stmt) {
 614                  SecDbStepResult sr;
 615                  while ((sr = _SecDbStep(dbconn, stmt, error)) == kSecDbRowStep);
 616                  if (sr == kSecDbErrorStep)
 617                      ok = false;
 618                  ok &= SecDbReleaseCachedStmt(dbconn, sql, stmt, error);
 619              }
 620          } else {
 621              // TODO We already have an error here we really just want the left over sql in it's userData
 622              ok = SecDbError(SQLITE_ERROR, error, CFSTR("Error with unexecuted sql remaining %@"), sql);
 623          }
 624          CFRelease(sql);
 625          sql = tail;
 626      }
 627      return ok;
 628  }
 629  
 630  static int SecDBGetInteger(SecDbConnectionRef dbconn, CFStringRef sql)
 631  {
 632      __block int number = -1;
 633      __block CFErrorRef error = NULL;
 634  
 635      (void)SecDbWithSQL(dbconn, sql, &error, ^bool(sqlite3_stmt *sqlStmt) {
 636          (void)SecDbStep(dbconn, sqlStmt, &error, ^(bool *stop) {
 637              number = sqlite3_column_int(sqlStmt, 0);
 638              *stop = true;
 639          });
 640          return true;
 641      });
 642      CFReleaseNull(error);
 643      return number;
 644  }
 645  
 646  
 647  void SecDBManagementTasks(SecDbConnectionRef dbconn)
 648  {
 649      int64_t page_count = SecDBGetInteger(dbconn, CFSTR("pragma page_count"));
 650      if (page_count <= 0) {
 651          return;
 652      }
 653      int64_t free_count = SecDBGetInteger(dbconn, CFSTR("pragma freelist_count"));
 654      if (free_count < 0) {
 655          return;
 656      }
 657  
 658      int64_t max_free = 8192;
 659  
 660      int64_t pages_in_use = page_count - free_count;
 661      double loadFactor = ((double)pages_in_use/(double)page_count);
 662      if (0.85 < loadFactor && free_count < max_free) {
 663          /* no work yet */
 664      } else {
 665          int64_t pages_to_free = (int64_t)(0.2 * free_count);
 666          if (0.4 > loadFactor) {
 667              pages_to_free = free_count;
 668          }
 669  
 670          char *formatString = NULL;
 671          asprintf(&formatString, "pragma incremental_vacuum(%d)", (int)pages_to_free);
 672          if (formatString) {
 673              char *sqlerror = NULL;
 674              int rc = sqlite3_exec(dbconn->handle, formatString, NULL, NULL, &sqlerror);
 675              if (rc) {
 676                  secerror("incremental_vacuum failed with: (%d) %{public}s", rc, sqlerror);
 677              }
 678              sqlite3_free(sqlerror);
 679              free(formatString);
 680          }
 681      }
 682  }
 683  
 684  
 685  static bool SecDbBeginTransaction(SecDbConnectionRef dbconn, SecDbTransactionType type, CFErrorRef *error)
 686  {
 687      bool ok = true;
 688      CFStringRef query;
 689      switch (type) {
 690          case kSecDbImmediateTransactionType:
 691              secdebug("db", "SecDbBeginTransaction SecDbBeginTransaction %p", dbconn);
 692              query = CFSTR("BEGIN IMMEDIATE");
 693              break;
 694          case kSecDbExclusiveRemoteSOSTransactionType:
 695              secdebug("db", "SecDbBeginTransaction kSecDbExclusiveRemoteSOSTransactionType %p", dbconn);
 696              dbconn->source = kSecDbSOSTransaction;
 697              query = CFSTR("BEGIN EXCLUSIVE");
 698              break;
 699          case kSecDbExclusiveRemoteCKKSTransactionType:
 700              secdebug("db", "SecDbBeginTransaction kSecDbExclusiveRemoteCKKSTransactionType %p", dbconn);
 701              dbconn->source = kSecDbCKKSTransaction;
 702              query = CFSTR("BEGIN EXCLUSIVE");
 703              break;
 704          case kSecDbExclusiveTransactionType:
 705              if (type==kSecDbExclusiveTransactionType)
 706                  secdebug("db", "SecDbBeginTransaction kSecDbExclusiveTransactionType %p", dbconn);
 707              query = CFSTR("BEGIN EXCLUSIVE");
 708              break;
 709          case kSecDbNormalTransactionType:
 710              secdebug("db", "SecDbBeginTransaction kSecDbNormalTransactionType %p", dbconn);
 711              query = CFSTR("BEGIN");
 712              break;
 713          default:
 714              secdebug("db", "SecDbBeginTransaction invalid transaction type %lu", type);
 715              ok = SecDbError(SQLITE_ERROR, error, CFSTR("invalid transaction type %d"), (int)type);
 716              query = NULL;
 717              break;
 718      }
 719  
 720      if (query != NULL && sqlite3_get_autocommit(dbconn->handle) != 0) {
 721          ok = SecDbExec(dbconn, query, error);
 722      }
 723      if (ok)
 724          dbconn->inTransaction = true;
 725  
 726      return ok;
 727  }
 728  
 729  static bool SecDbEndTransaction(SecDbConnectionRef dbconn, bool commit, CFErrorRef *error)
 730  {
 731      __block bool ok = true;
 732      __block bool commited = false;
 733  
 734      dispatch_block_t notifyAndExec = ^{
 735          if (commit) {
 736              //secdebug("db", "SecDbEndTransaction kSecDbTransactionWillCommit %p", dbconn);
 737              SecDbNotifyPhase(dbconn, kSecDbTransactionWillCommit);
 738              commited = ok = SecDbExec(dbconn, CFSTR("END"), error);
 739              //secdebug("db", "SecDbEndTransaction kSecDbTransactionWillCommit %p (after notify)", dbconn);
 740          } else {
 741              ok = SecDbExec(dbconn, CFSTR("ROLLBACK"), error);
 742              commited = false;
 743          }
 744          dbconn->inTransaction = false;
 745          SecDbNotifyPhase(dbconn, commited ? kSecDbTransactionDidCommit : kSecDbTransactionDidRollback);
 746          secdebug("db", "SecDbEndTransaction %s %p", commited ? "kSecDbTransactionDidCommit" : "kSecDbTransactionDidRollback", dbconn);
 747          dbconn->source = kSecDbAPITransaction;
 748  
 749          if (commit && dbconn->db->useRobotVacuum) {
 750              SecDBManagementTasks(dbconn);
 751          }
 752      };
 753  
 754      SecDbPerformOnCommitQueue(dbconn, true, notifyAndExec);
 755  
 756      return ok;
 757  }
 758  
 759  bool SecDbTransaction(SecDbConnectionRef dbconn, SecDbTransactionType type,
 760                        CFErrorRef *error, void (^transaction)(bool *commit))
 761  {
 762      bool ok = true;
 763      bool commit = true;
 764  
 765      if (dbconn->inTransaction) {
 766          transaction(&commit);
 767          if (!commit) {
 768              secinfo("#SecDB", "#SecDB nested transaction asked to not be committed");
 769          }
 770      } else {
 771          ok = SecDbBeginTransaction(dbconn, type, error);
 772          if (ok) {
 773              transaction(&commit);
 774              ok = SecDbEndTransaction(dbconn, commit, error);
 775          }
 776      }
 777  
 778      return ok && commit;
 779  }
 780  
 781  sqlite3 *SecDbHandle(SecDbConnectionRef dbconn) {
 782      return dbconn->handle;
 783  }
 784  
 785  bool SecDbStep(SecDbConnectionRef dbconn, sqlite3_stmt *stmt, CFErrorRef *error, void (^row)(bool *stop)) {
 786      for (;;) {
 787          switch (_SecDbStep(dbconn, stmt, error)) {
 788              case kSecDbErrorStep:
 789                  secdebug("db", "kSecDbErrorStep %@", error ? *error : NULL);
 790                  return false;
 791              case kSecDbRowStep:
 792  #if SECDB_DEBUGGING
 793                  secdebug("db", "kSecDbRowStep %@", error ? *error : NULL);
 794  #endif
 795                  if (row) {
 796                      __block bool stop = false;
 797                      SecAutoreleaseInvokeWithPool(^{
 798                          row(&stop);
 799                      });
 800                      if (stop)
 801                          return true;
 802                      break;
 803                  }
 804                  SecDbError(SQLITE_ERROR, error, CFSTR("SecDbStep SQLITE_ROW returned without a row handler"));
 805                  return false;
 806              case kSecDbDoneStep:
 807  #if SECDB_DEBUGGING
 808                  secdebug("db", "kSecDbDoneStep %@", error ? *error : NULL);
 809  #endif
 810                  return true;
 811          }
 812      }
 813  }
 814  
 815  bool SecDbCheckpoint(SecDbConnectionRef dbconn, CFErrorRef *error)
 816  {
 817      return SecDbConnectionCheckCode(dbconn,
 818                                      sqlite3_wal_checkpoint_v2(dbconn->handle, NULL, SQLITE_CHECKPOINT_FULL, NULL, NULL),
 819                                      error,
 820                                      CFSTR("wal_checkpoint(FULL)"));
 821  }
 822  
 823  static sqlite3 *_SecDbOpenV2(const char *path,
 824                               int flags,
 825                               int useWAL,
 826                               int useRobotVacuum,
 827                               CFErrorRef *error) {
 828      sqlite3 *handle = NULL;
 829      int s3e = sqlite3_open_v2(path, &handle, flags, NULL);
 830      if (s3e) {
 831          if (handle) {
 832              SecDbErrorWithDb(s3e, handle, error, CFSTR("open_v2 \"%s\" 0x%X"), path, flags);
 833              sqlite3_close(handle);
 834              handle = NULL;
 835          } else {
 836              SecDbError(s3e, error, CFSTR("open_v2 \"%s\" 0x%X"), path, flags);
 837          }
 838      } else if (SQLITE_OPEN_READWRITE == (flags & SQLITE_OPEN_READWRITE)) {
 839          if (useRobotVacuum) {
 840  #define SECDB_SQLITE_AUTO_VACUUM_INCREMENTAL 2
 841              sqlite3_stmt *stmt = NULL;
 842              int vacuumMode = -1;
 843  
 844              /*
 845               * Setting auto_vacuum = incremental on a database that is not empty requires
 846               * a VACCUUM, so check if the vacuum mode is not INCREMENTAL, and if its not,
 847               * set it to incremental and vacuum.
 848               */
 849  
 850              s3e = sqlite3_prepare_v2(handle, "PRAGMA auto_vacuum", -1, &stmt, NULL);
 851              if (s3e == 0) {
 852                  s3e = sqlite3_step(stmt);
 853                  if (s3e == SQLITE_ROW) {
 854                      vacuumMode = sqlite3_column_int(stmt, 0);
 855                  }
 856                  (void)sqlite3_finalize(stmt);
 857              }
 858  
 859              if (vacuumMode != SECDB_SQLITE_AUTO_VACUUM_INCREMENTAL) {
 860                  (void)sqlite3_exec(handle, "PRAGMA auto_vacuum = incremental", NULL, NULL, NULL);
 861                  (void)sqlite3_exec(handle, "VACUUM", NULL, NULL, NULL);
 862              }
 863          }
 864          if (useWAL) {
 865              (void)sqlite3_exec(handle, "PRAGMA journal_mode = WAL", NULL, NULL, NULL);
 866          }
 867  
 868          // Let SQLite handle timeouts.
 869          sqlite3_busy_timeout(handle, 5*1000);
 870      }
 871      return handle;
 872  }
 873  
 874  static bool SecDbOpenV2(SecDbConnectionRef dbconn, const char *path, int flags, CFErrorRef *error) {
 875      return (dbconn->handle = _SecDbOpenV2(path, flags, dbconn->db->useWAL, dbconn->db->useRobotVacuum, error)) != NULL;
 876  }
 877  
 878  // This construction lets tests not exit here
 879  static void SecDbProductionCorruptionExitHandler(void)
 880  {
 881      exit(EXIT_FAILURE);
 882  }
 883  void (*SecDbCorruptionExitHandler)(void) = SecDbProductionCorruptionExitHandler;
 884  
 885  void SecDbResetCorruptionExitHandler(void)
 886  {
 887      SecDbCorruptionExitHandler = SecDbProductionCorruptionExitHandler;
 888  }
 889  
 890  /*
 891   There's not much to do in here because we should only ever be here when
 892   SQLite tells us the DB is corrupt, or the DB is unrecoverable because of
 893   some fatal logic problem. But we can't shoot it dead either due to client
 894   connections. So, first we create a marker to tell ourselves things are bad,
 895   then we'll die. When we come back up we'll notice the marker and remove the DB.
 896   */
 897  static bool SecDbHandleCorrupt(SecDbConnectionRef dbconn, int rc, CFErrorRef *error)
 898  {
 899      if (!dbconn->db->allowRepair) {
 900          SecCFCreateErrorWithFormat(rc, kSecErrnoDomain, NULL, error, NULL,
 901                                     CFSTR("SecDbHandleCorrupt not allowed to repair, handled error: [%d] %s"), rc, strerror(rc));
 902          dbconn->isCorrupted = false;
 903          return false;
 904      }
 905  
 906      CFStringPerformWithCString(dbconn->db->db_path, ^(const char *db_path) {
 907          char marker[PATH_MAX+1];
 908          snprintf(marker, sizeof(marker), "%s-iscorrupt", db_path);
 909          struct stat info = {};
 910          if (0 == stat(marker, &info)) {
 911              secerror("SecDbHandleCorrupt: Tried to write corruption marker %s but one already exists", marker);
 912          }
 913  
 914          FILE* file = fopen(marker, "w");
 915          if (file == NULL) {
 916              secerror("SecDbHandleCorrupt: Unable (%{darwin.errno}d) to create corruption marker %{public}s", errno, marker);
 917          } else {
 918              fclose(file);
 919          }
 920      });
 921  
 922      secwarning("SecDbHandleCorrupt: killing self so that successor might cleanly delete corrupt db");
 923  
 924      // Call through function pointer so tests can replace it and call a SecKeychainDbReset instead
 925      SecDbCorruptionExitHandler();
 926      return true;
 927  }
 928  
 929  static bool SecDbProcessCorruptionMarker(CFStringRef db_path) {
 930      __block bool ok = true;
 931      CFStringPerformWithCString(db_path, ^(const char *db_path) {
 932          char marker[PATH_MAX+1];
 933          snprintf(marker, sizeof(marker), "%s-iscorrupt", db_path);
 934          struct stat info = {};
 935          int result = stat(marker, &info);
 936          if (result != 0 && errno == ENOENT) {
 937              return;
 938          } else if (result != 0) {
 939              secerror("SecDbSecDbProcessCorruptionMarker: Unable to check for corruption marker: %{darwin.errno}d", errno);
 940              return;
 941          }
 942  
 943          secwarning("SecDbSecDbProcessCorruptionMarker: found corruption marker %s", marker);
 944          if (remove(marker)) {
 945              secerror("SecDbSecDbProcessCorruptionMarker: Unable (%{darwin.errno}d) to delete corruption marker", errno);
 946              ok = false;
 947          } else if (remove(db_path) && errno != ENOENT) {    // Not sure how we'd get ENOENT but it would suit us just fine
 948              secerror("SecDbSecDbProcessCorruptionMarker: Unable (%{darwin.errno}d) to delete db %{public}s", errno, db_path);
 949              ok = false;
 950          } else {
 951              secwarning("SecDbSecDbProcessCorruptionMarker: deleted corrupt db %{public}s", db_path);
 952          }
 953      });
 954      return ok;
 955  }
 956  
 957  void
 958  SecDbSetCorruptionReset(SecDbRef db, void (^corruptionReset)(void))
 959  {
 960      if (db->corruptionReset) {
 961          Block_release(db->corruptionReset);
 962          db->corruptionReset = NULL;
 963      }
 964      if (corruptionReset) {
 965          db->corruptionReset = Block_copy(corruptionReset);
 966      }
 967  }
 968  
 969  static bool SecDbLoggingEnabled(CFStringRef type)
 970  {
 971      CFTypeRef profile = NULL;
 972      bool enabled = false;
 973  
 974      if (csr_check(CSR_ALLOW_APPLE_INTERNAL) != 0)
 975          return false;
 976  
 977      profile = (CFNumberRef)CFPreferencesCopyValue(CFSTR("SQLProfile"), CFSTR("com.apple.security"), kCFPreferencesAnyUser, kCFPreferencesAnyHost);
 978  
 979      if (profile == NULL)
 980          return false;
 981  
 982      if (CFGetTypeID(profile) == CFBooleanGetTypeID()) {
 983          enabled = CFBooleanGetValue((CFBooleanRef)profile);
 984      } else if (CFGetTypeID(profile) == CFNumberGetTypeID()) {
 985          int32_t num = 0;
 986          CFNumberGetValue(profile, kCFNumberSInt32Type, &num);
 987          enabled = !!num;
 988      }
 989  
 990      CFReleaseSafe(profile);
 991  
 992      return enabled;
 993  }
 994  
 995  static unsigned
 996  SecDbProfileMask(void)
 997  {
 998      static dispatch_once_t onceToken;
 999      static unsigned profile_mask = 0;
1000  
1001      // sudo defaults write /Library/Preferences/com.apple.security SQLProfile -bool true
1002      dispatch_once(&onceToken, ^{
1003          if (SecDbLoggingEnabled(CFSTR("SQLProfile")))
1004              profile_mask = SQLITE_TRACE_PROFILE;
1005  #if DEBUG
1006          profile_mask |= SQLITE_TRACE_STMT;
1007  #else
1008          if (SecDbLoggingEnabled(CFSTR("SQLTrace")))
1009              profile_mask = SQLITE_TRACE_STMT;
1010  #endif
1011          if (SecDbLoggingEnabled(CFSTR("SQLRow")))
1012              profile_mask = SQLITE_TRACE_ROW;
1013          secinfo("#SecDB", "sqlDb: sql trace mask: 0x%08x", profile_mask);
1014      });
1015      return profile_mask;
1016  }
1017  
1018  static int
1019  SecDbTraceV2(unsigned mask, void *ctx, void *p, void *x) {
1020      SecDbConnectionRef dbconn __unused = ctx;
1021  
1022  #if SECDB_DEBUGGING
1023      const char *trace = "unknown";
1024      char *tofree = NULL;
1025  
1026      if (mask == SQLITE_TRACE_PROFILE)
1027          trace = sqlite3_sql(p);
1028      else if (mask == SQLITE_TRACE_STMT) {
1029          trace = sqlite3_sql(p);
1030      } else if (mask == SQLITE_TRACE_ROW) {
1031          trace = tofree = sqlite3_expanded_sql(p);
1032      }
1033  
1034      secinfo("#SecDB", "#SecDB %{public}s", trace);
1035  
1036      sqlite3_free(tofree);
1037  #endif
1038  
1039      return 0;
1040  }
1041  
1042  
1043  static bool SecDbOpenHandle(SecDbConnectionRef dbconn, bool *created, CFErrorRef *error)
1044  {
1045      __block bool ok = true;
1046  
1047      // This is pretty terrible because now what? We know we have a corrupt DB
1048      // and now we can't get rid of it.
1049      if (!SecDbProcessCorruptionMarker(dbconn->db->db_path)) {
1050          SecCFCreateErrorWithFormat(errno, kSecErrnoDomain, NULL, error, NULL, CFSTR("Unable to process corruption marker: %{darwin.errno}d"), errno);
1051          return false;
1052      }
1053  
1054      CFStringPerformWithCString(dbconn->db->db_path, ^(const char *db_path) {
1055  #if TARGET_OS_IPHONE
1056          int flags = SQLITE_OPEN_FILEPROTECTION_NONE;
1057  #else
1058          int flags = 0;
1059  #endif
1060          flags |= (dbconn->db->readWrite) ? SQLITE_OPEN_READWRITE : SQLITE_OPEN_READONLY;
1061          ok = created && SecDbOpenV2(dbconn, db_path, flags, NULL);
1062          if (!ok) {
1063              ok = true;
1064              if (created) {
1065                  char *tmp = dirname((char *)db_path);
1066                  if (tmp) {
1067                      mode_t omode = dbconn->db->mode;
1068                      if (omode & S_IRUSR) { omode |= S_IXUSR; } // owner can read
1069                      if (omode & S_IRGRP) { omode |= S_IXGRP; } // group can read
1070                      if (omode & S_IROTH) { omode |= S_IXOTH; } // other can read
1071                      int errnum = mkpath_np(tmp, omode);
1072                      if (errnum != 0 && errnum != EEXIST) {
1073                          SecCFCreateErrorWithFormat(errnum, kSecErrnoDomain, NULL, error, NULL,
1074                                                     CFSTR("mkpath_np %s: [%d] %s"), tmp, errnum, strerror(errnum));
1075                          ok = false;
1076                      }
1077                  }
1078              }
1079              // if the enclosing directory is ok, try to create the database.
1080              // this forces us to open it read-write, so we'll need to be the owner here.
1081              flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
1082  #if TARGET_OS_IPHONE
1083              flags |= SQLITE_OPEN_FILEPROTECTION_NONE;
1084  #endif
1085              ok = ok && SecDbOpenV2(dbconn, db_path, flags, error);
1086              if (ok) {
1087                  chmod(db_path, dbconn->db->mode); // default: 0600 (S_IRUSR | S_IWUSR)
1088                  if (created)
1089                      *created = true;
1090              }
1091          }
1092  
1093          if (ok) {
1094              unsigned mask = SecDbProfileMask();
1095              if (mask) {
1096                  (void)sqlite3_trace_v2(dbconn->handle,
1097                                         mask,
1098                                         SecDbTraceV2,
1099                                         dbconn);
1100              }
1101          }
1102      });
1103  
1104      return ok;
1105  }
1106  
1107  static SecDbConnectionRef
1108  SecDbConnectionCreate(SecDbRef db, bool readOnly, CFErrorRef *error)
1109  {
1110      SecDbConnectionRef dbconn = NULL;
1111  
1112      dbconn = CFTypeAllocate(SecDbConnection, struct __OpaqueSecDbConnection, kCFAllocatorDefault);
1113      require(dbconn != NULL, done);
1114  
1115      dbconn->db = db;
1116      dbconn->readOnly = readOnly;
1117      dbconn->inTransaction = false;
1118      dbconn->source = kSecDbInvalidTransaction;
1119      dbconn->isCorrupted = false;
1120      dbconn->maybeCorruptedCode = 0;
1121      dbconn->hasIOFailure = false;
1122      dbconn->corruptionError = NULL;
1123      dbconn->handle = NULL;
1124      dbconn->changes = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
1125  
1126  done:
1127      return dbconn;
1128  }
1129  
1130  bool SecDbConnectionIsReadOnly(SecDbConnectionRef dbconn) {
1131      return dbconn->readOnly;
1132  }
1133  
1134  static void SecDbConectionSetReadOnly(SecDbConnectionRef dbconn, bool readOnly) {
1135      dbconn->readOnly = readOnly;
1136  }
1137  
1138  SecDbConnectionRef SecDbConnectionAcquire(SecDbRef db, bool readOnly, CFErrorRef *error) {
1139      SecDbConnectionRef dbconn = NULL;
1140      SecDbConnectionAcquireRefMigrationSafe(db, readOnly, &dbconn, error);
1141      return dbconn;
1142  }
1143  
1144  static void SecDbConnectionConsumeResource(SecDbRef db, bool readOnly) {
1145      if (readOnly) {
1146          dispatch_semaphore_wait(db->readSemaphore, DISPATCH_TIME_FOREVER);
1147      } else {
1148          pthread_mutex_lock(&(db->writeMutex));
1149      }
1150  }
1151  
1152  static void SecDbConnectionMakeResourceAvailable(SecDbRef db, bool readOnly) {
1153      if (readOnly) {
1154          dispatch_semaphore_signal(db->readSemaphore);
1155      } else {
1156          pthread_mutex_unlock(&(db->writeMutex));
1157      }
1158  }
1159  
1160  bool SecDbConnectionAcquireRefMigrationSafe(SecDbRef db, bool readOnly, SecDbConnectionRef* dbconnRef, CFErrorRef *error)
1161  {
1162      CFRetain(db);
1163  #if SECDB_DEBUGGING
1164      secinfo("dbconn", "acquire %s connection", readOnly ? "ro" : "rw");
1165  #endif
1166      SecDbConnectionConsumeResource(db, readOnly);
1167  
1168      __block SecDbConnectionRef dbconn = NULL;
1169      __block bool ok = true;
1170      __block bool ranOpenedHandler = false;
1171  
1172      bool (^assignDbConn)(SecDbConnectionRef) = ^bool(SecDbConnectionRef connection) {
1173          dbconn = connection;
1174          if (dbconnRef) {
1175              *dbconnRef = connection;
1176          }
1177  
1178          return dbconn != NULL;
1179      };
1180  
1181      dispatch_sync(db->queue, ^{
1182          if (!db->didFirstOpen) {
1183              bool didCreate = false;
1184              ok = assignDbConn(SecDbConnectionCreate(db, false, error));
1185              CFErrorRef localError = NULL;
1186              if (ok && !SecDbOpenHandle(dbconn, &didCreate, &localError)) {
1187                  secerror("Unable to create database: %@", localError);
1188                  if (localError && CFEqual(CFErrorGetDomain(localError), kSecDbErrorDomain)) {
1189                      int code = (int)CFErrorGetCode(localError);
1190                      dbconn->isCorrupted = (SQLITE_CORRUPT == code) || (SQLITE_NOTADB == code);
1191                  }
1192                  // If the open failure isn't due to corruption, propagate the error.
1193                  ok = dbconn->isCorrupted;
1194                  if (!ok && error && *error == NULL) {
1195                      *error = localError;
1196                      localError = NULL;
1197                  }
1198              }
1199              CFReleaseNull(localError);
1200  
1201              if (ok) {
1202                  db->didFirstOpen = ok = SecDbDidCreateFirstConnection(dbconn, didCreate, error);
1203                  ranOpenedHandler = true;
1204                  SecDbConectionSetReadOnly(dbconn, readOnly);    // first connection always created "rw", so set to real value
1205              } else {
1206                  CFReleaseNull(dbconn);
1207              }
1208          } else {
1209              /* Try to get one from the cache */
1210              CFMutableArrayRef cache = readOnly ? db->idleReadConnections : db->idleWriteConnections;
1211              if (CFArrayGetCount(cache) && !dbconn) {
1212                  if (assignDbConn((SecDbConnectionRef)CFArrayGetValueAtIndex(cache, 0))) {
1213                      CFRetainSafe(dbconn);
1214                  }
1215                  CFArrayRemoveValueAtIndex(cache, 0);
1216              }
1217          }
1218      });
1219  
1220      if (ok && !dbconn) {
1221          /* Nothing found in cache, create a new connection */
1222          bool created = false;
1223          if (assignDbConn(SecDbConnectionCreate(db, readOnly, error)) && !SecDbOpenHandle(dbconn, &created, error)) {
1224              CFReleaseNull(dbconn);
1225          }
1226      }
1227  
1228      if (dbconn && !ranOpenedHandler && dbconn->db->opened) {
1229          dispatch_sync(db->queue, ^{
1230              if (dbconn->db->callOpenedHandlerForNextConnection) {
1231                  dbconn->db->callOpenedHandlerForNextConnection = false;
1232                  if (!dbconn->db->opened(db, dbconn, false, &dbconn->db->callOpenedHandlerForNextConnection, error)) {
1233                      if (!dbconn->isCorrupted || !SecDbHandleCorrupt(dbconn, 0, error)) {
1234                          CFReleaseNull(dbconn);
1235                      }
1236                  }
1237              }
1238          });
1239      }
1240  
1241      if (dbconnRef) {
1242          *dbconnRef = dbconn;
1243      }
1244  
1245      if (!dbconn) {
1246          // Caller doesn't get (to use) a connection so the backing synchronization primitive is available again
1247          SecDbConnectionMakeResourceAvailable(db, readOnly);
1248          CFRelease(db);
1249      }
1250  
1251      return dbconn ? true : false;
1252  }
1253  
1254  void SecDbConnectionRelease(SecDbConnectionRef dbconn) {
1255      if (!dbconn) {
1256          secerror("SecDbConnectionRelease called with NULL dbconn");
1257          return;
1258      }
1259      SecDbRef db = dbconn->db;
1260  #if SECDB_DEBUGGING
1261      secinfo("dbconn", "release %@", dbconn);
1262  #endif
1263  
1264      bool readOnly = SecDbConnectionIsReadOnly(dbconn);
1265      dispatch_sync(db->queue, ^{
1266          if (dbconn->hasIOFailure) {
1267              // Something wrong on the file layer (e.g. revoked file descriptor for networked home)
1268              secwarning("SecDbConnectionRelease: IO failure reported in connection, throwing away currently idle caches");
1269              // Any other checked-out connections are beyond our grasp. If they did not have IO failures they'll come back,
1270              // otherwise this branch gets taken more than once and gradually those connections die off
1271              CFArrayRemoveAllValues(db->idleWriteConnections);
1272              CFArrayRemoveAllValues(db->idleReadConnections);
1273          } else {
1274              CFMutableArrayRef cache = readOnly ? db->idleReadConnections : db->idleWriteConnections;
1275              CFIndex count = CFArrayGetCount(cache);
1276              if ((unsigned long)count < (readOnly ? kSecDbMaxReaders : kSecDbMaxWriters)) {
1277                  CFArrayAppendValue(cache, dbconn);
1278              } else {
1279                  secerror("dbconn: did not expect to run out of room in the %s cache when releasing connection", readOnly ? "ro" : "rw");
1280              }
1281          }
1282      });
1283  
1284      // Signal after we have put the connection back in the pool of connections
1285      SecDbConnectionMakeResourceAvailable(db, readOnly);
1286      CFRelease(dbconn);
1287      CFRelease(db);
1288  }
1289  
1290  void SecDbReleaseAllConnections(SecDbRef db) {
1291      // Force all connections to be removed (e.g. file descriptor no longer valid)
1292      if (!db) {
1293          secerror("called with NULL db");
1294          return;
1295      }
1296      dispatch_sync(db->queue, ^{
1297          CFArrayRemoveAllValues(db->idleReadConnections);
1298          CFArrayRemoveAllValues(db->idleWriteConnections);
1299      });
1300  }
1301  
1302  static void onQueueSecDbForceCloseForCache(CFMutableArrayRef cache) {
1303      CFArrayForEach(cache, ^(const void* ptr) {
1304          SecDbConnectionRef connection = (SecDbConnectionRef)ptr;
1305  
1306          // this pointer is claimed to be nonretained
1307          connection->db = NULL;
1308  
1309          if(connection->handle) {
1310              sqlite3_close(connection->handle);
1311              connection->handle = NULL;
1312          }
1313      });
1314      CFArrayRemoveAllValues(cache);
1315  }
1316  
1317  // Please make sure you want to do this. Any use of the outstanding connections to this DB will cause a crash.
1318  void SecDbForceClose(SecDbRef db) {
1319      dispatch_sync(db->queue, ^{
1320          onQueueSecDbForceCloseForCache(db->idleReadConnections);
1321          onQueueSecDbForceCloseForCache(db->idleWriteConnections);
1322      });
1323  }
1324  
1325  bool SecDbPerformRead(SecDbRef db, CFErrorRef *error, void (^perform)(SecDbConnectionRef dbconn)) {
1326      SecDbConnectionRef dbconn = SecDbConnectionAcquire(db, true, error);
1327      bool success = false;
1328      if (dbconn) {
1329          perform(dbconn);
1330          success = true;
1331          SecDbConnectionRelease(dbconn);
1332      }
1333      return success;
1334  }
1335  
1336  bool SecDbPerformWrite(SecDbRef db, CFErrorRef *error, void (^perform)(SecDbConnectionRef dbconn)) {
1337      if(!db) {
1338          SecError(errSecNotAvailable, error, CFSTR("failed to get a db handle"));
1339          return false;
1340      }
1341      SecDbConnectionRef dbconn = SecDbConnectionAcquire(db, false, error);
1342      bool success = false;
1343      if (dbconn) {
1344          perform(dbconn);
1345          success = true;
1346          SecDbConnectionRelease(dbconn);
1347      }
1348      return success;
1349  }
1350  
1351  static CFStringRef
1352  SecDbConnectionCopyFormatDescription(CFTypeRef value, CFDictionaryRef formatOptions)
1353  {
1354      SecDbConnectionRef dbconn = (SecDbConnectionRef)value;
1355      return CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("<SecDbConnection %s %s>"),
1356                                      dbconn->readOnly ? "ro" : "rw", dbconn->handle ? "open" : "closed");
1357  }
1358  
1359  static void
1360  SecDbConnectionDestroy(CFTypeRef value)
1361  {
1362      SecDbConnectionRef dbconn = (SecDbConnectionRef)value;
1363      if (dbconn->handle) {
1364          int s3e = sqlite3_close(dbconn->handle);
1365          if (s3e != SQLITE_OK) {
1366              secerror("failed to close database connection (%d) for %@: %s", s3e, dbconn->db->db_path, sqlite3_errmsg(dbconn->handle));
1367          }
1368          os_assert(s3e == SQLITE_OK); // Crash now or jetsam later
1369      }
1370      dbconn->db = NULL;
1371      CFReleaseNull(dbconn->changes);
1372      CFReleaseNull(dbconn->corruptionError);
1373  
1374  }
1375  
1376  void SecDbPerformOnCommitQueue(SecDbConnectionRef dbconn, bool barrier, dispatch_block_t perform) {
1377      if (barrier) {
1378          dispatch_barrier_sync(dbconn->db->commitQueue, ^{
1379              perform();
1380          });
1381      } else {
1382          dispatch_sync(dbconn->db->commitQueue, ^{
1383              perform();
1384          });
1385      }
1386  }
1387  
1388  // MARK: -
1389  // MARK: Bind helpers
1390  
1391  // Logging binds is very spammy when debug logging is on (~90% of log lines), and isn't often useful.
1392  // Enable this in your local build if you actually want every single SQL variable bind logged for debugging.
1393  #define LOG_SECDB_BINDS 0
1394  
1395  bool SecDbBindBlob(sqlite3_stmt *stmt, int param, const void *zData, size_t n, void(*xDel)(void*), CFErrorRef *error) {
1396      if (n > INT_MAX) {
1397          return SecDbErrorWithStmt(SQLITE_TOOBIG, stmt, error,
1398                                    CFSTR("bind_blob[%d]: blob bigger than INT_MAX"), param);
1399      }
1400      bool ok = SecDbErrorWithStmt(sqlite3_bind_blob(stmt, param, zData, (int)n, xDel),
1401                                   stmt, error, CFSTR("bind_blob[%d]"), param);
1402  #if LOG_SECDB_BINDS
1403      secinfo("bind", "bind_blob[%d]: %.*P: %@", param, (int)n, zData, error ? *error : NULL);
1404  #endif
1405      return ok;
1406  }
1407  
1408  bool SecDbBindText(sqlite3_stmt *stmt, int param, const char *zData, size_t n, void(*xDel)(void*), CFErrorRef *error) {
1409      if (n > INT_MAX) {
1410          return SecDbErrorWithStmt(SQLITE_TOOBIG, stmt, error,
1411                                    CFSTR("bind_text[%d]: text bigger than INT_MAX"), param);
1412      }
1413      bool ok = SecDbErrorWithStmt(sqlite3_bind_text(stmt, param, zData, (int)n, xDel), stmt, error,
1414                                   CFSTR("bind_text[%d]"), param);
1415  #if LOG_SECDB_BINDS
1416      secinfo("bind", "bind_text[%d]: \"%s\" error: %@", param, zData, error ? *error : NULL);
1417  #endif
1418      return ok;
1419  }
1420  
1421  bool SecDbBindDouble(sqlite3_stmt *stmt, int param, double value, CFErrorRef *error) {
1422      bool ok = SecDbErrorWithStmt(sqlite3_bind_double(stmt, param, value), stmt, error,
1423                                   CFSTR("bind_double[%d]"), param);
1424  #if LOG_SECDB_BINDS
1425      secinfo("bind", "bind_double[%d]: %f error: %@", param, value, error ? *error : NULL);
1426  #endif
1427      return ok;
1428  }
1429  
1430  bool SecDbBindInt(sqlite3_stmt *stmt, int param, int value, CFErrorRef *error) {
1431      bool ok = SecDbErrorWithStmt(sqlite3_bind_int(stmt, param, value), stmt, error,
1432                                   CFSTR("bind_int[%d]"), param);
1433  #if LOG_SECDB_BINDS
1434      secinfo("bind", "bind_int[%d]: %d error: %@", param, value, error ? *error : NULL);
1435  #endif
1436      return ok;
1437  }
1438  
1439  bool SecDbBindInt64(sqlite3_stmt *stmt, int param, sqlite3_int64 value, CFErrorRef *error) {
1440      bool ok = SecDbErrorWithStmt(sqlite3_bind_int64(stmt, param, value), stmt, error,
1441                                   CFSTR("bind_int64[%d]"), param);
1442  #if LOG_SECDB_BINDS
1443      secinfo("bind", "bind_int64[%d]: %lld error: %@", param, value, error ? *error : NULL);
1444  #endif
1445      return ok;
1446  }
1447  
1448  
1449  /* AUDIT[securityd](done):
1450   value (ok) is a caller provided, non NULL CFTypeRef.
1451   */
1452  bool SecDbBindObject(sqlite3_stmt *stmt, int param, CFTypeRef value, CFErrorRef *error) {
1453      CFTypeID valueId;
1454      __block bool result = false;
1455  
1456  	/* TODO: Can we use SQLITE_STATIC below everwhere we currently use
1457       SQLITE_TRANSIENT since we finalize the statement before the value
1458       goes out of scope? */
1459      if (!value || (valueId = CFGetTypeID(value)) == CFNullGetTypeID()) {
1460          /* Skip bindings for NULL values.  sqlite3 will interpret unbound
1461           params as NULL which is exactly what we want. */
1462          result = true;
1463      } else if (valueId == CFStringGetTypeID()) {
1464          CFStringPerformWithCStringAndLength(value, ^(const char *cstr, size_t clen) {
1465              result = SecDbBindText(stmt, param, cstr, clen, SQLITE_TRANSIENT, error);
1466          });
1467      } else if (valueId == CFDataGetTypeID()) {
1468          CFIndex len = CFDataGetLength(value);
1469          if (len) {
1470              result = SecDbBindBlob(stmt, param, CFDataGetBytePtr(value),
1471                                     len, SQLITE_TRANSIENT, error);
1472          } else {
1473              result = SecDbBindText(stmt, param, "", 0, SQLITE_TRANSIENT, error);
1474          }
1475      } else if (valueId == CFDateGetTypeID()) {
1476          CFAbsoluteTime abs_time = CFDateGetAbsoluteTime(value);
1477          result = SecDbBindDouble(stmt, param, abs_time, error);
1478      } else if (valueId == CFBooleanGetTypeID()) {
1479          int bval = CFBooleanGetValue(value);
1480          result = SecDbBindInt(stmt, param, bval, error);
1481      } else if (valueId == CFNumberGetTypeID()) {
1482          Boolean convertOk;
1483          if (CFNumberIsFloatType(value)) {
1484              double nval;
1485              convertOk = CFNumberGetValue(value, kCFNumberDoubleType, &nval);
1486              result = SecDbBindDouble(stmt, param, nval, error);
1487          } else {
1488              sqlite_int64 nval64;
1489              convertOk = CFNumberGetValue(value, kCFNumberSInt64Type, &nval64);
1490              if (convertOk) {
1491                  result = SecDbBindInt64(stmt, param, nval64, error);
1492              }
1493          }
1494          if (!convertOk) {
1495              result = SecDbError(SQLITE_INTERNAL, error, CFSTR("bind CFNumberGetValue failed for %@"), value);
1496          }
1497      } else {
1498          if (error) {
1499              CFStringRef valueDesc = CFCopyTypeIDDescription(valueId);
1500              SecDbError(SQLITE_MISMATCH, error, CFSTR("bind unsupported type %@"), valueDesc);
1501              CFReleaseSafe(valueDesc);
1502          }
1503      }
1504  
1505  	return result;
1506  }
1507  
1508  // MARK: -
1509  // MARK: SecDbStatementRef
1510  
1511  bool SecDbReset(sqlite3_stmt *stmt, CFErrorRef *error) {
1512      return SecDbErrorWithStmt(sqlite3_reset(stmt), stmt, error, CFSTR("reset"));
1513  }
1514  
1515  bool SecDbClearBindings(sqlite3_stmt *stmt, CFErrorRef *error) {
1516      return SecDbErrorWithStmt(sqlite3_clear_bindings(stmt), stmt, error, CFSTR("clear bindings"));
1517  }
1518  
1519  bool SecDbFinalize(sqlite3_stmt *stmt, CFErrorRef *error) {
1520      sqlite3 *handle = sqlite3_db_handle(stmt);
1521      int s3e = sqlite3_finalize(stmt);
1522      return s3e == SQLITE_OK ? true : SecDbErrorWithDb(s3e, handle, error, CFSTR("finalize: %p"), stmt);
1523  }
1524  
1525  sqlite3_stmt *SecDbPrepareV2(SecDbConnectionRef dbconn, const char *sql, size_t sqlLen, const char **sqlTail, CFErrorRef *error) {
1526      sqlite3 *db = SecDbHandle(dbconn);
1527      if (sqlLen > INT_MAX) {
1528          SecDbErrorWithDb(SQLITE_TOOBIG, db, error, CFSTR("prepare_v2: sql bigger than INT_MAX"));
1529          return NULL;
1530      }
1531      int ntries = 0;
1532      for (;;) {
1533          sqlite3_stmt *stmt = NULL;
1534          int s3e = sqlite3_prepare_v2(db, sql, (int)sqlLen, &stmt, sqlTail);
1535          if (s3e == SQLITE_OK)
1536              return stmt;
1537          else if (!SecDbWaitIfNeeded(dbconn, s3e, NULL, CFSTR("preparev2"), ntries, error))
1538              return NULL;
1539          ntries++;
1540      }
1541  }
1542  
1543  static sqlite3_stmt *SecDbCopyStatementWithTailRange(SecDbConnectionRef dbconn, CFStringRef sql, CFRange *sqlTail, CFErrorRef *error) {
1544      __block sqlite3_stmt *stmt = NULL;
1545      if (sql) CFStringPerformWithCStringAndLength(sql, ^(const char *sqlStr, size_t sqlLen) {
1546          const char *tail = NULL;
1547          stmt = SecDbPrepareV2(dbconn, sqlStr, sqlLen, &tail, error);
1548          if (sqlTail && sqlStr < tail && tail < sqlStr + sqlLen) {
1549              sqlTail->location = tail - sqlStr;
1550              sqlTail->length = sqlLen - sqlTail->location;
1551          }
1552      });
1553  
1554      return stmt;
1555  }
1556  
1557  sqlite3_stmt *SecDbCopyStmt(SecDbConnectionRef dbconn, CFStringRef sql, CFStringRef *tail, CFErrorRef *error) {
1558      // TODO: Add caching and cache lookup of statements
1559      CFRange sqlTail = {};
1560      sqlite3_stmt *stmt = SecDbCopyStatementWithTailRange(dbconn, sql, &sqlTail, error);
1561      if (sqlTail.length > 0) {
1562          CFStringRef excess = CFStringCreateWithSubstring(CFGetAllocator(sql), sql, sqlTail);
1563          if (tail) {
1564              *tail = excess;
1565          } else {
1566              SecDbError(SQLITE_INTERNAL, error,
1567                         CFSTR("prepare_v2: %@ unused sql: %@"),
1568                         sql, excess);
1569              CFReleaseSafe(excess);
1570              SecDbFinalize(stmt, error);
1571              stmt = NULL;
1572          }
1573      }
1574      return stmt;
1575  }
1576  
1577  /*
1578   TODO: Could do a hack here with a custom kCFAllocatorNULL allocator for a second CFRuntimeBase inside a SecDbStatement,
1579   TODO: Better yet make a full blow SecDbStatement instance whenever SecDbCopyStmt is called.  Then, when the statement is released, in the Dispose method, we Reset and ClearBindings the sqlite3_stmt * and hand it back to the SecDb with the original CFStringRef for the sql (or hash thereof) as an argument. */
1580  bool SecDbReleaseCachedStmt(SecDbConnectionRef dbconn, CFStringRef sql, sqlite3_stmt *stmt, CFErrorRef *error) {
1581      if (stmt) {
1582          return SecDbFinalize(stmt, error);
1583      }
1584      return true;
1585  }
1586  
1587  bool SecDbPrepare(SecDbConnectionRef dbconn, CFStringRef sql, CFErrorRef *error, void(^exec)(sqlite3_stmt *stmt)) {
1588      assert(sql != NULL);
1589      sqlite3_stmt *stmt = SecDbCopyStmt(dbconn, sql, NULL, error);
1590      if (!stmt)
1591          return false;
1592  
1593      exec(stmt);
1594      return SecDbReleaseCachedStmt(dbconn, sql, stmt, error);
1595  }
1596  
1597  bool SecDbWithSQL(SecDbConnectionRef dbconn, CFStringRef sql, CFErrorRef *error, bool(^perform)(sqlite3_stmt *stmt)) {
1598      bool ok = true;
1599      CFRetain(sql);
1600      while (sql) {
1601          CFStringRef tail = NULL;
1602          if (ok) {
1603              sqlite3_stmt *stmt = SecDbCopyStmt(dbconn, sql, &tail, error);
1604              ok = stmt != NULL;
1605              if (stmt) {
1606                  if (perform) {
1607                      ok = perform(stmt);
1608                  } else {
1609                      // TODO: Use a different error scope here.
1610                      ok = SecError(-50 /* errSecParam */, error, CFSTR("SecDbWithSQL perform block missing"));
1611                  }
1612                  ok &= SecDbReleaseCachedStmt(dbconn, sql, stmt, error);
1613              }
1614          } else {
1615              // TODO We already have an error here we really just want the left over sql in it's userData
1616              ok = SecDbError(SQLITE_ERROR, error, CFSTR("Error with unexecuted sql remaining %@"), sql);
1617          }
1618          CFRelease(sql);
1619          sql = tail;
1620      }
1621      return ok;
1622  }
1623  
1624  /* SecDbForEach returns true if all SQLITE_ROW returns of sqlite3_step() return true from the row block.
1625   If the row block returns false and doesn't set an error (to indicate it has reached a limit),
1626   this entire function returns false. In that case no error will be set. */
1627  bool SecDbForEach(SecDbConnectionRef dbconn, sqlite3_stmt *stmt, CFErrorRef *error, bool(^row)(int row_index)) {
1628      bool result = false;
1629      for (int row_ix = 0;;++row_ix) {
1630          if (SecDbConnectionIsReadOnly(dbconn) && !sqlite3_stmt_readonly(stmt)) {
1631              secerror("SecDbForEach: SecDbConnection is readonly but we're about to write: %s", sqlite3_sql(stmt));
1632          }
1633          int s3e = sqlite3_step(stmt);
1634          if (s3e == SQLITE_ROW) {
1635              if (row) {
1636                  if (!row(row_ix)) {
1637                      break;
1638                  }
1639              } else {
1640                  // If we have no row block then getting SQLITE_ROW is an error
1641                  SecDbError(s3e, error,
1642                             CFSTR("step[%d]: %s returned SQLITE_ROW with NULL row block"),
1643                             row_ix, sqlite3_sql(stmt));
1644              }
1645          } else {
1646              if (s3e == SQLITE_DONE) {
1647                  result = true;
1648              } else {
1649                  SecDbConnectionCheckCode(dbconn, s3e, error, CFSTR("SecDbForEach step[%d]"), row_ix);
1650              }
1651              break;
1652          }
1653      }
1654      return result;
1655  }
1656  
1657  void SecDbRecordChange(SecDbConnectionRef dbconn, CFTypeRef deleted, CFTypeRef inserted) {
1658      if (!dbconn->db->notifyPhase) return;
1659      CFTypeRef entry = SecDbEventCreateWithComponents(deleted, inserted);
1660      if (entry) {
1661          CFArrayAppendValue(dbconn->changes, entry);
1662          CFRelease(entry);
1663  
1664          if (!dbconn->inTransaction) {
1665              secerror("db %@ changed outside txn", dbconn);
1666              // Only notify of DidCommit, since WillCommit code assumes
1667              // we are in a txn.
1668              SecDbOnNotify(dbconn, ^{
1669                  SecDbNotifyPhase(dbconn, kSecDbTransactionDidCommit);
1670              });
1671          }
1672      }
1673  }
1674  
1675  
1676  CFGiblisFor(SecDbConnection)
1677  
1678  //
1679  // SecDbEvent Creation and consumption
1680  //
1681  
1682  static SecDbEventRef SecDbEventCreateInsert(CFTypeRef inserted) {
1683      return CFRetainSafe(inserted);
1684  }
1685  
1686  static SecDbEventRef SecDbEventCreateDelete(CFTypeRef deleted) {
1687      return CFArrayCreate(kCFAllocatorDefault, &deleted, 1, &kCFTypeArrayCallBacks);
1688  }
1689  
1690  static SecDbEventRef SecDbEventCreateUpdate(CFTypeRef deleted, CFTypeRef inserted) {
1691      const void *values[2] = { deleted, inserted };
1692      return CFArrayCreate(kCFAllocatorDefault, values, 2, &kCFTypeArrayCallBacks);
1693  }
1694  
1695  SecDbEventRef SecDbEventCreateWithComponents(CFTypeRef deleted, CFTypeRef inserted) {
1696      if (deleted && inserted)
1697          return SecDbEventCreateUpdate(deleted, inserted);
1698      else if (deleted)
1699          return SecDbEventCreateDelete(deleted);
1700      else if (inserted)
1701          return SecDbEventCreateInsert(inserted);
1702      else
1703          return NULL;
1704  }
1705  
1706  void SecDbEventTranslateComponents(SecDbEventRef item, CFTypeRef* deleted, CFTypeRef* inserted) {
1707      if(CFGetTypeID(item) == CFArrayGetTypeID()) {
1708          // One item: deletion. Two: update.
1709          CFIndex arraySize = CFArrayGetCount(item);
1710          if(arraySize == 1) {
1711              if(deleted) { *deleted = CFArrayGetValueAtIndex(item, 0); }
1712              if(inserted) { *inserted = NULL; }
1713          } else if(arraySize == 2) {
1714              if(deleted) { *deleted = CFArrayGetValueAtIndex(item, 0); }
1715              if(inserted) { *inserted = CFArrayGetValueAtIndex(item, 1); }
1716          } else {
1717              if(deleted) { *deleted = NULL; }
1718              if(inserted) { *inserted = NULL; }
1719          }
1720      } else {
1721          if(deleted) { *deleted = NULL; }
1722          if(inserted) { *inserted = item; }
1723      }
1724  
1725  }
1726  
1727  bool SecDbEventGetComponents(SecDbEventRef event, CFTypeRef *deleted, CFTypeRef *inserted, CFErrorRef *error) {
1728      if (isArray(event)) {
1729          CFArrayRef array = event;
1730          switch (CFArrayGetCount(array)) {
1731              case 2:
1732                  *deleted = CFArrayGetValueAtIndex(array, 0);
1733                  *inserted = CFArrayGetValueAtIndex(array, 1);
1734                  break;
1735              case 1:
1736                  *deleted = CFArrayGetValueAtIndex(array, 0);
1737                  *inserted = NULL;
1738                  break;
1739              default:
1740                  SecError(errSecParam, error, NULL, CFSTR("invalid entry in changes array: %@"), array);
1741                  break;
1742          }
1743      } else {
1744          *deleted = NULL;
1745          *inserted = event;
1746      }
1747      return true;
1748  }