security/nss/lib/softoken/sdb.c

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4 /*
michael@0 5 * This file implements PKCS 11 on top of our existing security modules
michael@0 6 *
michael@0 7 * For more information about PKCS 11 See PKCS 11 Token Inteface Standard.
michael@0 8 * This implementation has two slots:
michael@0 9 * slot 1 is our generic crypto support. It does not require login.
michael@0 10 * It supports Public Key ops, and all they bulk ciphers and hashes.
michael@0 11 * It can also support Private Key ops for imported Private keys. It does
michael@0 12 * not have any token storage.
michael@0 13 * slot 2 is our private key support. It requires a login before use. It
michael@0 14 * can store Private Keys and Certs as token objects. Currently only private
michael@0 15 * keys and their associated Certificates are saved on the token.
michael@0 16 *
michael@0 17 * In this implementation, session objects are only visible to the session
michael@0 18 * that created or generated them.
michael@0 19 */
michael@0 20
michael@0 21 #include "sdb.h"
michael@0 22 #include "pkcs11t.h"
michael@0 23 #include "seccomon.h"
michael@0 24 #include <sqlite3.h>
michael@0 25 #include "prthread.h"
michael@0 26 #include "prio.h"
michael@0 27 #include <stdio.h>
michael@0 28 #include "secport.h"
michael@0 29 #include "prmon.h"
michael@0 30 #include "prenv.h"
michael@0 31 #include "prprf.h"
michael@0 32 #include "prsystem.h" /* for PR_GetDirectorySeparator() */
michael@0 33 #include <sys/stat.h>
michael@0 34 #if defined(_WIN32)
michael@0 35 #include <io.h>
michael@0 36 #include <windows.h>
michael@0 37 #elif defined(XP_UNIX)
michael@0 38 #include <unistd.h>
michael@0 39 #endif
michael@0 40
michael@0 41 #ifdef SQLITE_UNSAFE_THREADS
michael@0 42 #include "prlock.h"
michael@0 43 /*
michael@0 44 * SQLite can be compiled to be thread safe or not.
michael@0 45 * turn on SQLITE_UNSAFE_THREADS if the OS does not support
michael@0 46 * a thread safe version of sqlite.
michael@0 47 */
michael@0 48 static PRLock *sqlite_lock = NULL;
michael@0 49
michael@0 50 #define LOCK_SQLITE() PR_Lock(sqlite_lock);
michael@0 51 #define UNLOCK_SQLITE() PR_Unlock(sqlite_lock);
michael@0 52 #else
michael@0 53 #define LOCK_SQLITE()
michael@0 54 #define UNLOCK_SQLITE()
michael@0 55 #endif
michael@0 56
michael@0 57 typedef enum {
michael@0 58 SDB_CERT = 1,
michael@0 59 SDB_KEY = 2
michael@0 60 } sdbDataType;
michael@0 61
michael@0 62 /*
michael@0 63 * defines controlling how long we wait to acquire locks.
michael@0 64 *
michael@0 65 * SDB_SQLITE_BUSY_TIMEOUT specifies how long (in milliseconds)
michael@0 66 * sqlite will wait on lock. If that timeout expires, sqlite will
michael@0 67 * return SQLITE_BUSY.
michael@0 68 * SDB_BUSY_RETRY_TIME specifies how many seconds the sdb_ code waits
michael@0 69 * after receiving a busy before retrying.
michael@0 70 * SDB_MAX_BUSY_RETRIES specifies how many times the sdb_ will retry on
michael@0 71 * a busy condition.
michael@0 72 *
michael@0 73 * SDB_SQLITE_BUSY_TIMEOUT affects all opertions, both manual
michael@0 74 * (prepare/step/reset/finalize) and automatic (sqlite3_exec()).
michael@0 75 * SDB_BUSY_RETRY_TIME and SDB_MAX_BUSY_RETRIES only affect manual operations
michael@0 76 *
michael@0 77 * total wait time for automatic operations:
michael@0 78 * 1 second (SDB_SQLITE_BUSY_TIMEOUT/1000).
michael@0 79 * total wait time for manual operations:
michael@0 80 * (1 second + 5 seconds) * 10 = 60 seconds.
michael@0 81 * (SDB_SQLITE_BUSY_TIMEOUT/1000 + SDB_BUSY_RETRY_TIME)*SDB_MAX_BUSY_RETRIES
michael@0 82 */
michael@0 83 #define SDB_SQLITE_BUSY_TIMEOUT 1000 /* milliseconds */
michael@0 84 #define SDB_BUSY_RETRY_TIME 5 /* seconds */
michael@0 85 #define SDB_MAX_BUSY_RETRIES 10
michael@0 86
michael@0 87 /*
michael@0 88 * Note on use of sqlReadDB: Only one thread at a time may have an actual
michael@0 89 * operation going on given sqlite3 * database. An operation is defined as
michael@0 90 * the time from a sqlite3_prepare() until the sqlite3_finalize().
michael@0 91 * Multiple sqlite3 * databases can be open and have simultaneous operations
michael@0 92 * going. We use the sqlXactDB for all write operations. This database
michael@0 93 * is only opened when we first create a transaction and closed when the
michael@0 94 * transaction is complete. sqlReadDB is open when we first opened the database
michael@0 95 * and is used for all read operation. It's use is protected by a monitor. This
michael@0 96 * is because an operation can span the use of FindObjectsInit() through the
michael@0 97 * call to FindObjectsFinal(). In the intermediate time it is possible to call
michael@0 98 * other operations like NSC_GetAttributeValue */
michael@0 99
michael@0 100 struct SDBPrivateStr {
michael@0 101 char *sqlDBName; /* invariant, path to this database */
michael@0 102 sqlite3 *sqlXactDB; /* access protected by dbMon, use protected
michael@0 103 * by the transaction. Current transaction db*/
michael@0 104 PRThread *sqlXactThread; /* protected by dbMon,
michael@0 105 * current transaction thread */
michael@0 106 sqlite3 *sqlReadDB; /* use protected by dbMon, value invariant */
michael@0 107 PRIntervalTime lastUpdateTime; /* last time the cache was updated */
michael@0 108 PRIntervalTime updateInterval; /* how long the cache can go before it
michael@0 109 * must be updated again */
michael@0 110 sdbDataType type; /* invariant, database type */
michael@0 111 char *table; /* invariant, SQL table which contains the db */
michael@0 112 char *cacheTable; /* invariant, SQL table cache of db */
michael@0 113 PRMonitor *dbMon; /* invariant, monitor to protect
michael@0 114 * sqlXact* fields, and use of the sqlReadDB */
michael@0 115 };
michael@0 116
michael@0 117 typedef struct SDBPrivateStr SDBPrivate;
michael@0 118
michael@0 119 /*
michael@0 120 * known attributes
michael@0 121 */
michael@0 122 static const CK_ATTRIBUTE_TYPE known_attributes[] = {
michael@0 123 CKA_CLASS, CKA_TOKEN, CKA_PRIVATE, CKA_LABEL, CKA_APPLICATION,
michael@0 124 CKA_VALUE, CKA_OBJECT_ID, CKA_CERTIFICATE_TYPE, CKA_ISSUER,
michael@0 125 CKA_SERIAL_NUMBER, CKA_AC_ISSUER, CKA_OWNER, CKA_ATTR_TYPES, CKA_TRUSTED,
michael@0 126 CKA_CERTIFICATE_CATEGORY, CKA_JAVA_MIDP_SECURITY_DOMAIN, CKA_URL,
michael@0 127 CKA_HASH_OF_SUBJECT_PUBLIC_KEY, CKA_HASH_OF_ISSUER_PUBLIC_KEY,
michael@0 128 CKA_CHECK_VALUE, CKA_KEY_TYPE, CKA_SUBJECT, CKA_ID, CKA_SENSITIVE,
michael@0 129 CKA_ENCRYPT, CKA_DECRYPT, CKA_WRAP, CKA_UNWRAP, CKA_SIGN, CKA_SIGN_RECOVER,
michael@0 130 CKA_VERIFY, CKA_VERIFY_RECOVER, CKA_DERIVE, CKA_START_DATE, CKA_END_DATE,
michael@0 131 CKA_MODULUS, CKA_MODULUS_BITS, CKA_PUBLIC_EXPONENT, CKA_PRIVATE_EXPONENT,
michael@0 132 CKA_PRIME_1, CKA_PRIME_2, CKA_EXPONENT_1, CKA_EXPONENT_2, CKA_COEFFICIENT,
michael@0 133 CKA_PRIME, CKA_SUBPRIME, CKA_BASE, CKA_PRIME_BITS,
michael@0 134 CKA_SUB_PRIME_BITS, CKA_VALUE_BITS, CKA_VALUE_LEN, CKA_EXTRACTABLE,
michael@0 135 CKA_LOCAL, CKA_NEVER_EXTRACTABLE, CKA_ALWAYS_SENSITIVE,
michael@0 136 CKA_KEY_GEN_MECHANISM, CKA_MODIFIABLE, CKA_EC_PARAMS,
michael@0 137 CKA_EC_POINT, CKA_SECONDARY_AUTH, CKA_AUTH_PIN_FLAGS,
michael@0 138 CKA_ALWAYS_AUTHENTICATE, CKA_WRAP_WITH_TRUSTED, CKA_WRAP_TEMPLATE,
michael@0 139 CKA_UNWRAP_TEMPLATE, CKA_HW_FEATURE_TYPE, CKA_RESET_ON_INIT,
michael@0 140 CKA_HAS_RESET, CKA_PIXEL_X, CKA_PIXEL_Y, CKA_RESOLUTION, CKA_CHAR_ROWS,
michael@0 141 CKA_CHAR_COLUMNS, CKA_COLOR, CKA_BITS_PER_PIXEL, CKA_CHAR_SETS,
michael@0 142 CKA_ENCODING_METHODS, CKA_MIME_TYPES, CKA_MECHANISM_TYPE,
michael@0 143 CKA_REQUIRED_CMS_ATTRIBUTES, CKA_DEFAULT_CMS_ATTRIBUTES,
michael@0 144 CKA_SUPPORTED_CMS_ATTRIBUTES, CKA_NETSCAPE_URL, CKA_NETSCAPE_EMAIL,
michael@0 145 CKA_NETSCAPE_SMIME_INFO, CKA_NETSCAPE_SMIME_TIMESTAMP,
michael@0 146 CKA_NETSCAPE_PKCS8_SALT, CKA_NETSCAPE_PASSWORD_CHECK, CKA_NETSCAPE_EXPIRES,
michael@0 147 CKA_NETSCAPE_KRL, CKA_NETSCAPE_PQG_COUNTER, CKA_NETSCAPE_PQG_SEED,
michael@0 148 CKA_NETSCAPE_PQG_H, CKA_NETSCAPE_PQG_SEED_BITS, CKA_NETSCAPE_MODULE_SPEC,
michael@0 149 CKA_TRUST_DIGITAL_SIGNATURE, CKA_TRUST_NON_REPUDIATION,
michael@0 150 CKA_TRUST_KEY_ENCIPHERMENT, CKA_TRUST_DATA_ENCIPHERMENT,
michael@0 151 CKA_TRUST_KEY_AGREEMENT, CKA_TRUST_KEY_CERT_SIGN, CKA_TRUST_CRL_SIGN,
michael@0 152 CKA_TRUST_SERVER_AUTH, CKA_TRUST_CLIENT_AUTH, CKA_TRUST_CODE_SIGNING,
michael@0 153 CKA_TRUST_EMAIL_PROTECTION, CKA_TRUST_IPSEC_END_SYSTEM,
michael@0 154 CKA_TRUST_IPSEC_TUNNEL, CKA_TRUST_IPSEC_USER, CKA_TRUST_TIME_STAMPING,
michael@0 155 CKA_TRUST_STEP_UP_APPROVED, CKA_CERT_SHA1_HASH, CKA_CERT_MD5_HASH,
michael@0 156 CKA_NETSCAPE_DB, CKA_NETSCAPE_TRUST, CKA_NSS_OVERRIDE_EXTENSIONS
michael@0 157 };
michael@0 158
michael@0 159 static int known_attributes_size= sizeof(known_attributes)/
michael@0 160 sizeof(known_attributes[0]);
michael@0 161
michael@0 162 /* Magic for an explicit NULL. NOTE: ideally this should be
michael@0 163 * out of band data. Since it's not completely out of band, pick
michael@0 164 * a value that has no meaning to any existing PKCS #11 attributes.
michael@0 165 * This value is 1) not a valid string (imbedded '\0'). 2) not a U_LONG
michael@0 166 * or a normal key (too short). 3) not a bool (too long). 4) not an RSA
michael@0 167 * public exponent (too many bits).
michael@0 168 */
michael@0 169 const unsigned char SQLITE_EXPLICIT_NULL[] = { 0xa5, 0x0, 0x5a };
michael@0 170 #define SQLITE_EXPLICIT_NULL_LEN 3
michael@0 171
michael@0 172 /*
michael@0 173 * determine when we've completed our tasks
michael@0 174 */
michael@0 175 static int
michael@0 176 sdb_done(int err, int *count)
michael@0 177 {
michael@0 178 /* allow as many rows as the database wants to give */
michael@0 179 if (err == SQLITE_ROW) {
michael@0 180 *count = 0;
michael@0 181 return 0;
michael@0 182 }
michael@0 183 if (err != SQLITE_BUSY) {
michael@0 184 return 1;
michael@0 185 }
michael@0 186 /* err == SQLITE_BUSY, Dont' retry forever in this case */
michael@0 187 if (++(*count) >= SDB_MAX_BUSY_RETRIES) {
michael@0 188 return 1;
michael@0 189 }
michael@0 190 return 0;
michael@0 191 }
michael@0 192
michael@0 193 /*
michael@0 194 * find out where sqlite stores the temp tables. We do this by replicating
michael@0 195 * the logic from sqlite.
michael@0 196 */
michael@0 197 #if defined(_WIN32)
michael@0 198 static char *
michael@0 199 sdb_getFallbackTempDir(void)
michael@0 200 {
michael@0 201 /* sqlite uses sqlite3_temp_directory if it is not NULL. We don't have
michael@0 202 * access to sqlite3_temp_directory because it is not exported from
michael@0 203 * sqlite3.dll. Assume sqlite3_win32_set_directory isn't called and
michael@0 204 * sqlite3_temp_directory is NULL.
michael@0 205 */
michael@0 206 char path[MAX_PATH];
michael@0 207 DWORD rv;
michael@0 208 size_t len;
michael@0 209
michael@0 210 rv = GetTempPathA(MAX_PATH, path);
michael@0 211 if (rv > MAX_PATH || rv == 0)
michael@0 212 return NULL;
michael@0 213 len = strlen(path);
michael@0 214 if (len == 0)
michael@0 215 return NULL;
michael@0 216 /* The returned string ends with a backslash, for example, "C:\TEMP\". */
michael@0 217 if (path[len - 1] == '\\')
michael@0 218 path[len - 1] = '\0';
michael@0 219 return PORT_Strdup(path);
michael@0 220 }
michael@0 221 #elif defined(XP_UNIX)
michael@0 222 static char *
michael@0 223 sdb_getFallbackTempDir(void)
michael@0 224 {
michael@0 225 const char *azDirs[] = {
michael@0 226 NULL,
michael@0 227 NULL,
michael@0 228 "/var/tmp",
michael@0 229 "/usr/tmp",
michael@0 230 "/tmp",
michael@0 231 NULL /* List terminator */
michael@0 232 };
michael@0 233 unsigned int i;
michael@0 234 struct stat buf;
michael@0 235 const char *zDir = NULL;
michael@0 236
michael@0 237 azDirs[0] = sqlite3_temp_directory;
michael@0 238 azDirs[1] = getenv("TMPDIR");
michael@0 239
michael@0 240 for (i = 0; i < PR_ARRAY_SIZE(azDirs); i++) {
michael@0 241 zDir = azDirs[i];
michael@0 242 if (zDir == NULL) continue;
michael@0 243 if (stat(zDir, &buf)) continue;
michael@0 244 if (!S_ISDIR(buf.st_mode)) continue;
michael@0 245 if (access(zDir, 07)) continue;
michael@0 246 break;
michael@0 247 }
michael@0 248
michael@0 249 if (zDir == NULL)
michael@0 250 return NULL;
michael@0 251 return PORT_Strdup(zDir);
michael@0 252 }
michael@0 253 #else
michael@0 254 #error "sdb_getFallbackTempDir not implemented"
michael@0 255 #endif
michael@0 256
michael@0 257 #ifndef SQLITE_FCNTL_TEMPFILENAME
michael@0 258 /* SQLITE_FCNTL_TEMPFILENAME was added in SQLite 3.7.15 */
michael@0 259 #define SQLITE_FCNTL_TEMPFILENAME 16
michael@0 260 #endif
michael@0 261
michael@0 262 static char *
michael@0 263 sdb_getTempDir(sqlite3 *sqlDB)
michael@0 264 {
michael@0 265 int sqlrv;
michael@0 266 char *result = NULL;
michael@0 267 char *tempName = NULL;
michael@0 268 char *foundSeparator = NULL;
michael@0 269
michael@0 270 /* Obtain temporary filename in sqlite's directory for temporary tables */
michael@0 271 sqlrv = sqlite3_file_control(sqlDB, 0, SQLITE_FCNTL_TEMPFILENAME,
michael@0 272 (void*)&tempName);
michael@0 273 if (sqlrv == SQLITE_NOTFOUND) {
michael@0 274 /* SQLITE_FCNTL_TEMPFILENAME not implemented because we are using
michael@0 275 * an older SQLite. */
michael@0 276 return sdb_getFallbackTempDir();
michael@0 277 }
michael@0 278 if (sqlrv != SQLITE_OK) {
michael@0 279 return NULL;
michael@0 280 }
michael@0 281
michael@0 282 /* We'll extract the temporary directory from tempName */
michael@0 283 foundSeparator = PORT_Strrchr(tempName, PR_GetDirectorySeparator());
michael@0 284 if (foundSeparator) {
michael@0 285 /* We shorten the temp filename string to contain only
michael@0 286 * the directory name (including the trailing separator).
michael@0 287 * We know the byte after the foundSeparator position is
michael@0 288 * safe to use, in the shortest scenario it contains the
michael@0 289 * end-of-string byte.
michael@0 290 * By keeping the separator at the found position, it will
michael@0 291 * even work if tempDir consists of the separator, only.
michael@0 292 * (In this case the toplevel directory will be used for
michael@0 293 * access speed testing). */
michael@0 294 ++foundSeparator;
michael@0 295 *foundSeparator = 0;
michael@0 296
michael@0 297 /* Now we copy the directory name for our caller */
michael@0 298 result = PORT_Strdup(tempName);
michael@0 299 }
michael@0 300
michael@0 301 sqlite3_free(tempName);
michael@0 302 return result;
michael@0 303 }
michael@0 304
michael@0 305 /*
michael@0 306 * Map SQL_LITE errors to PKCS #11 errors as best we can.
michael@0 307 */
michael@0 308 static CK_RV
michael@0 309 sdb_mapSQLError(sdbDataType type, int sqlerr)
michael@0 310 {
michael@0 311 switch (sqlerr) {
michael@0 312 /* good matches */
michael@0 313 case SQLITE_OK:
michael@0 314 case SQLITE_DONE:
michael@0 315 return CKR_OK;
michael@0 316 case SQLITE_NOMEM:
michael@0 317 return CKR_HOST_MEMORY;
michael@0 318 case SQLITE_READONLY:
michael@0 319 return CKR_TOKEN_WRITE_PROTECTED;
michael@0 320 /* close matches */
michael@0 321 case SQLITE_AUTH:
michael@0 322 case SQLITE_PERM:
michael@0 323 /*return CKR_USER_NOT_LOGGED_IN; */
michael@0 324 case SQLITE_CANTOPEN:
michael@0 325 case SQLITE_NOTFOUND:
michael@0 326 /* NSS distiguishes between failure to open the cert and the key db */
michael@0 327 return type == SDB_CERT ?
michael@0 328 CKR_NETSCAPE_CERTDB_FAILED : CKR_NETSCAPE_KEYDB_FAILED;
michael@0 329 case SQLITE_IOERR:
michael@0 330 return CKR_DEVICE_ERROR;
michael@0 331 default:
michael@0 332 break;
michael@0 333 }
michael@0 334 return CKR_GENERAL_ERROR;
michael@0 335 }
michael@0 336
michael@0 337
michael@0 338 /*
michael@0 339 * build up database name from a directory, prefix, name, version and flags.
michael@0 340 */
michael@0 341 static char *sdb_BuildFileName(const char * directory,
michael@0 342 const char *prefix, const char *type,
michael@0 343 int version)
michael@0 344 {
michael@0 345 char *dbname = NULL;
michael@0 346 /* build the full dbname */
michael@0 347 dbname = sqlite3_mprintf("%s%c%s%s%d.db", directory,
michael@0 348 (int)(unsigned char)PR_GetDirectorySeparator(),
michael@0 349 prefix, type, version);
michael@0 350 return dbname;
michael@0 351 }
michael@0 352
michael@0 353
michael@0 354 /*
michael@0 355 * find out how expensive the access system call is for non-existant files
michael@0 356 * in the given directory. Return the number of operations done in 33 ms.
michael@0 357 */
michael@0 358 static PRUint32
michael@0 359 sdb_measureAccess(const char *directory)
michael@0 360 {
michael@0 361 PRUint32 i;
michael@0 362 PRIntervalTime time;
michael@0 363 PRIntervalTime delta;
michael@0 364 PRIntervalTime duration = PR_MillisecondsToInterval(33);
michael@0 365 const char *doesntExistName = "_dOeSnotExist_.db";
michael@0 366 char *temp, *tempStartOfFilename;
michael@0 367 size_t maxTempLen, maxFileNameLen, directoryLength;
michael@0 368
michael@0 369 /* no directory, just return one */
michael@0 370 if (directory == NULL) {
michael@0 371 return 1;
michael@0 372 }
michael@0 373
michael@0 374 /* our calculation assumes time is a 4 bytes == 32 bit integer */
michael@0 375 PORT_Assert(sizeof(time) == 4);
michael@0 376
michael@0 377 directoryLength = strlen(directory);
michael@0 378
michael@0 379 maxTempLen = directoryLength + strlen(doesntExistName)
michael@0 380 + 1 /* potential additional separator char */
michael@0 381 + 11 /* max chars for 32 bit int plus potential sign */
michael@0 382 + 1; /* zero terminator */
michael@0 383
michael@0 384 temp = PORT_Alloc(maxTempLen);
michael@0 385 if (!temp) {
michael@0 386 return 1;
michael@0 387 }
michael@0 388
michael@0 389 /* We'll copy directory into temp just once, then ensure it ends
michael@0 390 * with the directory separator, then remember the position after
michael@0 391 * the separator, and calculate the number of remaining bytes. */
michael@0 392
michael@0 393 strcpy(temp, directory);
michael@0 394 if (directory[directoryLength - 1] != PR_GetDirectorySeparator()) {
michael@0 395 temp[directoryLength++] = PR_GetDirectorySeparator();
michael@0 396 }
michael@0 397 tempStartOfFilename = temp + directoryLength;
michael@0 398 maxFileNameLen = maxTempLen - directoryLength;
michael@0 399
michael@0 400 /* measure number of Access operations that can be done in 33 milliseconds
michael@0 401 * (1/30'th of a second), or 10000 operations, which ever comes first.
michael@0 402 */
michael@0 403 time = PR_IntervalNow();
michael@0 404 for (i=0; i < 10000u; i++) {
michael@0 405 PRIntervalTime next;
michael@0 406
michael@0 407 /* We'll use the variable part first in the filename string, just in
michael@0 408 * case it's longer than assumed, so if anything gets cut off, it
michael@0 409 * will be cut off from the constant part.
michael@0 410 * This code assumes the directory name at the beginning of
michael@0 411 * temp remains unchanged during our loop. */
michael@0 412 PR_snprintf(tempStartOfFilename, maxFileNameLen,
michael@0 413 ".%lu%s", (PRUint32)(time+i), doesntExistName);
michael@0 414 PR_Access(temp,PR_ACCESS_EXISTS);
michael@0 415 next = PR_IntervalNow();
michael@0 416 delta = next - time;
michael@0 417 if (delta >= duration)
michael@0 418 break;
michael@0 419 }
michael@0 420
michael@0 421 PORT_Free(temp);
michael@0 422
michael@0 423 /* always return 1 or greater */
michael@0 424 return i ? i : 1u;
michael@0 425 }
michael@0 426
michael@0 427 /*
michael@0 428 * some file sytems are very slow to run sqlite3 on, particularly if the
michael@0 429 * access count is pretty high. On these filesystems is faster to create
michael@0 430 * a temporary database on the local filesystem and access that. This
michael@0 431 * code uses a temporary table to create that cache. Temp tables are
michael@0 432 * automatically cleared when the database handle it was created on
michael@0 433 * Is freed.
michael@0 434 */
michael@0 435 static const char DROP_CACHE_CMD[] = "DROP TABLE %s";
michael@0 436 static const char CREATE_CACHE_CMD[] =
michael@0 437 "CREATE TEMPORARY TABLE %s AS SELECT * FROM %s";
michael@0 438 static const char CREATE_ISSUER_INDEX_CMD[] =
michael@0 439 "CREATE INDEX issuer ON %s (a81)";
michael@0 440 static const char CREATE_SUBJECT_INDEX_CMD[] =
michael@0 441 "CREATE INDEX subject ON %s (a101)";
michael@0 442 static const char CREATE_LABEL_INDEX_CMD[] = "CREATE INDEX label ON %s (a3)";
michael@0 443 static const char CREATE_ID_INDEX_CMD[] = "CREATE INDEX ckaid ON %s (a102)";
michael@0 444
michael@0 445 static CK_RV
michael@0 446 sdb_buildCache(sqlite3 *sqlDB, sdbDataType type,
michael@0 447 const char *cacheTable, const char *table)
michael@0 448 {
michael@0 449 char *newStr;
michael@0 450 int sqlerr = SQLITE_OK;
michael@0 451
michael@0 452 newStr = sqlite3_mprintf(CREATE_CACHE_CMD, cacheTable, table);
michael@0 453 if (newStr == NULL) {
michael@0 454 return CKR_HOST_MEMORY;
michael@0 455 }
michael@0 456 sqlerr = sqlite3_exec(sqlDB, newStr, NULL, 0, NULL);
michael@0 457 sqlite3_free(newStr);
michael@0 458 if (sqlerr != SQLITE_OK) {
michael@0 459 return sdb_mapSQLError(type, sqlerr);
michael@0 460 }
michael@0 461 /* failure to create the indexes is not an issue */
michael@0 462 newStr = sqlite3_mprintf(CREATE_ISSUER_INDEX_CMD, cacheTable);
michael@0 463 if (newStr == NULL) {
michael@0 464 return CKR_OK;
michael@0 465 }
michael@0 466 sqlerr = sqlite3_exec(sqlDB, newStr, NULL, 0, NULL);
michael@0 467 sqlite3_free(newStr);
michael@0 468 newStr = sqlite3_mprintf(CREATE_SUBJECT_INDEX_CMD, cacheTable);
michael@0 469 if (newStr == NULL) {
michael@0 470 return CKR_OK;
michael@0 471 }
michael@0 472 sqlerr = sqlite3_exec(sqlDB, newStr, NULL, 0, NULL);
michael@0 473 sqlite3_free(newStr);
michael@0 474 newStr = sqlite3_mprintf(CREATE_LABEL_INDEX_CMD, cacheTable);
michael@0 475 if (newStr == NULL) {
michael@0 476 return CKR_OK;
michael@0 477 }
michael@0 478 sqlerr = sqlite3_exec(sqlDB, newStr, NULL, 0, NULL);
michael@0 479 sqlite3_free(newStr);
michael@0 480 newStr = sqlite3_mprintf(CREATE_ID_INDEX_CMD, cacheTable);
michael@0 481 if (newStr == NULL) {
michael@0 482 return CKR_OK;
michael@0 483 }
michael@0 484 sqlerr = sqlite3_exec(sqlDB, newStr, NULL, 0, NULL);
michael@0 485 sqlite3_free(newStr);
michael@0 486 return CKR_OK;
michael@0 487 }
michael@0 488
michael@0 489 /*
michael@0 490 * update the cache and the data records describing it.
michael@0 491 * The cache is updated by dropping the temp database and recreating it.
michael@0 492 */
michael@0 493 static CK_RV
michael@0 494 sdb_updateCache(SDBPrivate *sdb_p)
michael@0 495 {
michael@0 496 int sqlerr = SQLITE_OK;
michael@0 497 CK_RV error = CKR_OK;
michael@0 498 char *newStr;
michael@0 499
michael@0 500 /* drop the old table */
michael@0 501 newStr = sqlite3_mprintf(DROP_CACHE_CMD, sdb_p->cacheTable);
michael@0 502 if (newStr == NULL) {
michael@0 503 return CKR_HOST_MEMORY;
michael@0 504 }
michael@0 505 sqlerr = sqlite3_exec(sdb_p->sqlReadDB, newStr, NULL, 0, NULL);
michael@0 506 sqlite3_free(newStr);
michael@0 507 if ((sqlerr != SQLITE_OK) && (sqlerr != SQLITE_ERROR )) {
michael@0 508 /* something went wrong with the drop, don't try to refresh...
michael@0 509 * NOTE: SQLITE_ERROR is returned if the table doesn't exist. In
michael@0 510 * that case, we just continue on and try to reload it */
michael@0 511 return sdb_mapSQLError(sdb_p->type, sqlerr);
michael@0 512 }
michael@0 513
michael@0 514
michael@0 515 /* set up the new table */
michael@0 516 error = sdb_buildCache(sdb_p->sqlReadDB,sdb_p->type,
michael@0 517 sdb_p->cacheTable,sdb_p->table );
michael@0 518 if (error == CKR_OK) {
michael@0 519 /* we have a new cache! */
michael@0 520 sdb_p->lastUpdateTime = PR_IntervalNow();
michael@0 521 }
michael@0 522 return error;
michael@0 523 }
michael@0 524
michael@0 525 /*
michael@0 526 * The sharing of sqlite3 handles across threads is tricky. Older versions
michael@0 527 * couldn't at all, but newer ones can under strict conditions. Basically
michael@0 528 * no 2 threads can use the same handle while another thread has an open
michael@0 529 * stmt running. Once the sqlite3_stmt is finalized, another thread can then
michael@0 530 * use the database handle.
michael@0 531 *
michael@0 532 * We use monitors to protect against trying to use a database before
michael@0 533 * it's sqlite3_stmt is finalized. This is preferable to the opening and
michael@0 534 * closing the database each operation because there is significant overhead
michael@0 535 * in the open and close. Also continually opening and closing the database
michael@0 536 * defeats the cache code as the cache table is lost on close (thus
michael@0 537 * requiring us to have to reinitialize the cache every operation).
michael@0 538 *
michael@0 539 * An execption to the shared handle is transations. All writes happen
michael@0 540 * through a transaction. When we are in a transaction, we must use the
michael@0 541 * same database pointer for that entire transation. In this case we save
michael@0 542 * the transaction database and use it for all accesses on the transaction
michael@0 543 * thread. Other threads use the common database.
michael@0 544 *
michael@0 545 * There can only be once active transaction on the database at a time.
michael@0 546 *
michael@0 547 * sdb_openDBLocal() provides us with a valid database handle for whatever
michael@0 548 * state we are in (reading or in a transaction), and acquires any locks
michael@0 549 * appropriate to that state. It also decides when it's time to refresh
michael@0 550 * the cache before we start an operation. Any database handle returned
michael@0 551 * just eventually be closed with sdb_closeDBLocal().
michael@0 552 *
michael@0 553 * The table returned either points to the database's physical table, or
michael@0 554 * to the cached shadow. Tranactions always return the physical table
michael@0 555 * and read operations return either the physical table or the cache
michael@0 556 * depending on whether or not the cache exists.
michael@0 557 */
michael@0 558 static CK_RV
michael@0 559 sdb_openDBLocal(SDBPrivate *sdb_p, sqlite3 **sqlDB, const char **table)
michael@0 560 {
michael@0 561 *sqlDB = NULL;
michael@0 562
michael@0 563 PR_EnterMonitor(sdb_p->dbMon);
michael@0 564
michael@0 565 if (table) {
michael@0 566 *table = sdb_p->table;
michael@0 567 }
michael@0 568
michael@0 569 /* We're in a transaction, use the transaction DB */
michael@0 570 if ((sdb_p->sqlXactDB) && (sdb_p->sqlXactThread == PR_GetCurrentThread())) {
michael@0 571 *sqlDB =sdb_p->sqlXactDB;
michael@0 572 /* only one thread can get here, safe to unlock */
michael@0 573 PR_ExitMonitor(sdb_p->dbMon);
michael@0 574 return CKR_OK;
michael@0 575 }
michael@0 576
michael@0 577 /*
michael@0 578 * if we are just reading from the table, we may have the table
michael@0 579 * cached in a temporary table (especially if it's on a shared FS).
michael@0 580 * In that case we want to see updates to the table, the the granularity
michael@0 581 * is on order of human scale, not computer scale.
michael@0 582 */
michael@0 583 if (table && sdb_p->cacheTable) {
michael@0 584 PRIntervalTime now = PR_IntervalNow();
michael@0 585 if ((now - sdb_p->lastUpdateTime) > sdb_p->updateInterval) {
michael@0 586 sdb_updateCache(sdb_p);
michael@0 587 }
michael@0 588 *table = sdb_p->cacheTable;
michael@0 589 }
michael@0 590
michael@0 591 *sqlDB = sdb_p->sqlReadDB;
michael@0 592
michael@0 593 /* leave holding the lock. only one thread can actually use a given
michael@0 594 * database connection at once */
michael@0 595
michael@0 596 return CKR_OK;
michael@0 597 }
michael@0 598
michael@0 599 /* closing the local database currenly means unlocking the monitor */
michael@0 600 static CK_RV
michael@0 601 sdb_closeDBLocal(SDBPrivate *sdb_p, sqlite3 *sqlDB)
michael@0 602 {
michael@0 603 if (sdb_p->sqlXactDB != sqlDB) {
michael@0 604 /* if we weren't in a transaction, we got a lock */
michael@0 605 PR_ExitMonitor(sdb_p->dbMon);
michael@0 606 }
michael@0 607 return CKR_OK;
michael@0 608 }
michael@0 609
michael@0 610
michael@0 611 /*
michael@0 612 * wrapper to sqlite3_open which also sets the busy_timeout
michael@0 613 */
michael@0 614 static int
michael@0 615 sdb_openDB(const char *name, sqlite3 **sqlDB, int flags)
michael@0 616 {
michael@0 617 int sqlerr;
michael@0 618 /*
michael@0 619 * in sqlite3 3.5.0, there is a new open call that allows us
michael@0 620 * to specify read only. Most new OS's are still on 3.3.x (including
michael@0 621 * NSS's internal version and the version shipped with Firefox).
michael@0 622 */
michael@0 623 *sqlDB = NULL;
michael@0 624 sqlerr = sqlite3_open(name, sqlDB);
michael@0 625 if (sqlerr != SQLITE_OK) {
michael@0 626 return sqlerr;
michael@0 627 }
michael@0 628
michael@0 629 sqlerr = sqlite3_busy_timeout(*sqlDB, SDB_SQLITE_BUSY_TIMEOUT);
michael@0 630 if (sqlerr != SQLITE_OK) {
michael@0 631 sqlite3_close(*sqlDB);
michael@0 632 *sqlDB = NULL;
michael@0 633 return sqlerr;
michael@0 634 }
michael@0 635 return SQLITE_OK;
michael@0 636 }
michael@0 637
michael@0 638 /* Sigh, if we created a new table since we opened the database,
michael@0 639 * the database handle will not see the new table, we need to close this
michael@0 640 * database and reopen it. Caller must be in a transaction or holding
michael@0 641 * the dbMon. sqlDB is changed on success. */
michael@0 642 static int
michael@0 643 sdb_reopenDBLocal(SDBPrivate *sdb_p, sqlite3 **sqlDB) {
michael@0 644 sqlite3 *newDB;
michael@0 645 int sqlerr;
michael@0 646
michael@0 647 /* open a new database */
michael@0 648 sqlerr = sdb_openDB(sdb_p->sqlDBName, &newDB, SDB_RDONLY);
michael@0 649 if (sqlerr != SQLITE_OK) {
michael@0 650 return sqlerr;
michael@0 651 }
michael@0 652
michael@0 653 /* if we are in a transaction, we may not be holding the monitor.
michael@0 654 * grab it before we update the transaction database. This is
michael@0 655 * safe since are using monitors. */
michael@0 656 PR_EnterMonitor(sdb_p->dbMon);
michael@0 657 /* update our view of the database */
michael@0 658 if (sdb_p->sqlReadDB == *sqlDB) {
michael@0 659 sdb_p->sqlReadDB = newDB;
michael@0 660 } else if (sdb_p->sqlXactDB == *sqlDB) {
michael@0 661 sdb_p->sqlXactDB = newDB;
michael@0 662 }
michael@0 663 PR_ExitMonitor(sdb_p->dbMon);
michael@0 664
michael@0 665 /* close the old one */
michael@0 666 sqlite3_close(*sqlDB);
michael@0 667
michael@0 668 *sqlDB = newDB;
michael@0 669 return SQLITE_OK;
michael@0 670 }
michael@0 671
michael@0 672 struct SDBFindStr {
michael@0 673 sqlite3 *sqlDB;
michael@0 674 sqlite3_stmt *findstmt;
michael@0 675 };
michael@0 676
michael@0 677
michael@0 678 static const char FIND_OBJECTS_CMD[] = "SELECT ALL * FROM %s WHERE %s;";
michael@0 679 static const char FIND_OBJECTS_ALL_CMD[] = "SELECT ALL * FROM %s;";
michael@0 680 CK_RV
michael@0 681 sdb_FindObjectsInit(SDB *sdb, const CK_ATTRIBUTE *template, CK_ULONG count,
michael@0 682 SDBFind **find)
michael@0 683 {
michael@0 684 SDBPrivate *sdb_p = sdb->private;
michael@0 685 sqlite3 *sqlDB = NULL;
michael@0 686 const char *table;
michael@0 687 char *newStr, *findStr = NULL;
michael@0 688 sqlite3_stmt *findstmt = NULL;
michael@0 689 char *join="";
michael@0 690 int sqlerr = SQLITE_OK;
michael@0 691 CK_RV error = CKR_OK;
michael@0 692 int i;
michael@0 693
michael@0 694 LOCK_SQLITE()
michael@0 695 *find = NULL;
michael@0 696 error = sdb_openDBLocal(sdb_p, &sqlDB, &table);
michael@0 697 if (error != CKR_OK) {
michael@0 698 goto loser;
michael@0 699 }
michael@0 700
michael@0 701 findStr = sqlite3_mprintf("");
michael@0 702 for (i=0; findStr && i < count; i++) {
michael@0 703 newStr = sqlite3_mprintf("%s%sa%x=$DATA%d", findStr, join,
michael@0 704 template[i].type, i);
michael@0 705 join=" AND ";
michael@0 706 sqlite3_free(findStr);
michael@0 707 findStr = newStr;
michael@0 708 }
michael@0 709
michael@0 710 if (findStr == NULL) {
michael@0 711 error = CKR_HOST_MEMORY;
michael@0 712 goto loser;
michael@0 713 }
michael@0 714
michael@0 715 if (count == 0) {
michael@0 716 newStr = sqlite3_mprintf(FIND_OBJECTS_ALL_CMD, table);
michael@0 717 } else {
michael@0 718 newStr = sqlite3_mprintf(FIND_OBJECTS_CMD, table, findStr);
michael@0 719 }
michael@0 720 sqlite3_free(findStr);
michael@0 721 if (newStr == NULL) {
michael@0 722 error = CKR_HOST_MEMORY;
michael@0 723 goto loser;
michael@0 724 }
michael@0 725 sqlerr = sqlite3_prepare_v2(sqlDB, newStr, -1, &findstmt, NULL);
michael@0 726 sqlite3_free(newStr);
michael@0 727 for (i=0; sqlerr == SQLITE_OK && i < count; i++) {
michael@0 728 const void *blobData = template[i].pValue;
michael@0 729 unsigned int blobSize = template[i].ulValueLen;
michael@0 730 if (blobSize == 0) {
michael@0 731 blobSize = SQLITE_EXPLICIT_NULL_LEN;
michael@0 732 blobData = SQLITE_EXPLICIT_NULL;
michael@0 733 }
michael@0 734 sqlerr = sqlite3_bind_blob(findstmt, i+1, blobData, blobSize,
michael@0 735 SQLITE_TRANSIENT);
michael@0 736 }
michael@0 737 if (sqlerr == SQLITE_OK) {
michael@0 738 *find = PORT_New(SDBFind);
michael@0 739 if (*find == NULL) {
michael@0 740 error = CKR_HOST_MEMORY;
michael@0 741 goto loser;
michael@0 742 }
michael@0 743 (*find)->findstmt = findstmt;
michael@0 744 (*find)->sqlDB = sqlDB;
michael@0 745 UNLOCK_SQLITE()
michael@0 746 return CKR_OK;
michael@0 747 }
michael@0 748 error = sdb_mapSQLError(sdb_p->type, sqlerr);
michael@0 749
michael@0 750 loser:
michael@0 751 if (findstmt) {
michael@0 752 sqlite3_reset(findstmt);
michael@0 753 sqlite3_finalize(findstmt);
michael@0 754 }
michael@0 755 if (sqlDB) {
michael@0 756 sdb_closeDBLocal(sdb_p, sqlDB) ;
michael@0 757 }
michael@0 758 UNLOCK_SQLITE()
michael@0 759 return error;
michael@0 760 }
michael@0 761
michael@0 762
michael@0 763 CK_RV
michael@0 764 sdb_FindObjects(SDB *sdb, SDBFind *sdbFind, CK_OBJECT_HANDLE *object,
michael@0 765 CK_ULONG arraySize, CK_ULONG *count)
michael@0 766 {
michael@0 767 SDBPrivate *sdb_p = sdb->private;
michael@0 768 sqlite3_stmt *stmt = sdbFind->findstmt;
michael@0 769 int sqlerr = SQLITE_OK;
michael@0 770 int retry = 0;
michael@0 771
michael@0 772 *count = 0;
michael@0 773
michael@0 774 if (arraySize == 0) {
michael@0 775 return CKR_OK;
michael@0 776 }
michael@0 777 LOCK_SQLITE()
michael@0 778
michael@0 779 do {
michael@0 780 sqlerr = sqlite3_step(stmt);
michael@0 781 if (sqlerr == SQLITE_BUSY) {
michael@0 782 PR_Sleep(SDB_BUSY_RETRY_TIME);
michael@0 783 }
michael@0 784 if (sqlerr == SQLITE_ROW) {
michael@0 785 /* only care about the id */
michael@0 786 *object++= sqlite3_column_int(stmt, 0);
michael@0 787 arraySize--;
michael@0 788 (*count)++;
michael@0 789 }
michael@0 790 } while (!sdb_done(sqlerr,&retry) && (arraySize > 0));
michael@0 791
michael@0 792 /* we only have some of the objects, there is probably more,
michael@0 793 * set the sqlerr to an OK value so we return CKR_OK */
michael@0 794 if (sqlerr == SQLITE_ROW && arraySize == 0) {
michael@0 795 sqlerr = SQLITE_DONE;
michael@0 796 }
michael@0 797 UNLOCK_SQLITE()
michael@0 798
michael@0 799 return sdb_mapSQLError(sdb_p->type, sqlerr);
michael@0 800 }
michael@0 801
michael@0 802 CK_RV
michael@0 803 sdb_FindObjectsFinal(SDB *sdb, SDBFind *sdbFind)
michael@0 804 {
michael@0 805 SDBPrivate *sdb_p = sdb->private;
michael@0 806 sqlite3_stmt *stmt = sdbFind->findstmt;
michael@0 807 sqlite3 *sqlDB = sdbFind->sqlDB;
michael@0 808 int sqlerr = SQLITE_OK;
michael@0 809
michael@0 810 LOCK_SQLITE()
michael@0 811 if (stmt) {
michael@0 812 sqlite3_reset(stmt);
michael@0 813 sqlerr = sqlite3_finalize(stmt);
michael@0 814 }
michael@0 815 if (sqlDB) {
michael@0 816 sdb_closeDBLocal(sdb_p, sqlDB) ;
michael@0 817 }
michael@0 818 PORT_Free(sdbFind);
michael@0 819
michael@0 820 UNLOCK_SQLITE()
michael@0 821 return sdb_mapSQLError(sdb_p->type, sqlerr);
michael@0 822 }
michael@0 823
michael@0 824 static const char GET_ATTRIBUTE_CMD[] = "SELECT ALL %s FROM %s WHERE id=$ID;";
michael@0 825 CK_RV
michael@0 826 sdb_GetAttributeValueNoLock(SDB *sdb, CK_OBJECT_HANDLE object_id,
michael@0 827 CK_ATTRIBUTE *template, CK_ULONG count)
michael@0 828 {
michael@0 829 SDBPrivate *sdb_p = sdb->private;
michael@0 830 sqlite3 *sqlDB = NULL;
michael@0 831 sqlite3_stmt *stmt = NULL;
michael@0 832 char *getStr = NULL;
michael@0 833 char *newStr = NULL;
michael@0 834 const char *table = NULL;
michael@0 835 int sqlerr = SQLITE_OK;
michael@0 836 CK_RV error = CKR_OK;
michael@0 837 int found = 0;
michael@0 838 int retry = 0;
michael@0 839 int i;
michael@0 840
michael@0 841
michael@0 842 /* open a new db if necessary */
michael@0 843 error = sdb_openDBLocal(sdb_p, &sqlDB, &table);
michael@0 844 if (error != CKR_OK) {
michael@0 845 goto loser;
michael@0 846 }
michael@0 847
michael@0 848 for (i=0; i < count; i++) {
michael@0 849 getStr = sqlite3_mprintf("a%x", template[i].type);
michael@0 850
michael@0 851 if (getStr == NULL) {
michael@0 852 error = CKR_HOST_MEMORY;
michael@0 853 goto loser;
michael@0 854 }
michael@0 855
michael@0 856 newStr = sqlite3_mprintf(GET_ATTRIBUTE_CMD, getStr, table);
michael@0 857 sqlite3_free(getStr);
michael@0 858 getStr = NULL;
michael@0 859 if (newStr == NULL) {
michael@0 860 error = CKR_HOST_MEMORY;
michael@0 861 goto loser;
michael@0 862 }
michael@0 863
michael@0 864 sqlerr = sqlite3_prepare_v2(sqlDB, newStr, -1, &stmt, NULL);
michael@0 865 sqlite3_free(newStr);
michael@0 866 newStr = NULL;
michael@0 867 if (sqlerr == SQLITE_ERROR) {
michael@0 868 template[i].ulValueLen = -1;
michael@0 869 error = CKR_ATTRIBUTE_TYPE_INVALID;
michael@0 870 continue;
michael@0 871 } else if (sqlerr != SQLITE_OK) { goto loser; }
michael@0 872
michael@0 873 sqlerr = sqlite3_bind_int(stmt, 1, object_id);
michael@0 874 if (sqlerr != SQLITE_OK) { goto loser; }
michael@0 875
michael@0 876 do {
michael@0 877 sqlerr = sqlite3_step(stmt);
michael@0 878 if (sqlerr == SQLITE_BUSY) {
michael@0 879 PR_Sleep(SDB_BUSY_RETRY_TIME);
michael@0 880 }
michael@0 881 if (sqlerr == SQLITE_ROW) {
michael@0 882 int blobSize;
michael@0 883 const char *blobData;
michael@0 884
michael@0 885 blobSize = sqlite3_column_bytes(stmt, 0);
michael@0 886 blobData = sqlite3_column_blob(stmt, 0);
michael@0 887 if (blobData == NULL) {
michael@0 888 template[i].ulValueLen = -1;
michael@0 889 error = CKR_ATTRIBUTE_TYPE_INVALID;
michael@0 890 break;
michael@0 891 }
michael@0 892 /* If the blob equals our explicit NULL value, then the
michael@0 893 * attribute is a NULL. */
michael@0 894 if ((blobSize == SQLITE_EXPLICIT_NULL_LEN) &&
michael@0 895 (PORT_Memcmp(blobData, SQLITE_EXPLICIT_NULL,
michael@0 896 SQLITE_EXPLICIT_NULL_LEN) == 0)) {
michael@0 897 blobSize = 0;
michael@0 898 }
michael@0 899 if (template[i].pValue) {
michael@0 900 if (template[i].ulValueLen < blobSize) {
michael@0 901 template[i].ulValueLen = -1;
michael@0 902 error = CKR_BUFFER_TOO_SMALL;
michael@0 903 break;
michael@0 904 }
michael@0 905 PORT_Memcpy(template[i].pValue, blobData, blobSize);
michael@0 906 }
michael@0 907 template[i].ulValueLen = blobSize;
michael@0 908 found = 1;
michael@0 909 }
michael@0 910 } while (!sdb_done(sqlerr,&retry));
michael@0 911 sqlite3_reset(stmt);
michael@0 912 sqlite3_finalize(stmt);
michael@0 913 stmt = NULL;
michael@0 914 }
michael@0 915
michael@0 916 loser:
michael@0 917 /* fix up the error if necessary */
michael@0 918 if (error == CKR_OK) {
michael@0 919 error = sdb_mapSQLError(sdb_p->type, sqlerr);
michael@0 920 if (!found && error == CKR_OK) {
michael@0 921 error = CKR_OBJECT_HANDLE_INVALID;
michael@0 922 }
michael@0 923 }
michael@0 924
michael@0 925 if (stmt) {
michael@0 926 sqlite3_reset(stmt);
michael@0 927 sqlite3_finalize(stmt);
michael@0 928 }
michael@0 929
michael@0 930 /* if we had to open a new database, free it now */
michael@0 931 if (sqlDB) {
michael@0 932 sdb_closeDBLocal(sdb_p, sqlDB) ;
michael@0 933 }
michael@0 934 return error;
michael@0 935 }
michael@0 936
michael@0 937 CK_RV
michael@0 938 sdb_GetAttributeValue(SDB *sdb, CK_OBJECT_HANDLE object_id,
michael@0 939 CK_ATTRIBUTE *template, CK_ULONG count)
michael@0 940 {
michael@0 941 CK_RV crv;
michael@0 942
michael@0 943 if (count == 0) {
michael@0 944 return CKR_OK;
michael@0 945 }
michael@0 946
michael@0 947 LOCK_SQLITE()
michael@0 948 crv = sdb_GetAttributeValueNoLock(sdb, object_id, template, count);
michael@0 949 UNLOCK_SQLITE()
michael@0 950 return crv;
michael@0 951 }
michael@0 952
michael@0 953 static const char SET_ATTRIBUTE_CMD[] = "UPDATE %s SET %s WHERE id=$ID;";
michael@0 954 CK_RV
michael@0 955 sdb_SetAttributeValue(SDB *sdb, CK_OBJECT_HANDLE object_id,
michael@0 956 const CK_ATTRIBUTE *template, CK_ULONG count)
michael@0 957 {
michael@0 958 SDBPrivate *sdb_p = sdb->private;
michael@0 959 sqlite3 *sqlDB = NULL;
michael@0 960 sqlite3_stmt *stmt = NULL;
michael@0 961 char *setStr = NULL;
michael@0 962 char *newStr = NULL;
michael@0 963 int sqlerr = SQLITE_OK;
michael@0 964 int retry = 0;
michael@0 965 CK_RV error = CKR_OK;
michael@0 966 int i;
michael@0 967
michael@0 968 if ((sdb->sdb_flags & SDB_RDONLY) != 0) {
michael@0 969 return CKR_TOKEN_WRITE_PROTECTED;
michael@0 970 }
michael@0 971
michael@0 972 if (count == 0) {
michael@0 973 return CKR_OK;
michael@0 974 }
michael@0 975
michael@0 976 LOCK_SQLITE()
michael@0 977 setStr = sqlite3_mprintf("");
michael@0 978 for (i=0; setStr && i < count; i++) {
michael@0 979 if (i==0) {
michael@0 980 sqlite3_free(setStr);
michael@0 981 setStr = sqlite3_mprintf("a%x=$VALUE%d",
michael@0 982 template[i].type, i);
michael@0 983 continue;
michael@0 984 }
michael@0 985 newStr = sqlite3_mprintf("%s,a%x=$VALUE%d", setStr,
michael@0 986 template[i].type, i);
michael@0 987 sqlite3_free(setStr);
michael@0 988 setStr = newStr;
michael@0 989 }
michael@0 990 newStr = NULL;
michael@0 991
michael@0 992 if (setStr == NULL) {
michael@0 993 return CKR_HOST_MEMORY;
michael@0 994 }
michael@0 995 newStr = sqlite3_mprintf(SET_ATTRIBUTE_CMD, sdb_p->table, setStr);
michael@0 996 sqlite3_free(setStr);
michael@0 997 if (newStr == NULL) {
michael@0 998 UNLOCK_SQLITE()
michael@0 999 return CKR_HOST_MEMORY;
michael@0 1000 }
michael@0 1001 error = sdb_openDBLocal(sdb_p, &sqlDB, NULL);
michael@0 1002 if (error != CKR_OK) {
michael@0 1003 goto loser;
michael@0 1004 }
michael@0 1005 sqlerr = sqlite3_prepare_v2(sqlDB, newStr, -1, &stmt, NULL);
michael@0 1006 if (sqlerr != SQLITE_OK) goto loser;
michael@0 1007 for (i=0; i < count; i++) {
michael@0 1008 if (template[i].ulValueLen != 0) {
michael@0 1009 sqlerr = sqlite3_bind_blob(stmt, i+1, template[i].pValue,
michael@0 1010 template[i].ulValueLen, SQLITE_STATIC);
michael@0 1011 } else {
michael@0 1012 sqlerr = sqlite3_bind_blob(stmt, i+2, SQLITE_EXPLICIT_NULL,
michael@0 1013 SQLITE_EXPLICIT_NULL_LEN, SQLITE_STATIC);
michael@0 1014 }
michael@0 1015 if (sqlerr != SQLITE_OK) goto loser;
michael@0 1016 }
michael@0 1017 sqlerr = sqlite3_bind_int(stmt, i+1, object_id);
michael@0 1018 if (sqlerr != SQLITE_OK) goto loser;
michael@0 1019
michael@0 1020 do {
michael@0 1021 sqlerr = sqlite3_step(stmt);
michael@0 1022 if (sqlerr == SQLITE_BUSY) {
michael@0 1023 PR_Sleep(SDB_BUSY_RETRY_TIME);
michael@0 1024 }
michael@0 1025 } while (!sdb_done(sqlerr,&retry));
michael@0 1026
michael@0 1027 loser:
michael@0 1028 if (newStr) {
michael@0 1029 sqlite3_free(newStr);
michael@0 1030 }
michael@0 1031 if (error == CKR_OK) {
michael@0 1032 error = sdb_mapSQLError(sdb_p->type, sqlerr);
michael@0 1033 }
michael@0 1034
michael@0 1035 if (stmt) {
michael@0 1036 sqlite3_reset(stmt);
michael@0 1037 sqlite3_finalize(stmt);
michael@0 1038 }
michael@0 1039
michael@0 1040 if (sqlDB) {
michael@0 1041 sdb_closeDBLocal(sdb_p, sqlDB) ;
michael@0 1042 }
michael@0 1043
michael@0 1044 UNLOCK_SQLITE()
michael@0 1045 return error;
michael@0 1046 }
michael@0 1047
michael@0 1048 /*
michael@0 1049 * check to see if a candidate object handle already exists.
michael@0 1050 */
michael@0 1051 static PRBool
michael@0 1052 sdb_objectExists(SDB *sdb, CK_OBJECT_HANDLE candidate)
michael@0 1053 {
michael@0 1054 CK_RV crv;
michael@0 1055 CK_ATTRIBUTE template = { CKA_LABEL, NULL, 0 };
michael@0 1056
michael@0 1057 crv = sdb_GetAttributeValueNoLock(sdb,candidate,&template, 1);
michael@0 1058 if (crv == CKR_OBJECT_HANDLE_INVALID) {
michael@0 1059 return PR_FALSE;
michael@0 1060 }
michael@0 1061 return PR_TRUE;
michael@0 1062 }
michael@0 1063
michael@0 1064 /*
michael@0 1065 * if we're here, we are in a transaction, so it's safe
michael@0 1066 * to examine the current state of the database
michael@0 1067 */
michael@0 1068 static CK_OBJECT_HANDLE
michael@0 1069 sdb_getObjectId(SDB *sdb)
michael@0 1070 {
michael@0 1071 CK_OBJECT_HANDLE candidate;
michael@0 1072 static CK_OBJECT_HANDLE next_obj = CK_INVALID_HANDLE;
michael@0 1073 int count;
michael@0 1074 /*
michael@0 1075 * get an initial object handle to use
michael@0 1076 */
michael@0 1077 if (next_obj == CK_INVALID_HANDLE) {
michael@0 1078 PRTime time;
michael@0 1079 time = PR_Now();
michael@0 1080
michael@0 1081 next_obj = (CK_OBJECT_HANDLE)(time & 0x3fffffffL);
michael@0 1082 }
michael@0 1083 candidate = next_obj++;
michael@0 1084 /* detect that we've looped through all the handles... */
michael@0 1085 for (count = 0; count < 0x40000000; count++, candidate = next_obj++) {
michael@0 1086 /* mask off excess bits */
michael@0 1087 candidate &= 0x3fffffff;
michael@0 1088 /* if we hit zero, go to the next entry */
michael@0 1089 if (candidate == CK_INVALID_HANDLE) {
michael@0 1090 continue;
michael@0 1091 }
michael@0 1092 /* make sure we aren't already using */
michael@0 1093 if (!sdb_objectExists(sdb, candidate)) {
michael@0 1094 /* this one is free */
michael@0 1095 return candidate;
michael@0 1096 }
michael@0 1097 }
michael@0 1098
michael@0 1099 /* no handle is free, fail */
michael@0 1100 return CK_INVALID_HANDLE;
michael@0 1101 }
michael@0 1102
michael@0 1103 static const char CREATE_CMD[] = "INSERT INTO %s (id%s) VALUES($ID%s);";
michael@0 1104 CK_RV
michael@0 1105 sdb_CreateObject(SDB *sdb, CK_OBJECT_HANDLE *object_id,
michael@0 1106 const CK_ATTRIBUTE *template, CK_ULONG count)
michael@0 1107 {
michael@0 1108 SDBPrivate *sdb_p = sdb->private;
michael@0 1109 sqlite3 *sqlDB = NULL;
michael@0 1110 sqlite3_stmt *stmt = NULL;
michael@0 1111 char *columnStr = NULL;
michael@0 1112 char *valueStr = NULL;
michael@0 1113 char *newStr = NULL;
michael@0 1114 int sqlerr = SQLITE_OK;
michael@0 1115 CK_RV error = CKR_OK;
michael@0 1116 CK_OBJECT_HANDLE this_object = CK_INVALID_HANDLE;
michael@0 1117 int retry = 0;
michael@0 1118 int i;
michael@0 1119
michael@0 1120 if ((sdb->sdb_flags & SDB_RDONLY) != 0) {
michael@0 1121 return CKR_TOKEN_WRITE_PROTECTED;
michael@0 1122 }
michael@0 1123
michael@0 1124 LOCK_SQLITE()
michael@0 1125 if ((*object_id != CK_INVALID_HANDLE) &&
michael@0 1126 !sdb_objectExists(sdb, *object_id)) {
michael@0 1127 this_object = *object_id;
michael@0 1128 } else {
michael@0 1129 this_object = sdb_getObjectId(sdb);
michael@0 1130 }
michael@0 1131 if (this_object == CK_INVALID_HANDLE) {
michael@0 1132 UNLOCK_SQLITE();
michael@0 1133 return CKR_HOST_MEMORY;
michael@0 1134 }
michael@0 1135 columnStr = sqlite3_mprintf("");
michael@0 1136 valueStr = sqlite3_mprintf("");
michael@0 1137 *object_id = this_object;
michael@0 1138 for (i=0; columnStr && valueStr && i < count; i++) {
michael@0 1139 newStr = sqlite3_mprintf("%s,a%x", columnStr, template[i].type);
michael@0 1140 sqlite3_free(columnStr);
michael@0 1141 columnStr = newStr;
michael@0 1142 newStr = sqlite3_mprintf("%s,$VALUE%d", valueStr, i);
michael@0 1143 sqlite3_free(valueStr);
michael@0 1144 valueStr = newStr;
michael@0 1145 }
michael@0 1146 newStr = NULL;
michael@0 1147 if ((columnStr == NULL) || (valueStr == NULL)) {
michael@0 1148 if (columnStr) {
michael@0 1149 sqlite3_free(columnStr);
michael@0 1150 }
michael@0 1151 if (valueStr) {
michael@0 1152 sqlite3_free(valueStr);
michael@0 1153 }
michael@0 1154 UNLOCK_SQLITE()
michael@0 1155 return CKR_HOST_MEMORY;
michael@0 1156 }
michael@0 1157 newStr = sqlite3_mprintf(CREATE_CMD, sdb_p->table, columnStr, valueStr);
michael@0 1158 sqlite3_free(columnStr);
michael@0 1159 sqlite3_free(valueStr);
michael@0 1160 error = sdb_openDBLocal(sdb_p, &sqlDB, NULL);
michael@0 1161 if (error != CKR_OK) {
michael@0 1162 goto loser;
michael@0 1163 }
michael@0 1164 sqlerr = sqlite3_prepare_v2(sqlDB, newStr, -1, &stmt, NULL);
michael@0 1165 if (sqlerr != SQLITE_OK) goto loser;
michael@0 1166 sqlerr = sqlite3_bind_int(stmt, 1, *object_id);
michael@0 1167 if (sqlerr != SQLITE_OK) goto loser;
michael@0 1168 for (i=0; i < count; i++) {
michael@0 1169 if (template[i].ulValueLen) {
michael@0 1170 sqlerr = sqlite3_bind_blob(stmt, i+2, template[i].pValue,
michael@0 1171 template[i].ulValueLen, SQLITE_STATIC);
michael@0 1172 } else {
michael@0 1173 sqlerr = sqlite3_bind_blob(stmt, i+2, SQLITE_EXPLICIT_NULL,
michael@0 1174 SQLITE_EXPLICIT_NULL_LEN, SQLITE_STATIC);
michael@0 1175 }
michael@0 1176 if (sqlerr != SQLITE_OK) goto loser;
michael@0 1177 }
michael@0 1178
michael@0 1179 do {
michael@0 1180 sqlerr = sqlite3_step(stmt);
michael@0 1181 if (sqlerr == SQLITE_BUSY) {
michael@0 1182 PR_Sleep(SDB_BUSY_RETRY_TIME);
michael@0 1183 }
michael@0 1184 } while (!sdb_done(sqlerr,&retry));
michael@0 1185
michael@0 1186 loser:
michael@0 1187 if (newStr) {
michael@0 1188 sqlite3_free(newStr);
michael@0 1189 }
michael@0 1190 if (error == CKR_OK) {
michael@0 1191 error = sdb_mapSQLError(sdb_p->type, sqlerr);
michael@0 1192 }
michael@0 1193
michael@0 1194 if (stmt) {
michael@0 1195 sqlite3_reset(stmt);
michael@0 1196 sqlite3_finalize(stmt);
michael@0 1197 }
michael@0 1198
michael@0 1199 if (sqlDB) {
michael@0 1200 sdb_closeDBLocal(sdb_p, sqlDB) ;
michael@0 1201 }
michael@0 1202 UNLOCK_SQLITE()
michael@0 1203
michael@0 1204 return error;
michael@0 1205 }
michael@0 1206
michael@0 1207 static const char DESTROY_CMD[] = "DELETE FROM %s WHERE (id=$ID);";
michael@0 1208 CK_RV
michael@0 1209 sdb_DestroyObject(SDB *sdb, CK_OBJECT_HANDLE object_id)
michael@0 1210 {
michael@0 1211 SDBPrivate *sdb_p = sdb->private;
michael@0 1212 sqlite3 *sqlDB = NULL;
michael@0 1213 sqlite3_stmt *stmt = NULL;
michael@0 1214 char *newStr = NULL;
michael@0 1215 int sqlerr = SQLITE_OK;
michael@0 1216 CK_RV error = CKR_OK;
michael@0 1217 int retry = 0;
michael@0 1218
michael@0 1219 if ((sdb->sdb_flags & SDB_RDONLY) != 0) {
michael@0 1220 return CKR_TOKEN_WRITE_PROTECTED;
michael@0 1221 }
michael@0 1222
michael@0 1223 LOCK_SQLITE()
michael@0 1224 error = sdb_openDBLocal(sdb_p, &sqlDB, NULL);
michael@0 1225 if (error != CKR_OK) {
michael@0 1226 goto loser;
michael@0 1227 }
michael@0 1228 newStr = sqlite3_mprintf(DESTROY_CMD, sdb_p->table);
michael@0 1229 if (newStr == NULL) {
michael@0 1230 error = CKR_HOST_MEMORY;
michael@0 1231 goto loser;
michael@0 1232 }
michael@0 1233 sqlerr =sqlite3_prepare_v2(sqlDB, newStr, -1, &stmt, NULL);
michael@0 1234 sqlite3_free(newStr);
michael@0 1235 if (sqlerr != SQLITE_OK) goto loser;
michael@0 1236 sqlerr =sqlite3_bind_int(stmt, 1, object_id);
michael@0 1237 if (sqlerr != SQLITE_OK) goto loser;
michael@0 1238
michael@0 1239 do {
michael@0 1240 sqlerr = sqlite3_step(stmt);
michael@0 1241 if (sqlerr == SQLITE_BUSY) {
michael@0 1242 PR_Sleep(SDB_BUSY_RETRY_TIME);
michael@0 1243 }
michael@0 1244 } while (!sdb_done(sqlerr,&retry));
michael@0 1245
michael@0 1246 loser:
michael@0 1247 if (error == CKR_OK) {
michael@0 1248 error = sdb_mapSQLError(sdb_p->type, sqlerr);
michael@0 1249 }
michael@0 1250
michael@0 1251 if (stmt) {
michael@0 1252 sqlite3_reset(stmt);
michael@0 1253 sqlite3_finalize(stmt);
michael@0 1254 }
michael@0 1255
michael@0 1256 if (sqlDB) {
michael@0 1257 sdb_closeDBLocal(sdb_p, sqlDB) ;
michael@0 1258 }
michael@0 1259
michael@0 1260 UNLOCK_SQLITE()
michael@0 1261 return error;
michael@0 1262 }
michael@0 1263
michael@0 1264 static const char BEGIN_CMD[] = "BEGIN IMMEDIATE TRANSACTION;";
michael@0 1265 /*
michael@0 1266 * start a transaction.
michael@0 1267 *
michael@0 1268 * We need to open a new database, then store that new database into
michael@0 1269 * the private data structure. We open the database first, then use locks
michael@0 1270 * to protect storing the data to prevent deadlocks.
michael@0 1271 */
michael@0 1272 CK_RV
michael@0 1273 sdb_Begin(SDB *sdb)
michael@0 1274 {
michael@0 1275 SDBPrivate *sdb_p = sdb->private;
michael@0 1276 sqlite3 *sqlDB = NULL;
michael@0 1277 sqlite3_stmt *stmt = NULL;
michael@0 1278 int sqlerr = SQLITE_OK;
michael@0 1279 CK_RV error = CKR_OK;
michael@0 1280 int retry = 0;
michael@0 1281
michael@0 1282
michael@0 1283 if ((sdb->sdb_flags & SDB_RDONLY) != 0) {
michael@0 1284 return CKR_TOKEN_WRITE_PROTECTED;
michael@0 1285 }
michael@0 1286
michael@0 1287
michael@0 1288 LOCK_SQLITE()
michael@0 1289
michael@0 1290 /* get a new version that we will use for the entire transaction */
michael@0 1291 sqlerr = sdb_openDB(sdb_p->sqlDBName, &sqlDB, SDB_RDWR);
michael@0 1292 if (sqlerr != SQLITE_OK) {
michael@0 1293 goto loser;
michael@0 1294 }
michael@0 1295
michael@0 1296 sqlerr =sqlite3_prepare_v2(sqlDB, BEGIN_CMD, -1, &stmt, NULL);
michael@0 1297
michael@0 1298 do {
michael@0 1299 sqlerr = sqlite3_step(stmt);
michael@0 1300 if (sqlerr == SQLITE_BUSY) {
michael@0 1301 PR_Sleep(SDB_BUSY_RETRY_TIME);
michael@0 1302 }
michael@0 1303 } while (!sdb_done(sqlerr,&retry));
michael@0 1304
michael@0 1305 if (stmt) {
michael@0 1306 sqlite3_reset(stmt);
michael@0 1307 sqlite3_finalize(stmt);
michael@0 1308 }
michael@0 1309
michael@0 1310 loser:
michael@0 1311 error = sdb_mapSQLError(sdb_p->type, sqlerr);
michael@0 1312
michael@0 1313 /* we are starting a new transaction,
michael@0 1314 * and if we succeeded, then save this database for the rest of
michael@0 1315 * our transaction */
michael@0 1316 if (error == CKR_OK) {
michael@0 1317 /* we hold a 'BEGIN TRANSACTION' and a sdb_p->lock. At this point
michael@0 1318 * sdb_p->sqlXactDB MUST be null */
michael@0 1319 PR_EnterMonitor(sdb_p->dbMon);
michael@0 1320 PORT_Assert(sdb_p->sqlXactDB == NULL);
michael@0 1321 sdb_p->sqlXactDB = sqlDB;
michael@0 1322 sdb_p->sqlXactThread = PR_GetCurrentThread();
michael@0 1323 PR_ExitMonitor(sdb_p->dbMon);
michael@0 1324 } else {
michael@0 1325 /* we failed to start our transaction,
michael@0 1326 * free any databases we opened. */
michael@0 1327 if (sqlDB) {
michael@0 1328 sqlite3_close(sqlDB);
michael@0 1329 }
michael@0 1330 }
michael@0 1331
michael@0 1332 UNLOCK_SQLITE()
michael@0 1333 return error;
michael@0 1334 }
michael@0 1335
michael@0 1336 /*
michael@0 1337 * Complete a transaction. Basically undo everything we did in begin.
michael@0 1338 * There are 2 flavors Abort and Commit. Basically the only differerence between
michael@0 1339 * these 2 are what the database will show. (no change in to former, change in
michael@0 1340 * the latter).
michael@0 1341 */
michael@0 1342 static CK_RV
michael@0 1343 sdb_complete(SDB *sdb, const char *cmd)
michael@0 1344 {
michael@0 1345 SDBPrivate *sdb_p = sdb->private;
michael@0 1346 sqlite3 *sqlDB = NULL;
michael@0 1347 sqlite3_stmt *stmt = NULL;
michael@0 1348 int sqlerr = SQLITE_OK;
michael@0 1349 CK_RV error = CKR_OK;
michael@0 1350 int retry = 0;
michael@0 1351
michael@0 1352
michael@0 1353 if ((sdb->sdb_flags & SDB_RDONLY) != 0) {
michael@0 1354 return CKR_TOKEN_WRITE_PROTECTED;
michael@0 1355 }
michael@0 1356
michael@0 1357 /* We must have a transation database, or we shouldn't have arrived here */
michael@0 1358 PR_EnterMonitor(sdb_p->dbMon);
michael@0 1359 PORT_Assert(sdb_p->sqlXactDB);
michael@0 1360 if (sdb_p->sqlXactDB == NULL) {
michael@0 1361 PR_ExitMonitor(sdb_p->dbMon);
michael@0 1362 return CKR_GENERAL_ERROR; /* shouldn't happen */
michael@0 1363 }
michael@0 1364 PORT_Assert( sdb_p->sqlXactThread == PR_GetCurrentThread());
michael@0 1365 if ( sdb_p->sqlXactThread != PR_GetCurrentThread()) {
michael@0 1366 PR_ExitMonitor(sdb_p->dbMon);
michael@0 1367 return CKR_GENERAL_ERROR; /* shouldn't happen */
michael@0 1368 }
michael@0 1369 sqlDB = sdb_p->sqlXactDB;
michael@0 1370 sdb_p->sqlXactDB = NULL; /* no one else can get to this DB,
michael@0 1371 * safe to unlock */
michael@0 1372 sdb_p->sqlXactThread = NULL;
michael@0 1373 PR_ExitMonitor(sdb_p->dbMon);
michael@0 1374
michael@0 1375 sqlerr =sqlite3_prepare_v2(sqlDB, cmd, -1, &stmt, NULL);
michael@0 1376
michael@0 1377 do {
michael@0 1378 sqlerr = sqlite3_step(stmt);
michael@0 1379 if (sqlerr == SQLITE_BUSY) {
michael@0 1380 PR_Sleep(SDB_BUSY_RETRY_TIME);
michael@0 1381 }
michael@0 1382 } while (!sdb_done(sqlerr,&retry));
michael@0 1383
michael@0 1384 /* Pending BEGIN TRANSACTIONS Can move forward at this point. */
michael@0 1385
michael@0 1386 if (stmt) {
michael@0 1387 sqlite3_reset(stmt);
michael@0 1388 sqlite3_finalize(stmt);
michael@0 1389 }
michael@0 1390
michael@0 1391 /* we we have a cached DB image, update it as well */
michael@0 1392 if (sdb_p->cacheTable) {
michael@0 1393 PR_EnterMonitor(sdb_p->dbMon);
michael@0 1394 sdb_updateCache(sdb_p);
michael@0 1395 PR_ExitMonitor(sdb_p->dbMon);
michael@0 1396 }
michael@0 1397
michael@0 1398 error = sdb_mapSQLError(sdb_p->type, sqlerr);
michael@0 1399
michael@0 1400 /* We just finished a transaction.
michael@0 1401 * Free the database, and remove it from the list */
michael@0 1402 sqlite3_close(sqlDB);
michael@0 1403
michael@0 1404 return error;
michael@0 1405 }
michael@0 1406
michael@0 1407 static const char COMMIT_CMD[] = "COMMIT TRANSACTION;";
michael@0 1408 CK_RV
michael@0 1409 sdb_Commit(SDB *sdb)
michael@0 1410 {
michael@0 1411 CK_RV crv;
michael@0 1412 LOCK_SQLITE()
michael@0 1413 crv = sdb_complete(sdb,COMMIT_CMD);
michael@0 1414 UNLOCK_SQLITE()
michael@0 1415 return crv;
michael@0 1416 }
michael@0 1417
michael@0 1418 static const char ROLLBACK_CMD[] = "ROLLBACK TRANSACTION;";
michael@0 1419 CK_RV
michael@0 1420 sdb_Abort(SDB *sdb)
michael@0 1421 {
michael@0 1422 CK_RV crv;
michael@0 1423 LOCK_SQLITE()
michael@0 1424 crv = sdb_complete(sdb,ROLLBACK_CMD);
michael@0 1425 UNLOCK_SQLITE()
michael@0 1426 return crv;
michael@0 1427 }
michael@0 1428
michael@0 1429 static int tableExists(sqlite3 *sqlDB, const char *tableName);
michael@0 1430
michael@0 1431 static const char GET_PW_CMD[] = "SELECT ALL * FROM metaData WHERE id=$ID;";
michael@0 1432 CK_RV
michael@0 1433 sdb_GetMetaData(SDB *sdb, const char *id, SECItem *item1, SECItem *item2)
michael@0 1434 {
michael@0 1435 SDBPrivate *sdb_p = sdb->private;
michael@0 1436 sqlite3 *sqlDB = sdb_p->sqlXactDB;
michael@0 1437 sqlite3_stmt *stmt = NULL;
michael@0 1438 int sqlerr = SQLITE_OK;
michael@0 1439 CK_RV error = CKR_OK;
michael@0 1440 int found = 0;
michael@0 1441 int retry = 0;
michael@0 1442
michael@0 1443 LOCK_SQLITE()
michael@0 1444 error = sdb_openDBLocal(sdb_p, &sqlDB, NULL);
michael@0 1445 if (error != CKR_OK) {
michael@0 1446 goto loser;
michael@0 1447 }
michael@0 1448
michael@0 1449 /* handle 'test' versions of the sqlite db */
michael@0 1450 sqlerr = sqlite3_prepare_v2(sqlDB, GET_PW_CMD, -1, &stmt, NULL);
michael@0 1451 /* Sigh, if we created a new table since we opened the database,
michael@0 1452 * the database handle will not see the new table, we need to close this
michael@0 1453 * database and reopen it. This is safe because we are holding the lock
michael@0 1454 * still. */
michael@0 1455 if (sqlerr == SQLITE_SCHEMA) {
michael@0 1456 sqlerr = sdb_reopenDBLocal(sdb_p, &sqlDB);
michael@0 1457 if (sqlerr != SQLITE_OK) {
michael@0 1458 goto loser;
michael@0 1459 }
michael@0 1460 sqlerr = sqlite3_prepare_v2(sqlDB, GET_PW_CMD, -1, &stmt, NULL);
michael@0 1461 }
michael@0 1462 if (sqlerr != SQLITE_OK) goto loser;
michael@0 1463 sqlerr = sqlite3_bind_text(stmt, 1, id, PORT_Strlen(id), SQLITE_STATIC);
michael@0 1464 do {
michael@0 1465 sqlerr = sqlite3_step(stmt);
michael@0 1466 if (sqlerr == SQLITE_BUSY) {
michael@0 1467 PR_Sleep(SDB_BUSY_RETRY_TIME);
michael@0 1468 }
michael@0 1469 if (sqlerr == SQLITE_ROW) {
michael@0 1470 const char *blobData;
michael@0 1471 unsigned int len = item1->len;
michael@0 1472 item1->len = sqlite3_column_bytes(stmt, 1);
michael@0 1473 if (item1->len > len) {
michael@0 1474 error = CKR_BUFFER_TOO_SMALL;
michael@0 1475 continue;
michael@0 1476 }
michael@0 1477 blobData = sqlite3_column_blob(stmt, 1);
michael@0 1478 PORT_Memcpy(item1->data,blobData, item1->len);
michael@0 1479 if (item2) {
michael@0 1480 len = item2->len;
michael@0 1481 item2->len = sqlite3_column_bytes(stmt, 2);
michael@0 1482 if (item2->len > len) {
michael@0 1483 error = CKR_BUFFER_TOO_SMALL;
michael@0 1484 continue;
michael@0 1485 }
michael@0 1486 blobData = sqlite3_column_blob(stmt, 2);
michael@0 1487 PORT_Memcpy(item2->data,blobData, item2->len);
michael@0 1488 }
michael@0 1489 found = 1;
michael@0 1490 }
michael@0 1491 } while (!sdb_done(sqlerr,&retry));
michael@0 1492
michael@0 1493 loser:
michael@0 1494 /* fix up the error if necessary */
michael@0 1495 if (error == CKR_OK) {
michael@0 1496 error = sdb_mapSQLError(sdb_p->type, sqlerr);
michael@0 1497 if (!found && error == CKR_OK) {
michael@0 1498 error = CKR_OBJECT_HANDLE_INVALID;
michael@0 1499 }
michael@0 1500 }
michael@0 1501
michael@0 1502 if (stmt) {
michael@0 1503 sqlite3_reset(stmt);
michael@0 1504 sqlite3_finalize(stmt);
michael@0 1505 }
michael@0 1506
michael@0 1507 if (sqlDB) {
michael@0 1508 sdb_closeDBLocal(sdb_p, sqlDB) ;
michael@0 1509 }
michael@0 1510 UNLOCK_SQLITE()
michael@0 1511
michael@0 1512 return error;
michael@0 1513 }
michael@0 1514
michael@0 1515 static const char PW_CREATE_TABLE_CMD[] =
michael@0 1516 "CREATE TABLE metaData (id PRIMARY KEY UNIQUE ON CONFLICT REPLACE, item1, item2);";
michael@0 1517 static const char PW_CREATE_CMD[] =
michael@0 1518 "INSERT INTO metaData (id,item1,item2) VALUES($ID,$ITEM1,$ITEM2);";
michael@0 1519 static const char MD_CREATE_CMD[] =
michael@0 1520 "INSERT INTO metaData (id,item1) VALUES($ID,$ITEM1);";
michael@0 1521 CK_RV
michael@0 1522 sdb_PutMetaData(SDB *sdb, const char *id, const SECItem *item1,
michael@0 1523 const SECItem *item2)
michael@0 1524 {
michael@0 1525 SDBPrivate *sdb_p = sdb->private;
michael@0 1526 sqlite3 *sqlDB = sdb_p->sqlXactDB;
michael@0 1527 sqlite3_stmt *stmt = NULL;
michael@0 1528 int sqlerr = SQLITE_OK;
michael@0 1529 CK_RV error = CKR_OK;
michael@0 1530 int retry = 0;
michael@0 1531 const char *cmd = PW_CREATE_CMD;
michael@0 1532
michael@0 1533 if ((sdb->sdb_flags & SDB_RDONLY) != 0) {
michael@0 1534 return CKR_TOKEN_WRITE_PROTECTED;
michael@0 1535 }
michael@0 1536
michael@0 1537 LOCK_SQLITE()
michael@0 1538 error = sdb_openDBLocal(sdb_p, &sqlDB, NULL);
michael@0 1539 if (error != CKR_OK) {
michael@0 1540 goto loser;
michael@0 1541 }
michael@0 1542
michael@0 1543 if (!tableExists(sqlDB, "metaData")) {
michael@0 1544 sqlerr = sqlite3_exec(sqlDB, PW_CREATE_TABLE_CMD, NULL, 0, NULL);
michael@0 1545 if (sqlerr != SQLITE_OK) goto loser;
michael@0 1546 }
michael@0 1547 if (item2 == NULL) {
michael@0 1548 cmd = MD_CREATE_CMD;
michael@0 1549 }
michael@0 1550 sqlerr = sqlite3_prepare_v2(sqlDB, cmd, -1, &stmt, NULL);
michael@0 1551 if (sqlerr != SQLITE_OK) goto loser;
michael@0 1552 sqlerr = sqlite3_bind_text(stmt, 1, id, PORT_Strlen(id), SQLITE_STATIC);
michael@0 1553 if (sqlerr != SQLITE_OK) goto loser;
michael@0 1554 sqlerr = sqlite3_bind_blob(stmt, 2, item1->data, item1->len, SQLITE_STATIC);
michael@0 1555 if (sqlerr != SQLITE_OK) goto loser;
michael@0 1556 if (item2) {
michael@0 1557 sqlerr = sqlite3_bind_blob(stmt, 3, item2->data,
michael@0 1558 item2->len, SQLITE_STATIC);
michael@0 1559 if (sqlerr != SQLITE_OK) goto loser;
michael@0 1560 }
michael@0 1561
michael@0 1562 do {
michael@0 1563 sqlerr = sqlite3_step(stmt);
michael@0 1564 if (sqlerr == SQLITE_BUSY) {
michael@0 1565 PR_Sleep(SDB_BUSY_RETRY_TIME);
michael@0 1566 }
michael@0 1567 } while (!sdb_done(sqlerr,&retry));
michael@0 1568
michael@0 1569 loser:
michael@0 1570 /* fix up the error if necessary */
michael@0 1571 if (error == CKR_OK) {
michael@0 1572 error = sdb_mapSQLError(sdb_p->type, sqlerr);
michael@0 1573 }
michael@0 1574
michael@0 1575 if (stmt) {
michael@0 1576 sqlite3_reset(stmt);
michael@0 1577 sqlite3_finalize(stmt);
michael@0 1578 }
michael@0 1579
michael@0 1580 if (sqlDB) {
michael@0 1581 sdb_closeDBLocal(sdb_p, sqlDB) ;
michael@0 1582 }
michael@0 1583 UNLOCK_SQLITE()
michael@0 1584
michael@0 1585 return error;
michael@0 1586 }
michael@0 1587
michael@0 1588 static const char RESET_CMD[] = "DROP TABLE IF EXISTS %s;";
michael@0 1589 CK_RV
michael@0 1590 sdb_Reset(SDB *sdb)
michael@0 1591 {
michael@0 1592 SDBPrivate *sdb_p = sdb->private;
michael@0 1593 sqlite3 *sqlDB = NULL;
michael@0 1594 char *newStr;
michael@0 1595 int sqlerr = SQLITE_OK;
michael@0 1596 CK_RV error = CKR_OK;
michael@0 1597
michael@0 1598 /* only Key databases can be reset */
michael@0 1599 if (sdb_p->type != SDB_KEY) {
michael@0 1600 return CKR_OBJECT_HANDLE_INVALID;
michael@0 1601 }
michael@0 1602
michael@0 1603 LOCK_SQLITE()
michael@0 1604 error = sdb_openDBLocal(sdb_p, &sqlDB, NULL);
michael@0 1605 if (error != CKR_OK) {
michael@0 1606 goto loser;
michael@0 1607 }
michael@0 1608
michael@0 1609 /* delete the key table */
michael@0 1610 newStr = sqlite3_mprintf(RESET_CMD, sdb_p->table);
michael@0 1611 if (newStr == NULL) {
michael@0 1612 error = CKR_HOST_MEMORY;
michael@0 1613 goto loser;
michael@0 1614 }
michael@0 1615 sqlerr = sqlite3_exec(sqlDB, newStr, NULL, 0, NULL);
michael@0 1616 sqlite3_free(newStr);
michael@0 1617
michael@0 1618 if (sqlerr != SQLITE_OK) goto loser;
michael@0 1619
michael@0 1620 /* delete the password entry table */
michael@0 1621 sqlerr = sqlite3_exec(sqlDB, "DROP TABLE IF EXISTS metaData;",
michael@0 1622 NULL, 0, NULL);
michael@0 1623
michael@0 1624 loser:
michael@0 1625 /* fix up the error if necessary */
michael@0 1626 if (error == CKR_OK) {
michael@0 1627 error = sdb_mapSQLError(sdb_p->type, sqlerr);
michael@0 1628 }
michael@0 1629
michael@0 1630 if (sqlDB) {
michael@0 1631 sdb_closeDBLocal(sdb_p, sqlDB) ;
michael@0 1632 }
michael@0 1633
michael@0 1634 UNLOCK_SQLITE()
michael@0 1635 return error;
michael@0 1636 }
michael@0 1637
michael@0 1638
michael@0 1639 CK_RV
michael@0 1640 sdb_Close(SDB *sdb)
michael@0 1641 {
michael@0 1642 SDBPrivate *sdb_p = sdb->private;
michael@0 1643 int sqlerr = SQLITE_OK;
michael@0 1644 sdbDataType type = sdb_p->type;
michael@0 1645
michael@0 1646 sqlerr = sqlite3_close(sdb_p->sqlReadDB);
michael@0 1647 PORT_Free(sdb_p->sqlDBName);
michael@0 1648 if (sdb_p->cacheTable) {
michael@0 1649 sqlite3_free(sdb_p->cacheTable);
michael@0 1650 }
michael@0 1651 if (sdb_p->dbMon) {
michael@0 1652 PR_DestroyMonitor(sdb_p->dbMon);
michael@0 1653 }
michael@0 1654 free(sdb_p);
michael@0 1655 free(sdb);
michael@0 1656 return sdb_mapSQLError(type, sqlerr);
michael@0 1657 }
michael@0 1658
michael@0 1659
michael@0 1660 /*
michael@0 1661 * functions to support open
michael@0 1662 */
michael@0 1663
michael@0 1664 static const char CHECK_TABLE_CMD[] = "SELECT ALL * FROM %s LIMIT 0;";
michael@0 1665 /* return 1 if sqlDB contains table 'tableName */
michael@0 1666 static int tableExists(sqlite3 *sqlDB, const char *tableName)
michael@0 1667 {
michael@0 1668 char * cmd = sqlite3_mprintf(CHECK_TABLE_CMD, tableName);
michael@0 1669 int sqlerr = SQLITE_OK;
michael@0 1670
michael@0 1671 if (cmd == NULL) {
michael@0 1672 return 0;
michael@0 1673 }
michael@0 1674
michael@0 1675 sqlerr = sqlite3_exec(sqlDB, cmd, NULL, 0, 0);
michael@0 1676 sqlite3_free(cmd);
michael@0 1677
michael@0 1678 return (sqlerr == SQLITE_OK) ? 1 : 0;
michael@0 1679 }
michael@0 1680
michael@0 1681 void sdb_SetForkState(PRBool forked)
michael@0 1682 {
michael@0 1683 /* XXXright now this is a no-op. The global fork state in the softokn3
michael@0 1684 * shared library is already taken care of at the PKCS#11 level.
michael@0 1685 * If and when we add fork state to the sqlite shared library and extern
michael@0 1686 * interface, we will need to set it and reset it from here */
michael@0 1687 }
michael@0 1688
michael@0 1689 /*
michael@0 1690 * initialize a single database
michael@0 1691 */
michael@0 1692 static const char INIT_CMD[] =
michael@0 1693 "CREATE TABLE %s (id PRIMARY KEY UNIQUE ON CONFLICT ABORT%s)";
michael@0 1694 static const char ALTER_CMD[] =
michael@0 1695 "ALTER TABLE %s ADD COLUMN a%x";
michael@0 1696
michael@0 1697 CK_RV
michael@0 1698 sdb_init(char *dbname, char *table, sdbDataType type, int *inUpdate,
michael@0 1699 int *newInit, int flags, PRUint32 accessOps, SDB **pSdb)
michael@0 1700 {
michael@0 1701 int i;
michael@0 1702 char *initStr = NULL;
michael@0 1703 char *newStr;
michael@0 1704 int inTransaction = 0;
michael@0 1705 SDB *sdb = NULL;
michael@0 1706 SDBPrivate *sdb_p = NULL;
michael@0 1707 sqlite3 *sqlDB = NULL;
michael@0 1708 int sqlerr = SQLITE_OK;
michael@0 1709 CK_RV error = CKR_OK;
michael@0 1710 char *cacheTable = NULL;
michael@0 1711 PRIntervalTime now = 0;
michael@0 1712 char *env;
michael@0 1713 PRBool enableCache = PR_FALSE;
michael@0 1714 PRBool create;
michael@0 1715
michael@0 1716 *pSdb = NULL;
michael@0 1717 *inUpdate = 0;
michael@0 1718
michael@0 1719 /* sqlite3 doesn't have a flag to specify that we want to
michael@0 1720 * open the database read only. If the db doesn't exist,
michael@0 1721 * sqlite3 will always create it.
michael@0 1722 */
michael@0 1723 LOCK_SQLITE();
michael@0 1724 create = (PR_Access(dbname, PR_ACCESS_EXISTS) != PR_SUCCESS);
michael@0 1725 if ((flags == SDB_RDONLY) && create) {
michael@0 1726 error = sdb_mapSQLError(type, SQLITE_CANTOPEN);
michael@0 1727 goto loser;
michael@0 1728 }
michael@0 1729 sqlerr = sdb_openDB(dbname, &sqlDB, flags);
michael@0 1730 if (sqlerr != SQLITE_OK) {
michael@0 1731 error = sdb_mapSQLError(type, sqlerr);
michael@0 1732 goto loser;
michael@0 1733 }
michael@0 1734 /* sql created the file, but it doesn't set appropriate modes for
michael@0 1735 * a database */
michael@0 1736 if (create) {
michael@0 1737 /* NO NSPR call for this? :( */
michael@0 1738 chmod (dbname, 0600);
michael@0 1739 }
michael@0 1740
michael@0 1741 if (flags != SDB_RDONLY) {
michael@0 1742 sqlerr = sqlite3_exec(sqlDB, BEGIN_CMD, NULL, 0, NULL);
michael@0 1743 if (sqlerr != SQLITE_OK) {
michael@0 1744 error = sdb_mapSQLError(type, sqlerr);
michael@0 1745 goto loser;
michael@0 1746 }
michael@0 1747 inTransaction = 1;
michael@0 1748 }
michael@0 1749 if (!tableExists(sqlDB,table)) {
michael@0 1750 *newInit = 1;
michael@0 1751 if (flags != SDB_CREATE) {
michael@0 1752 error = sdb_mapSQLError(type, SQLITE_CANTOPEN);
michael@0 1753 goto loser;
michael@0 1754 }
michael@0 1755 initStr = sqlite3_mprintf("");
michael@0 1756 for (i=0; initStr && i < known_attributes_size; i++) {
michael@0 1757 newStr = sqlite3_mprintf("%s, a%x",initStr, known_attributes[i]);
michael@0 1758 sqlite3_free(initStr);
michael@0 1759 initStr = newStr;
michael@0 1760 }
michael@0 1761 if (initStr == NULL) {
michael@0 1762 error = CKR_HOST_MEMORY;
michael@0 1763 goto loser;
michael@0 1764 }
michael@0 1765
michael@0 1766 newStr = sqlite3_mprintf(INIT_CMD, table, initStr);
michael@0 1767 sqlite3_free(initStr);
michael@0 1768 if (newStr == NULL) {
michael@0 1769 error = CKR_HOST_MEMORY;
michael@0 1770 goto loser;
michael@0 1771 }
michael@0 1772 sqlerr = sqlite3_exec(sqlDB, newStr, NULL, 0, NULL);
michael@0 1773 sqlite3_free(newStr);
michael@0 1774 if (sqlerr != SQLITE_OK) {
michael@0 1775 error = sdb_mapSQLError(type, sqlerr);
michael@0 1776 goto loser;
michael@0 1777 }
michael@0 1778
michael@0 1779 newStr = sqlite3_mprintf(CREATE_ISSUER_INDEX_CMD, table);
michael@0 1780 if (newStr == NULL) {
michael@0 1781 error = CKR_HOST_MEMORY;
michael@0 1782 goto loser;
michael@0 1783 }
michael@0 1784 sqlerr = sqlite3_exec(sqlDB, newStr, NULL, 0, NULL);
michael@0 1785 sqlite3_free(newStr);
michael@0 1786 if (sqlerr != SQLITE_OK) {
michael@0 1787 error = sdb_mapSQLError(type, sqlerr);
michael@0 1788 goto loser;
michael@0 1789 }
michael@0 1790
michael@0 1791 newStr = sqlite3_mprintf(CREATE_SUBJECT_INDEX_CMD, table);
michael@0 1792 if (newStr == NULL) {
michael@0 1793 error = CKR_HOST_MEMORY;
michael@0 1794 goto loser;
michael@0 1795 }
michael@0 1796 sqlerr = sqlite3_exec(sqlDB, newStr, NULL, 0, NULL);
michael@0 1797 sqlite3_free(newStr);
michael@0 1798 if (sqlerr != SQLITE_OK) {
michael@0 1799 error = sdb_mapSQLError(type, sqlerr);
michael@0 1800 goto loser;
michael@0 1801 }
michael@0 1802
michael@0 1803 newStr = sqlite3_mprintf(CREATE_LABEL_INDEX_CMD, table);
michael@0 1804 if (newStr == NULL) {
michael@0 1805 error = CKR_HOST_MEMORY;
michael@0 1806 goto loser;
michael@0 1807 }
michael@0 1808 sqlerr = sqlite3_exec(sqlDB, newStr, NULL, 0, NULL);
michael@0 1809 sqlite3_free(newStr);
michael@0 1810 if (sqlerr != SQLITE_OK) {
michael@0 1811 error = sdb_mapSQLError(type, sqlerr);
michael@0 1812 goto loser;
michael@0 1813 }
michael@0 1814
michael@0 1815 newStr = sqlite3_mprintf(CREATE_ID_INDEX_CMD, table);
michael@0 1816 if (newStr == NULL) {
michael@0 1817 error = CKR_HOST_MEMORY;
michael@0 1818 goto loser;
michael@0 1819 }
michael@0 1820 sqlerr = sqlite3_exec(sqlDB, newStr, NULL, 0, NULL);
michael@0 1821 sqlite3_free(newStr);
michael@0 1822 if (sqlerr != SQLITE_OK) {
michael@0 1823 error = sdb_mapSQLError(type, sqlerr);
michael@0 1824 goto loser;
michael@0 1825 }
michael@0 1826 }
michael@0 1827 /*
michael@0 1828 * detect the case where we have created the database, but have
michael@0 1829 * not yet updated it.
michael@0 1830 *
michael@0 1831 * We only check the Key database because only the key database has
michael@0 1832 * a metaData table. The metaData table is created when a password
michael@0 1833 * is set, or in the case of update, when a password is supplied.
michael@0 1834 * If no key database exists, then the update would have happened immediately
michael@0 1835 * on noticing that the cert database didn't exist (see newInit set above).
michael@0 1836 */
michael@0 1837 if (type == SDB_KEY && !tableExists(sqlDB, "metaData")) {
michael@0 1838 *newInit = 1;
michael@0 1839 }
michael@0 1840
michael@0 1841 /* access to network filesystems are significantly slower than local ones
michael@0 1842 * for database operations. In those cases we need to create a cached copy
michael@0 1843 * of the database in a temporary location on the local disk. SQLITE
michael@0 1844 * already provides a way to create a temporary table and initialize it,
michael@0 1845 * so we use it for the cache (see sdb_buildCache for how it's done).*/
michael@0 1846
michael@0 1847 /*
michael@0 1848 * we decide whether or not to use the cache based on the following input.
michael@0 1849 *
michael@0 1850 * NSS_SDB_USE_CACHE environment variable is non-existant or set to
michael@0 1851 * anything other than "no" or "yes" ("auto", for instance).
michael@0 1852 * This is the normal case. NSS will measure the performance of access
michael@0 1853 * to the temp database versus the access to the users passed in
michael@0 1854 * database location. If the temp database location is "significantly"
michael@0 1855 * faster we will use the cache.
michael@0 1856 *
michael@0 1857 * NSS_SDB_USE_CACHE environment variable is set to "no": cache will not
michael@0 1858 * be used.
michael@0 1859 *
michael@0 1860 * NSS_SDB_USE_CACHE environment variable is set to "yes": cache will
michael@0 1861 * always be used.
michael@0 1862 *
michael@0 1863 * It is expected that most applications would use the "auto" selection,
michael@0 1864 * the environment variable is primarily to simplify testing, and to
michael@0 1865 * correct potential corner cases where */
michael@0 1866
michael@0 1867 env = PR_GetEnv("NSS_SDB_USE_CACHE");
michael@0 1868
michael@0 1869 if (env && PORT_Strcasecmp(env,"no") == 0) {
michael@0 1870 enableCache = PR_FALSE;
michael@0 1871 } else if (env && PORT_Strcasecmp(env,"yes") == 0) {
michael@0 1872 enableCache = PR_TRUE;
michael@0 1873 } else {
michael@0 1874 char *tempDir = NULL;
michael@0 1875 PRUint32 tempOps = 0;
michael@0 1876 /*
michael@0 1877 * Use PR_Access to determine how expensive it
michael@0 1878 * is to check for the existance of a local file compared to the same
michael@0 1879 * check in the temp directory. If the temp directory is faster, cache
michael@0 1880 * the database there. */
michael@0 1881 tempDir = sdb_getTempDir(sqlDB);
michael@0 1882 if (tempDir) {
michael@0 1883 tempOps = sdb_measureAccess(tempDir);
michael@0 1884 PORT_Free(tempDir);
michael@0 1885
michael@0 1886 /* There is a cost to continually copying the database.
michael@0 1887 * Account for that cost with the arbitrary factor of 10 */
michael@0 1888 enableCache = (PRBool)(tempOps > accessOps * 10);
michael@0 1889 }
michael@0 1890 }
michael@0 1891
michael@0 1892 if (enableCache) {
michael@0 1893 /* try to set the temp store to memory.*/
michael@0 1894 sqlite3_exec(sqlDB, "PRAGMA temp_store=MEMORY", NULL, 0, NULL);
michael@0 1895 /* Failure to set the temp store to memory is not fatal,
michael@0 1896 * ignore the error */
michael@0 1897
michael@0 1898 cacheTable = sqlite3_mprintf("%sCache",table);
michael@0 1899 if (cacheTable == NULL) {
michael@0 1900 error = CKR_HOST_MEMORY;
michael@0 1901 goto loser;
michael@0 1902 }
michael@0 1903 /* build the cache table */
michael@0 1904 error = sdb_buildCache(sqlDB, type, cacheTable, table);
michael@0 1905 if (error != CKR_OK) {
michael@0 1906 goto loser;
michael@0 1907 }
michael@0 1908 /* initialize the last cache build time */
michael@0 1909 now = PR_IntervalNow();
michael@0 1910 }
michael@0 1911
michael@0 1912 sdb = (SDB *) malloc(sizeof(SDB));
michael@0 1913 sdb_p = (SDBPrivate *) malloc(sizeof(SDBPrivate));
michael@0 1914
michael@0 1915 /* invariant fields */
michael@0 1916 sdb_p->sqlDBName = PORT_Strdup(dbname);
michael@0 1917 sdb_p->type = type;
michael@0 1918 sdb_p->table = table;
michael@0 1919 sdb_p->cacheTable = cacheTable;
michael@0 1920 sdb_p->lastUpdateTime = now;
michael@0 1921 /* set the cache delay time. This is how long we will wait before we
michael@0 1922 * decide the existing cache is stale. Currently set to 10 sec */
michael@0 1923 sdb_p->updateInterval = PR_SecondsToInterval(10);
michael@0 1924 sdb_p->dbMon = PR_NewMonitor();
michael@0 1925 /* these fields are protected by the lock */
michael@0 1926 sdb_p->sqlXactDB = NULL;
michael@0 1927 sdb_p->sqlXactThread = NULL;
michael@0 1928 sdb->private = sdb_p;
michael@0 1929 sdb->version = 0;
michael@0 1930 sdb->sdb_flags = flags | SDB_HAS_META;
michael@0 1931 sdb->app_private = NULL;
michael@0 1932 sdb->sdb_FindObjectsInit = sdb_FindObjectsInit;
michael@0 1933 sdb->sdb_FindObjects = sdb_FindObjects;
michael@0 1934 sdb->sdb_FindObjectsFinal = sdb_FindObjectsFinal;
michael@0 1935 sdb->sdb_GetAttributeValue = sdb_GetAttributeValue;
michael@0 1936 sdb->sdb_SetAttributeValue = sdb_SetAttributeValue;
michael@0 1937 sdb->sdb_CreateObject = sdb_CreateObject;
michael@0 1938 sdb->sdb_DestroyObject = sdb_DestroyObject;
michael@0 1939 sdb->sdb_GetMetaData = sdb_GetMetaData;
michael@0 1940 sdb->sdb_PutMetaData = sdb_PutMetaData;
michael@0 1941 sdb->sdb_Begin = sdb_Begin;
michael@0 1942 sdb->sdb_Commit = sdb_Commit;
michael@0 1943 sdb->sdb_Abort = sdb_Abort;
michael@0 1944 sdb->sdb_Reset = sdb_Reset;
michael@0 1945 sdb->sdb_Close = sdb_Close;
michael@0 1946 sdb->sdb_SetForkState = sdb_SetForkState;
michael@0 1947
michael@0 1948 if (inTransaction) {
michael@0 1949 sqlerr = sqlite3_exec(sqlDB, COMMIT_CMD, NULL, 0, NULL);
michael@0 1950 if (sqlerr != SQLITE_OK) {
michael@0 1951 error = sdb_mapSQLError(sdb_p->type, sqlerr);
michael@0 1952 goto loser;
michael@0 1953 }
michael@0 1954 inTransaction = 0;
michael@0 1955 }
michael@0 1956
michael@0 1957 sdb_p->sqlReadDB = sqlDB;
michael@0 1958
michael@0 1959 *pSdb = sdb;
michael@0 1960 UNLOCK_SQLITE();
michael@0 1961 return CKR_OK;
michael@0 1962
michael@0 1963 loser:
michael@0 1964 /* lots of stuff to do */
michael@0 1965 if (inTransaction) {
michael@0 1966 sqlite3_exec(sqlDB, ROLLBACK_CMD, NULL, 0, NULL);
michael@0 1967 }
michael@0 1968 if (sdb) {
michael@0 1969 free(sdb);
michael@0 1970 }
michael@0 1971 if (sdb_p) {
michael@0 1972 free(sdb_p);
michael@0 1973 }
michael@0 1974 if (sqlDB) {
michael@0 1975 sqlite3_close(sqlDB);
michael@0 1976 }
michael@0 1977 UNLOCK_SQLITE();
michael@0 1978 return error;
michael@0 1979
michael@0 1980 }
michael@0 1981
michael@0 1982
michael@0 1983 /* sdbopen */
michael@0 1984 CK_RV
michael@0 1985 s_open(const char *directory, const char *certPrefix, const char *keyPrefix,
michael@0 1986 int cert_version, int key_version, int flags,
michael@0 1987 SDB **certdb, SDB **keydb, int *newInit)
michael@0 1988 {
michael@0 1989 char *cert = sdb_BuildFileName(directory, certPrefix,
michael@0 1990 "cert", cert_version);
michael@0 1991 char *key = sdb_BuildFileName(directory, keyPrefix,
michael@0 1992 "key", key_version);
michael@0 1993 CK_RV error = CKR_OK;
michael@0 1994 int inUpdate;
michael@0 1995 PRUint32 accessOps;
michael@0 1996
michael@0 1997 if (certdb)
michael@0 1998 *certdb = NULL;
michael@0 1999 if (keydb)
michael@0 2000 *keydb = NULL;
michael@0 2001 *newInit = 0;
michael@0 2002
michael@0 2003 #ifdef SQLITE_UNSAFE_THREADS
michael@0 2004 if (sqlite_lock == NULL) {
michael@0 2005 sqlite_lock = PR_NewLock();
michael@0 2006 if (sqlite_lock == NULL) {
michael@0 2007 error = CKR_HOST_MEMORY;
michael@0 2008 goto loser;
michael@0 2009 }
michael@0 2010 }
michael@0 2011 #endif
michael@0 2012
michael@0 2013 /* how long does it take to test for a non-existant file in our working
michael@0 2014 * directory? Allows us to test if we may be on a network file system */
michael@0 2015 accessOps = 1;
michael@0 2016 {
michael@0 2017 char *env;
michael@0 2018 env = PR_GetEnv("NSS_SDB_USE_CACHE");
michael@0 2019 /* If the environment variable is set to yes or no, sdb_init() will
michael@0 2020 * ignore the value of accessOps, and we can skip the measuring.*/
michael@0 2021 if (!env || ((PORT_Strcasecmp(env, "no") != 0) &&
michael@0 2022 (PORT_Strcasecmp(env, "yes") != 0))){
michael@0 2023 accessOps = sdb_measureAccess(directory);
michael@0 2024 }
michael@0 2025 }
michael@0 2026
michael@0 2027 /*
michael@0 2028 * open the cert data base
michael@0 2029 */
michael@0 2030 if (certdb) {
michael@0 2031 /* initialize Certificate database */
michael@0 2032 error = sdb_init(cert, "nssPublic", SDB_CERT, &inUpdate,
michael@0 2033 newInit, flags, accessOps, certdb);
michael@0 2034 if (error != CKR_OK) {
michael@0 2035 goto loser;
michael@0 2036 }
michael@0 2037 }
michael@0 2038
michael@0 2039 /*
michael@0 2040 * open the key data base:
michael@0 2041 * NOTE:if we want to implement a single database, we open
michael@0 2042 * the same database file as the certificate here.
michael@0 2043 *
michael@0 2044 * cert an key db's have different tables, so they will not
michael@0 2045 * conflict.
michael@0 2046 */
michael@0 2047 if (keydb) {
michael@0 2048 /* initialize the Key database */
michael@0 2049 error = sdb_init(key, "nssPrivate", SDB_KEY, &inUpdate,
michael@0 2050 newInit, flags, accessOps, keydb);
michael@0 2051 if (error != CKR_OK) {
michael@0 2052 goto loser;
michael@0 2053 }
michael@0 2054 }
michael@0 2055
michael@0 2056
michael@0 2057 loser:
michael@0 2058 if (cert) {
michael@0 2059 sqlite3_free(cert);
michael@0 2060 }
michael@0 2061 if (key) {
michael@0 2062 sqlite3_free(key);
michael@0 2063 }
michael@0 2064
michael@0 2065 if (error != CKR_OK) {
michael@0 2066 /* currently redundant, but could be necessary if more code is added
michael@0 2067 * just before loser */
michael@0 2068 if (keydb && *keydb) {
michael@0 2069 sdb_Close(*keydb);
michael@0 2070 }
michael@0 2071 if (certdb && *certdb) {
michael@0 2072 sdb_Close(*certdb);
michael@0 2073 }
michael@0 2074 }
michael@0 2075
michael@0 2076 return error;
michael@0 2077 }
michael@0 2078
michael@0 2079 CK_RV
michael@0 2080 s_shutdown()
michael@0 2081 {
michael@0 2082 #ifdef SQLITE_UNSAFE_THREADS
michael@0 2083 if (sqlite_lock) {
michael@0 2084 PR_DestroyLock(sqlite_lock);
michael@0 2085 sqlite_lock = NULL;
michael@0 2086 }
michael@0 2087 #endif
michael@0 2088 return CKR_OK;
michael@0 2089 }

mercurial