security/nss/lib/softoken/sdb.c

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/security/nss/lib/softoken/sdb.c	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,2089 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +/*
     1.8 + * This file implements PKCS 11 on top of our existing security modules
     1.9 + *
    1.10 + * For more information about PKCS 11 See PKCS 11 Token Inteface Standard.
    1.11 + *   This implementation has two slots:
    1.12 + *	slot 1 is our generic crypto support. It does not require login.
    1.13 + *   It supports Public Key ops, and all they bulk ciphers and hashes. 
    1.14 + *   It can also support Private Key ops for imported Private keys. It does 
    1.15 + *   not have any token storage.
    1.16 + *	slot 2 is our private key support. It requires a login before use. It
    1.17 + *   can store Private Keys and Certs as token objects. Currently only private
    1.18 + *   keys and their associated Certificates are saved on the token.
    1.19 + *
    1.20 + *   In this implementation, session objects are only visible to the session
    1.21 + *   that created or generated them.
    1.22 + */
    1.23 +
    1.24 +#include "sdb.h"
    1.25 +#include "pkcs11t.h"
    1.26 +#include "seccomon.h"
    1.27 +#include <sqlite3.h>
    1.28 +#include "prthread.h"
    1.29 +#include "prio.h"
    1.30 +#include <stdio.h>
    1.31 +#include "secport.h"
    1.32 +#include "prmon.h"
    1.33 +#include "prenv.h"
    1.34 +#include "prprf.h"
    1.35 +#include "prsystem.h" /* for PR_GetDirectorySeparator() */
    1.36 +#include <sys/stat.h>
    1.37 +#if defined(_WIN32)
    1.38 +#include <io.h>
    1.39 +#include <windows.h>
    1.40 +#elif defined(XP_UNIX)
    1.41 +#include <unistd.h>
    1.42 +#endif
    1.43 +
    1.44 +#ifdef SQLITE_UNSAFE_THREADS
    1.45 +#include "prlock.h"
    1.46 +/*
    1.47 + * SQLite can be compiled to be thread safe or not.
    1.48 + * turn on SQLITE_UNSAFE_THREADS if the OS does not support
    1.49 + * a thread safe version of sqlite.
    1.50 + */
    1.51 +static PRLock *sqlite_lock = NULL;
    1.52 +
    1.53 +#define LOCK_SQLITE()  PR_Lock(sqlite_lock);
    1.54 +#define UNLOCK_SQLITE()  PR_Unlock(sqlite_lock);
    1.55 +#else
    1.56 +#define LOCK_SQLITE()  
    1.57 +#define UNLOCK_SQLITE()  
    1.58 +#endif
    1.59 +
    1.60 +typedef enum {
    1.61 +	SDB_CERT = 1,
    1.62 +	SDB_KEY = 2
    1.63 +} sdbDataType;
    1.64 +
    1.65 +/*
    1.66 + * defines controlling how long we wait to acquire locks.
    1.67 + *
    1.68 + * SDB_SQLITE_BUSY_TIMEOUT specifies how long (in milliseconds)
    1.69 + *  sqlite will wait on lock. If that timeout expires, sqlite will
    1.70 + *  return SQLITE_BUSY.
    1.71 + * SDB_BUSY_RETRY_TIME specifies how many seconds the sdb_ code waits
    1.72 + *  after receiving a busy before retrying.
    1.73 + * SDB_MAX_BUSY_RETRIES specifies how many times the sdb_ will retry on
    1.74 + *  a busy condition.
    1.75 + *
    1.76 + * SDB_SQLITE_BUSY_TIMEOUT affects all opertions, both manual 
    1.77 + *   (prepare/step/reset/finalize) and automatic (sqlite3_exec()).
    1.78 + * SDB_BUSY_RETRY_TIME and SDB_MAX_BUSY_RETRIES only affect manual operations
    1.79 + * 
    1.80 + * total wait time for automatic operations: 
    1.81 + *   1 second (SDB_SQLITE_BUSY_TIMEOUT/1000).
    1.82 + * total wait time for manual operations: 
    1.83 + *   (1 second + 5 seconds) * 10 = 60 seconds.
    1.84 + * (SDB_SQLITE_BUSY_TIMEOUT/1000 + SDB_BUSY_RETRY_TIME)*SDB_MAX_BUSY_RETRIES
    1.85 + */
    1.86 +#define SDB_SQLITE_BUSY_TIMEOUT 1000 /* milliseconds */
    1.87 +#define SDB_BUSY_RETRY_TIME        5 /* seconds */
    1.88 +#define SDB_MAX_BUSY_RETRIES      10
    1.89 +
    1.90 +/*
    1.91 + * Note on use of sqlReadDB: Only one thread at a time may have an actual
    1.92 + * operation going on given sqlite3 * database. An operation is defined as
    1.93 + * the time from a sqlite3_prepare() until the sqlite3_finalize().
    1.94 + * Multiple sqlite3 * databases can be open and have simultaneous operations
    1.95 + * going. We use the sqlXactDB for all write operations. This database
    1.96 + * is only opened when we first create a transaction and closed when the
    1.97 + * transaction is complete. sqlReadDB is open when we first opened the database
    1.98 + * and is used for all read operation. It's use is protected by a monitor. This
    1.99 + * is because an operation can span the use of FindObjectsInit() through the
   1.100 + * call to FindObjectsFinal(). In the intermediate time it is possible to call
   1.101 + * other operations like NSC_GetAttributeValue */
   1.102 +
   1.103 +struct SDBPrivateStr {
   1.104 +    char *sqlDBName;		/* invariant, path to this database */
   1.105 +    sqlite3 *sqlXactDB;		/* access protected by dbMon, use protected
   1.106 +                                 * by the transaction. Current transaction db*/
   1.107 +    PRThread *sqlXactThread;	/* protected by dbMon,
   1.108 +			         * current transaction thread */
   1.109 +    sqlite3 *sqlReadDB;		/* use protected by dbMon, value invariant */
   1.110 +    PRIntervalTime lastUpdateTime;  /* last time the cache was updated */
   1.111 +    PRIntervalTime updateInterval;  /* how long the cache can go before it 
   1.112 +                                     * must be updated again */
   1.113 +    sdbDataType type;		/* invariant, database type */
   1.114 +    char *table;	        /* invariant, SQL table which contains the db */
   1.115 +    char *cacheTable;	        /* invariant, SQL table cache of db */
   1.116 +    PRMonitor *dbMon;		/* invariant, monitor to protect 
   1.117 +				 * sqlXact* fields, and use of the sqlReadDB */
   1.118 +};
   1.119 +
   1.120 +typedef struct SDBPrivateStr SDBPrivate;
   1.121 +
   1.122 +/*
   1.123 + * known attributes
   1.124 + */
   1.125 +static const CK_ATTRIBUTE_TYPE known_attributes[] = {
   1.126 +    CKA_CLASS, CKA_TOKEN, CKA_PRIVATE, CKA_LABEL, CKA_APPLICATION,
   1.127 +    CKA_VALUE, CKA_OBJECT_ID, CKA_CERTIFICATE_TYPE, CKA_ISSUER,
   1.128 +    CKA_SERIAL_NUMBER, CKA_AC_ISSUER, CKA_OWNER, CKA_ATTR_TYPES, CKA_TRUSTED,
   1.129 +    CKA_CERTIFICATE_CATEGORY, CKA_JAVA_MIDP_SECURITY_DOMAIN, CKA_URL,
   1.130 +    CKA_HASH_OF_SUBJECT_PUBLIC_KEY, CKA_HASH_OF_ISSUER_PUBLIC_KEY,
   1.131 +    CKA_CHECK_VALUE, CKA_KEY_TYPE, CKA_SUBJECT, CKA_ID, CKA_SENSITIVE,
   1.132 +    CKA_ENCRYPT, CKA_DECRYPT, CKA_WRAP, CKA_UNWRAP, CKA_SIGN, CKA_SIGN_RECOVER,
   1.133 +    CKA_VERIFY, CKA_VERIFY_RECOVER, CKA_DERIVE, CKA_START_DATE, CKA_END_DATE,
   1.134 +    CKA_MODULUS, CKA_MODULUS_BITS, CKA_PUBLIC_EXPONENT, CKA_PRIVATE_EXPONENT,
   1.135 +    CKA_PRIME_1, CKA_PRIME_2, CKA_EXPONENT_1, CKA_EXPONENT_2, CKA_COEFFICIENT,
   1.136 +    CKA_PRIME, CKA_SUBPRIME, CKA_BASE, CKA_PRIME_BITS, 
   1.137 +    CKA_SUB_PRIME_BITS, CKA_VALUE_BITS, CKA_VALUE_LEN, CKA_EXTRACTABLE,
   1.138 +    CKA_LOCAL, CKA_NEVER_EXTRACTABLE, CKA_ALWAYS_SENSITIVE,
   1.139 +    CKA_KEY_GEN_MECHANISM, CKA_MODIFIABLE, CKA_EC_PARAMS,
   1.140 +    CKA_EC_POINT, CKA_SECONDARY_AUTH, CKA_AUTH_PIN_FLAGS,
   1.141 +    CKA_ALWAYS_AUTHENTICATE, CKA_WRAP_WITH_TRUSTED, CKA_WRAP_TEMPLATE,
   1.142 +    CKA_UNWRAP_TEMPLATE, CKA_HW_FEATURE_TYPE, CKA_RESET_ON_INIT,
   1.143 +    CKA_HAS_RESET, CKA_PIXEL_X, CKA_PIXEL_Y, CKA_RESOLUTION, CKA_CHAR_ROWS,
   1.144 +    CKA_CHAR_COLUMNS, CKA_COLOR, CKA_BITS_PER_PIXEL, CKA_CHAR_SETS,
   1.145 +    CKA_ENCODING_METHODS, CKA_MIME_TYPES, CKA_MECHANISM_TYPE,
   1.146 +    CKA_REQUIRED_CMS_ATTRIBUTES, CKA_DEFAULT_CMS_ATTRIBUTES,
   1.147 +    CKA_SUPPORTED_CMS_ATTRIBUTES, CKA_NETSCAPE_URL, CKA_NETSCAPE_EMAIL,
   1.148 +    CKA_NETSCAPE_SMIME_INFO, CKA_NETSCAPE_SMIME_TIMESTAMP,
   1.149 +    CKA_NETSCAPE_PKCS8_SALT, CKA_NETSCAPE_PASSWORD_CHECK, CKA_NETSCAPE_EXPIRES,
   1.150 +    CKA_NETSCAPE_KRL, CKA_NETSCAPE_PQG_COUNTER, CKA_NETSCAPE_PQG_SEED,
   1.151 +    CKA_NETSCAPE_PQG_H, CKA_NETSCAPE_PQG_SEED_BITS, CKA_NETSCAPE_MODULE_SPEC,
   1.152 +    CKA_TRUST_DIGITAL_SIGNATURE, CKA_TRUST_NON_REPUDIATION,
   1.153 +    CKA_TRUST_KEY_ENCIPHERMENT, CKA_TRUST_DATA_ENCIPHERMENT,
   1.154 +    CKA_TRUST_KEY_AGREEMENT, CKA_TRUST_KEY_CERT_SIGN, CKA_TRUST_CRL_SIGN,
   1.155 +    CKA_TRUST_SERVER_AUTH, CKA_TRUST_CLIENT_AUTH, CKA_TRUST_CODE_SIGNING,
   1.156 +    CKA_TRUST_EMAIL_PROTECTION, CKA_TRUST_IPSEC_END_SYSTEM,
   1.157 +    CKA_TRUST_IPSEC_TUNNEL, CKA_TRUST_IPSEC_USER, CKA_TRUST_TIME_STAMPING,
   1.158 +    CKA_TRUST_STEP_UP_APPROVED, CKA_CERT_SHA1_HASH, CKA_CERT_MD5_HASH,
   1.159 +    CKA_NETSCAPE_DB, CKA_NETSCAPE_TRUST, CKA_NSS_OVERRIDE_EXTENSIONS
   1.160 +};
   1.161 +
   1.162 +static int known_attributes_size= sizeof(known_attributes)/
   1.163 +			   sizeof(known_attributes[0]);
   1.164 +
   1.165 +/* Magic for an explicit NULL. NOTE: ideally this should be
   1.166 + * out of band data. Since it's not completely out of band, pick
   1.167 + * a value that has no meaning to any existing PKCS #11 attributes.
   1.168 + * This value is 1) not a valid string (imbedded '\0'). 2) not a U_LONG
   1.169 + * or a normal key (too short). 3) not a bool (too long). 4) not an RSA
   1.170 + * public exponent (too many bits).
   1.171 + */
   1.172 +const unsigned char SQLITE_EXPLICIT_NULL[] = { 0xa5, 0x0, 0x5a };
   1.173 +#define SQLITE_EXPLICIT_NULL_LEN 3
   1.174 +
   1.175 +/*
   1.176 + * determine when we've completed our tasks
   1.177 + */
   1.178 +static int 
   1.179 +sdb_done(int err, int *count)
   1.180 +{
   1.181 +    /* allow as many rows as the database wants to give */
   1.182 +    if (err == SQLITE_ROW) {
   1.183 +	*count = 0;
   1.184 +	return 0;
   1.185 +    }
   1.186 +    if (err != SQLITE_BUSY) {
   1.187 +	return 1;
   1.188 +    }
   1.189 +    /* err == SQLITE_BUSY, Dont' retry forever in this case */
   1.190 +    if (++(*count) >= SDB_MAX_BUSY_RETRIES) {
   1.191 +	return 1;
   1.192 +    }
   1.193 +    return 0;
   1.194 +}
   1.195 +
   1.196 +/*
   1.197 + * find out where sqlite stores the temp tables. We do this by replicating
   1.198 + * the logic from sqlite.
   1.199 + */
   1.200 +#if defined(_WIN32)
   1.201 +static char *
   1.202 +sdb_getFallbackTempDir(void)
   1.203 +{
   1.204 +    /* sqlite uses sqlite3_temp_directory if it is not NULL. We don't have
   1.205 +     * access to sqlite3_temp_directory because it is not exported from
   1.206 +     * sqlite3.dll. Assume sqlite3_win32_set_directory isn't called and
   1.207 +     * sqlite3_temp_directory is NULL.
   1.208 +     */
   1.209 +    char path[MAX_PATH];
   1.210 +    DWORD rv;
   1.211 +    size_t len;
   1.212 +
   1.213 +    rv = GetTempPathA(MAX_PATH, path);
   1.214 +    if (rv > MAX_PATH || rv == 0)
   1.215 +        return NULL;
   1.216 +    len = strlen(path);
   1.217 +    if (len == 0)
   1.218 +        return NULL;
   1.219 +    /* The returned string ends with a backslash, for example, "C:\TEMP\". */
   1.220 +    if (path[len - 1] == '\\')
   1.221 +        path[len - 1] = '\0';
   1.222 +    return PORT_Strdup(path);
   1.223 +}
   1.224 +#elif defined(XP_UNIX)
   1.225 +static char *
   1.226 +sdb_getFallbackTempDir(void)
   1.227 +{
   1.228 +    const char *azDirs[] = {
   1.229 +        NULL,
   1.230 +        NULL,
   1.231 +        "/var/tmp",
   1.232 +        "/usr/tmp",
   1.233 +        "/tmp",
   1.234 +        NULL     /* List terminator */
   1.235 +    };
   1.236 +    unsigned int i;
   1.237 +    struct stat buf;
   1.238 +    const char *zDir = NULL;
   1.239 +
   1.240 +    azDirs[0] = sqlite3_temp_directory;
   1.241 +    azDirs[1] = getenv("TMPDIR");
   1.242 +
   1.243 +    for (i = 0; i < PR_ARRAY_SIZE(azDirs); i++) {
   1.244 +        zDir = azDirs[i];
   1.245 +        if (zDir == NULL) continue;
   1.246 +        if (stat(zDir, &buf)) continue;
   1.247 +        if (!S_ISDIR(buf.st_mode)) continue;
   1.248 +        if (access(zDir, 07)) continue;
   1.249 +        break;
   1.250 +    }
   1.251 +
   1.252 +    if (zDir == NULL)
   1.253 +        return NULL;
   1.254 +    return PORT_Strdup(zDir);
   1.255 +}
   1.256 +#else
   1.257 +#error "sdb_getFallbackTempDir not implemented"
   1.258 +#endif
   1.259 +
   1.260 +#ifndef SQLITE_FCNTL_TEMPFILENAME
   1.261 +/* SQLITE_FCNTL_TEMPFILENAME was added in SQLite 3.7.15 */
   1.262 +#define SQLITE_FCNTL_TEMPFILENAME 16
   1.263 +#endif
   1.264 +
   1.265 +static char *
   1.266 +sdb_getTempDir(sqlite3 *sqlDB)
   1.267 +{
   1.268 +    int sqlrv;
   1.269 +    char *result = NULL;
   1.270 +    char *tempName = NULL;
   1.271 +    char *foundSeparator = NULL;
   1.272 +
   1.273 +    /* Obtain temporary filename in sqlite's directory for temporary tables */
   1.274 +    sqlrv = sqlite3_file_control(sqlDB, 0, SQLITE_FCNTL_TEMPFILENAME,
   1.275 +				 (void*)&tempName);
   1.276 +    if (sqlrv == SQLITE_NOTFOUND) {
   1.277 +	/* SQLITE_FCNTL_TEMPFILENAME not implemented because we are using
   1.278 +	 * an older SQLite. */
   1.279 +	return sdb_getFallbackTempDir();
   1.280 +    }
   1.281 +    if (sqlrv != SQLITE_OK) {
   1.282 +	return NULL;
   1.283 +    }
   1.284 +
   1.285 +    /* We'll extract the temporary directory from tempName */
   1.286 +    foundSeparator = PORT_Strrchr(tempName, PR_GetDirectorySeparator());
   1.287 +    if (foundSeparator) {
   1.288 +	/* We shorten the temp filename string to contain only
   1.289 +	  * the directory name (including the trailing separator).
   1.290 +	  * We know the byte after the foundSeparator position is
   1.291 +	  * safe to use, in the shortest scenario it contains the
   1.292 +	  * end-of-string byte.
   1.293 +	  * By keeping the separator at the found position, it will
   1.294 +	  * even work if tempDir consists of the separator, only.
   1.295 +	  * (In this case the toplevel directory will be used for
   1.296 +	  * access speed testing). */
   1.297 +	++foundSeparator;
   1.298 +	*foundSeparator = 0;
   1.299 +
   1.300 +	/* Now we copy the directory name for our caller */
   1.301 +	result = PORT_Strdup(tempName);
   1.302 +    }
   1.303 +
   1.304 +    sqlite3_free(tempName);
   1.305 +    return result;
   1.306 +}
   1.307 +
   1.308 +/*
   1.309 + * Map SQL_LITE errors to PKCS #11 errors as best we can.
   1.310 + */
   1.311 +static CK_RV
   1.312 +sdb_mapSQLError(sdbDataType type, int sqlerr)
   1.313 +{
   1.314 +    switch (sqlerr) {
   1.315 +    /* good matches */
   1.316 +    case SQLITE_OK:
   1.317 +    case SQLITE_DONE:
   1.318 +	return CKR_OK;
   1.319 +    case SQLITE_NOMEM:
   1.320 +	return CKR_HOST_MEMORY;
   1.321 +    case SQLITE_READONLY:
   1.322 +	return CKR_TOKEN_WRITE_PROTECTED;
   1.323 +    /* close matches */
   1.324 +    case SQLITE_AUTH:
   1.325 +    case SQLITE_PERM:
   1.326 +	/*return CKR_USER_NOT_LOGGED_IN; */
   1.327 +    case SQLITE_CANTOPEN:
   1.328 +    case SQLITE_NOTFOUND:
   1.329 +	/* NSS distiguishes between failure to open the cert and the key db */
   1.330 +	return type == SDB_CERT ? 
   1.331 +		CKR_NETSCAPE_CERTDB_FAILED : CKR_NETSCAPE_KEYDB_FAILED;
   1.332 +    case SQLITE_IOERR:
   1.333 +	return CKR_DEVICE_ERROR;
   1.334 +    default:
   1.335 +	break;
   1.336 +    }
   1.337 +    return CKR_GENERAL_ERROR;
   1.338 +}
   1.339 +
   1.340 +
   1.341 +/*
   1.342 + * build up database name from a directory, prefix, name, version and flags.
   1.343 + */
   1.344 +static char *sdb_BuildFileName(const char * directory, 
   1.345 +			const char *prefix, const char *type, 
   1.346 +			int version)
   1.347 +{
   1.348 +    char *dbname = NULL;
   1.349 +    /* build the full dbname */
   1.350 +    dbname = sqlite3_mprintf("%s%c%s%s%d.db", directory,
   1.351 +			     (int)(unsigned char)PR_GetDirectorySeparator(),
   1.352 +			     prefix, type, version);
   1.353 +    return dbname;
   1.354 +}
   1.355 +
   1.356 +
   1.357 +/*
   1.358 + * find out how expensive the access system call is for non-existant files
   1.359 + * in the given directory.  Return the number of operations done in 33 ms.
   1.360 + */
   1.361 +static PRUint32
   1.362 +sdb_measureAccess(const char *directory)
   1.363 +{
   1.364 +    PRUint32 i;
   1.365 +    PRIntervalTime time;
   1.366 +    PRIntervalTime delta;
   1.367 +    PRIntervalTime duration = PR_MillisecondsToInterval(33);
   1.368 +    const char *doesntExistName = "_dOeSnotExist_.db";
   1.369 +    char *temp, *tempStartOfFilename;
   1.370 +    size_t maxTempLen, maxFileNameLen, directoryLength;
   1.371 +
   1.372 +    /* no directory, just return one */
   1.373 +    if (directory == NULL) {
   1.374 +	return 1;
   1.375 +    }
   1.376 +
   1.377 +    /* our calculation assumes time is a 4 bytes == 32 bit integer */
   1.378 +    PORT_Assert(sizeof(time) == 4);
   1.379 +
   1.380 +    directoryLength = strlen(directory);
   1.381 +
   1.382 +    maxTempLen = directoryLength + strlen(doesntExistName)
   1.383 +		 + 1 /* potential additional separator char */
   1.384 +		 + 11 /* max chars for 32 bit int plus potential sign */
   1.385 +		 + 1; /* zero terminator */
   1.386 +
   1.387 +    temp = PORT_Alloc(maxTempLen);
   1.388 +    if (!temp) {
   1.389 +        return 1;
   1.390 +    }
   1.391 +
   1.392 +    /* We'll copy directory into temp just once, then ensure it ends
   1.393 +     * with the directory separator, then remember the position after
   1.394 +     * the separator, and calculate the number of remaining bytes. */
   1.395 +
   1.396 +    strcpy(temp, directory);
   1.397 +    if (directory[directoryLength - 1] != PR_GetDirectorySeparator()) {
   1.398 +	temp[directoryLength++] = PR_GetDirectorySeparator();
   1.399 +    }
   1.400 +    tempStartOfFilename = temp + directoryLength;
   1.401 +    maxFileNameLen = maxTempLen - directoryLength;
   1.402 +
   1.403 +    /* measure number of Access operations that can be done in 33 milliseconds
   1.404 +     * (1/30'th of a second), or 10000 operations, which ever comes first.
   1.405 +     */
   1.406 +    time =  PR_IntervalNow();
   1.407 +    for (i=0; i < 10000u; i++) { 
   1.408 +	PRIntervalTime next;
   1.409 +
   1.410 +	/* We'll use the variable part first in the filename string, just in
   1.411 +	 * case it's longer than assumed, so if anything gets cut off, it
   1.412 +	 * will be cut off from the constant part.
   1.413 +	 * This code assumes the directory name at the beginning of
   1.414 +	 * temp remains unchanged during our loop. */
   1.415 +        PR_snprintf(tempStartOfFilename, maxFileNameLen,
   1.416 +		    ".%lu%s", (PRUint32)(time+i), doesntExistName);
   1.417 +	PR_Access(temp,PR_ACCESS_EXISTS);
   1.418 +	next = PR_IntervalNow();
   1.419 +	delta = next - time;
   1.420 +	if (delta >= duration)
   1.421 +	    break;
   1.422 +    }
   1.423 +
   1.424 +    PORT_Free(temp);
   1.425 +
   1.426 +    /* always return 1 or greater */
   1.427 +    return i ? i : 1u;
   1.428 +}
   1.429 +
   1.430 +/*
   1.431 + * some file sytems are very slow to run sqlite3 on, particularly if the
   1.432 + * access count is pretty high. On these filesystems is faster to create
   1.433 + * a temporary database on the local filesystem and access that. This
   1.434 + * code uses a temporary table to create that cache. Temp tables are
   1.435 + * automatically cleared when the database handle it was created on
   1.436 + * Is freed.
   1.437 + */
   1.438 +static const char DROP_CACHE_CMD[] = "DROP TABLE %s";
   1.439 +static const char CREATE_CACHE_CMD[] =
   1.440 +  "CREATE TEMPORARY TABLE %s AS SELECT * FROM %s";
   1.441 +static const char CREATE_ISSUER_INDEX_CMD[] = 
   1.442 +  "CREATE INDEX issuer ON %s (a81)";
   1.443 +static const char CREATE_SUBJECT_INDEX_CMD[] = 
   1.444 +  "CREATE INDEX subject ON %s (a101)";
   1.445 +static const char CREATE_LABEL_INDEX_CMD[] = "CREATE INDEX label ON %s (a3)";
   1.446 +static const char CREATE_ID_INDEX_CMD[] = "CREATE INDEX ckaid ON %s (a102)";
   1.447 +
   1.448 +static CK_RV
   1.449 +sdb_buildCache(sqlite3 *sqlDB, sdbDataType type, 
   1.450 +		const char *cacheTable, const char *table)
   1.451 +{
   1.452 +    char *newStr;
   1.453 +    int sqlerr = SQLITE_OK;
   1.454 +
   1.455 +    newStr = sqlite3_mprintf(CREATE_CACHE_CMD, cacheTable, table);
   1.456 +    if (newStr == NULL) {
   1.457 +	return CKR_HOST_MEMORY;
   1.458 +    }
   1.459 +    sqlerr = sqlite3_exec(sqlDB, newStr, NULL, 0, NULL);
   1.460 +    sqlite3_free(newStr);
   1.461 +    if (sqlerr != SQLITE_OK) {
   1.462 +	return sdb_mapSQLError(type, sqlerr); 
   1.463 +    }
   1.464 +    /* failure to create the indexes is not an issue */
   1.465 +    newStr = sqlite3_mprintf(CREATE_ISSUER_INDEX_CMD, cacheTable);
   1.466 +    if (newStr == NULL) {
   1.467 +	return CKR_OK;
   1.468 +    }
   1.469 +    sqlerr = sqlite3_exec(sqlDB, newStr, NULL, 0, NULL);
   1.470 +    sqlite3_free(newStr);
   1.471 +    newStr = sqlite3_mprintf(CREATE_SUBJECT_INDEX_CMD, cacheTable);
   1.472 +    if (newStr == NULL) {
   1.473 +	return CKR_OK;
   1.474 +    }
   1.475 +    sqlerr = sqlite3_exec(sqlDB, newStr, NULL, 0, NULL);
   1.476 +    sqlite3_free(newStr);
   1.477 +    newStr = sqlite3_mprintf(CREATE_LABEL_INDEX_CMD, cacheTable);
   1.478 +    if (newStr == NULL) {
   1.479 +	return CKR_OK;
   1.480 +    }
   1.481 +    sqlerr = sqlite3_exec(sqlDB, newStr, NULL, 0, NULL);
   1.482 +    sqlite3_free(newStr);
   1.483 +    newStr = sqlite3_mprintf(CREATE_ID_INDEX_CMD, cacheTable);
   1.484 +    if (newStr == NULL) {
   1.485 +	return CKR_OK;
   1.486 +    }
   1.487 +    sqlerr = sqlite3_exec(sqlDB, newStr, NULL, 0, NULL);
   1.488 +    sqlite3_free(newStr);
   1.489 +    return CKR_OK;
   1.490 +}
   1.491 +
   1.492 +/*
   1.493 + * update the cache and the data records describing it.
   1.494 + *  The cache is updated by dropping the temp database and recreating it.
   1.495 + */
   1.496 +static CK_RV
   1.497 +sdb_updateCache(SDBPrivate *sdb_p)
   1.498 +{
   1.499 +    int sqlerr = SQLITE_OK;
   1.500 +    CK_RV error = CKR_OK;
   1.501 +    char *newStr;
   1.502 +
   1.503 +    /* drop the old table */
   1.504 +    newStr = sqlite3_mprintf(DROP_CACHE_CMD, sdb_p->cacheTable);
   1.505 +    if (newStr == NULL) {
   1.506 +	return CKR_HOST_MEMORY;
   1.507 +    }
   1.508 +    sqlerr = sqlite3_exec(sdb_p->sqlReadDB, newStr, NULL, 0, NULL);
   1.509 +    sqlite3_free(newStr);
   1.510 +    if ((sqlerr != SQLITE_OK) && (sqlerr != SQLITE_ERROR )) {
   1.511 +        /* something went wrong with the drop, don't try to refresh...
   1.512 +         * NOTE: SQLITE_ERROR is returned if the table doesn't exist. In
   1.513 +         * that case, we just continue on and try to reload it */
   1.514 +	return sdb_mapSQLError(sdb_p->type, sqlerr); 
   1.515 +    }
   1.516 +	
   1.517 +
   1.518 +    /* set up the new table */
   1.519 +    error = sdb_buildCache(sdb_p->sqlReadDB,sdb_p->type,
   1.520 +				sdb_p->cacheTable,sdb_p->table );
   1.521 +    if (error == CKR_OK) {
   1.522 +	/* we have a new cache! */
   1.523 +	sdb_p->lastUpdateTime = PR_IntervalNow();
   1.524 +    }
   1.525 +    return error;
   1.526 +}
   1.527 +
   1.528 +/*
   1.529 + *  The sharing of sqlite3 handles across threads is tricky. Older versions
   1.530 + *  couldn't at all, but newer ones can under strict conditions. Basically
   1.531 + *  no 2 threads can use the same handle while another thread has an open
   1.532 + *  stmt running. Once the sqlite3_stmt is finalized, another thread can then
   1.533 + *  use the database handle.
   1.534 + *
   1.535 + *  We use monitors to protect against trying to use a database before
   1.536 + *  it's sqlite3_stmt is finalized. This is preferable to the opening and
   1.537 + *  closing the database each operation because there is significant overhead
   1.538 + *  in the open and close. Also continually opening and closing the database
   1.539 + *  defeats the cache code as the cache table is lost on close (thus
   1.540 + *  requiring us to have to reinitialize the cache every operation).
   1.541 + * 
   1.542 + *  An execption to the shared handle is transations. All writes happen
   1.543 + *  through a transaction. When we are in  a transaction, we must use the 
   1.544 + *  same database pointer for that entire transation. In this case we save 
   1.545 + *  the transaction database and use it for all accesses on the transaction 
   1.546 + *  thread. Other threads use the common database.  
   1.547 + *
   1.548 + *  There can only be once active transaction on the database at a time.
   1.549 + *
   1.550 + *  sdb_openDBLocal() provides us with a valid database handle for whatever
   1.551 + *  state we are in (reading or in a transaction), and acquires any locks
   1.552 + *  appropriate to that state. It also decides when it's time to refresh
   1.553 + *  the cache before we start an operation. Any database handle returned
   1.554 + *  just eventually be closed with sdb_closeDBLocal().
   1.555 + *
   1.556 + *  The table returned either points to the database's physical table, or
   1.557 + *  to the cached shadow. Tranactions always return the physical table
   1.558 + *  and read operations return either the physical table or the cache
   1.559 + *  depending on whether or not the cache exists.
   1.560 + */
   1.561 +static CK_RV 
   1.562 +sdb_openDBLocal(SDBPrivate *sdb_p, sqlite3 **sqlDB, const char **table)
   1.563 +{
   1.564 +    *sqlDB = NULL;
   1.565 +
   1.566 +    PR_EnterMonitor(sdb_p->dbMon);
   1.567 +
   1.568 +    if (table) {
   1.569 +	*table = sdb_p->table;
   1.570 +    }
   1.571 +
   1.572 +    /* We're in a transaction, use the transaction DB */
   1.573 +    if ((sdb_p->sqlXactDB) && (sdb_p->sqlXactThread == PR_GetCurrentThread())) {
   1.574 +	*sqlDB =sdb_p->sqlXactDB;
   1.575 +	/* only one thread can get here, safe to unlock */
   1.576 +        PR_ExitMonitor(sdb_p->dbMon);
   1.577 +	return CKR_OK;
   1.578 +    }
   1.579 +
   1.580 +    /*
   1.581 +     * if we are just reading from the table, we may have the table
   1.582 +     * cached in a temporary table (especially if it's on a shared FS).
   1.583 +     * In that case we want to see updates to the table, the the granularity
   1.584 +     * is on order of human scale, not computer scale.
   1.585 +     */
   1.586 +    if (table && sdb_p->cacheTable) {
   1.587 +	PRIntervalTime now = PR_IntervalNow();
   1.588 +	if ((now - sdb_p->lastUpdateTime) > sdb_p->updateInterval) {
   1.589 +	       sdb_updateCache(sdb_p);
   1.590 +        }
   1.591 +	*table = sdb_p->cacheTable;
   1.592 +    }
   1.593 +
   1.594 +    *sqlDB = sdb_p->sqlReadDB;
   1.595 +
   1.596 +    /* leave holding the lock. only one thread can actually use a given
   1.597 +     * database connection at once */
   1.598 +	
   1.599 +    return CKR_OK;
   1.600 +}
   1.601 +
   1.602 +/* closing the local database currenly means unlocking the monitor */
   1.603 +static CK_RV 
   1.604 +sdb_closeDBLocal(SDBPrivate *sdb_p, sqlite3 *sqlDB) 
   1.605 +{
   1.606 +   if (sdb_p->sqlXactDB != sqlDB) {
   1.607 +	/* if we weren't in a transaction, we got a lock */
   1.608 +        PR_ExitMonitor(sdb_p->dbMon);
   1.609 +   }
   1.610 +   return CKR_OK;
   1.611 +}
   1.612 +
   1.613 +
   1.614 +/*
   1.615 + * wrapper to sqlite3_open which also sets the busy_timeout
   1.616 + */
   1.617 +static int
   1.618 +sdb_openDB(const char *name, sqlite3 **sqlDB, int flags)
   1.619 +{
   1.620 +    int sqlerr;
   1.621 +    /*
   1.622 +     * in sqlite3 3.5.0, there is a new open call that allows us
   1.623 +     * to specify read only. Most new OS's are still on 3.3.x (including
   1.624 +     * NSS's internal version and the version shipped with Firefox).
   1.625 +     */
   1.626 +    *sqlDB = NULL;
   1.627 +    sqlerr = sqlite3_open(name, sqlDB);
   1.628 +    if (sqlerr != SQLITE_OK) {
   1.629 +	return sqlerr;
   1.630 +    }
   1.631 +
   1.632 +    sqlerr = sqlite3_busy_timeout(*sqlDB, SDB_SQLITE_BUSY_TIMEOUT);
   1.633 +    if (sqlerr != SQLITE_OK) {
   1.634 +	sqlite3_close(*sqlDB);
   1.635 +	*sqlDB = NULL;
   1.636 +	return sqlerr;
   1.637 +    }
   1.638 +    return SQLITE_OK;
   1.639 +}
   1.640 +
   1.641 +/* Sigh, if we created a new table since we opened the database,
   1.642 + * the database handle will not see the new table, we need to close this
   1.643 + * database and reopen it. Caller must be in a transaction or holding
   1.644 + * the dbMon. sqlDB is changed on success. */
   1.645 +static int 
   1.646 +sdb_reopenDBLocal(SDBPrivate *sdb_p, sqlite3 **sqlDB) {
   1.647 +    sqlite3 *newDB;
   1.648 +    int sqlerr;
   1.649 +
   1.650 +    /* open a new database */
   1.651 +    sqlerr = sdb_openDB(sdb_p->sqlDBName, &newDB, SDB_RDONLY);
   1.652 +    if (sqlerr != SQLITE_OK) {
   1.653 +	return sqlerr;
   1.654 +    }
   1.655 +
   1.656 +    /* if we are in a transaction, we may not be holding the monitor.
   1.657 +     * grab it before we update the transaction database. This is
   1.658 +     * safe since are using monitors. */
   1.659 +    PR_EnterMonitor(sdb_p->dbMon);
   1.660 +    /* update our view of the database */
   1.661 +    if (sdb_p->sqlReadDB == *sqlDB) {
   1.662 +	sdb_p->sqlReadDB = newDB;
   1.663 +    } else if (sdb_p->sqlXactDB == *sqlDB) {
   1.664 +	sdb_p->sqlXactDB = newDB;
   1.665 +    }
   1.666 +    PR_ExitMonitor(sdb_p->dbMon);
   1.667 +
   1.668 +    /* close the old one */
   1.669 +    sqlite3_close(*sqlDB);
   1.670 +
   1.671 +    *sqlDB = newDB;
   1.672 +    return SQLITE_OK;
   1.673 +}
   1.674 +
   1.675 +struct SDBFindStr {
   1.676 +    sqlite3 *sqlDB;
   1.677 +    sqlite3_stmt *findstmt;
   1.678 +};
   1.679 +
   1.680 +
   1.681 +static const char FIND_OBJECTS_CMD[] =  "SELECT ALL * FROM %s WHERE %s;";
   1.682 +static const char FIND_OBJECTS_ALL_CMD[] = "SELECT ALL * FROM %s;";
   1.683 +CK_RV
   1.684 +sdb_FindObjectsInit(SDB *sdb, const CK_ATTRIBUTE *template, CK_ULONG count, 
   1.685 +				SDBFind **find)
   1.686 +{
   1.687 +    SDBPrivate *sdb_p = sdb->private;
   1.688 +    sqlite3  *sqlDB = NULL;
   1.689 +    const char *table;
   1.690 +    char *newStr, *findStr = NULL;
   1.691 +    sqlite3_stmt *findstmt = NULL;
   1.692 +    char *join="";
   1.693 +    int sqlerr = SQLITE_OK;
   1.694 +    CK_RV error = CKR_OK;
   1.695 +    int i;
   1.696 +
   1.697 +    LOCK_SQLITE()
   1.698 +    *find = NULL;
   1.699 +    error = sdb_openDBLocal(sdb_p, &sqlDB, &table);
   1.700 +    if (error != CKR_OK) {
   1.701 +	goto loser;
   1.702 +    }
   1.703 +
   1.704 +    findStr = sqlite3_mprintf("");
   1.705 +    for (i=0; findStr && i < count; i++) {
   1.706 +	newStr = sqlite3_mprintf("%s%sa%x=$DATA%d", findStr, join,
   1.707 +				template[i].type, i);
   1.708 +        join=" AND ";
   1.709 +	sqlite3_free(findStr);
   1.710 +	findStr = newStr;
   1.711 +    }
   1.712 +
   1.713 +    if (findStr == NULL) {
   1.714 +	error = CKR_HOST_MEMORY;
   1.715 +	goto loser;
   1.716 +    }
   1.717 +
   1.718 +    if (count == 0) {
   1.719 +	newStr = sqlite3_mprintf(FIND_OBJECTS_ALL_CMD, table);
   1.720 +    } else {
   1.721 +	newStr = sqlite3_mprintf(FIND_OBJECTS_CMD, table, findStr);
   1.722 +    }
   1.723 +    sqlite3_free(findStr);
   1.724 +    if (newStr == NULL) {
   1.725 +	error = CKR_HOST_MEMORY;
   1.726 +	goto loser;
   1.727 +    }
   1.728 +    sqlerr = sqlite3_prepare_v2(sqlDB, newStr, -1, &findstmt, NULL);
   1.729 +    sqlite3_free(newStr);
   1.730 +    for (i=0; sqlerr == SQLITE_OK && i < count; i++) {
   1.731 +	const void *blobData = template[i].pValue;
   1.732 +	unsigned int blobSize = template[i].ulValueLen;
   1.733 +	if (blobSize == 0) {
   1.734 +	    blobSize = SQLITE_EXPLICIT_NULL_LEN;
   1.735 +	    blobData = SQLITE_EXPLICIT_NULL;
   1.736 +	}
   1.737 +	sqlerr = sqlite3_bind_blob(findstmt, i+1, blobData, blobSize,
   1.738 +				   SQLITE_TRANSIENT);
   1.739 +    }
   1.740 +    if (sqlerr == SQLITE_OK) {
   1.741 +	*find = PORT_New(SDBFind);
   1.742 +	if (*find == NULL) {
   1.743 +	    error = CKR_HOST_MEMORY;
   1.744 +	    goto loser;
   1.745 +	}
   1.746 +	(*find)->findstmt = findstmt;
   1.747 +	(*find)->sqlDB = sqlDB;
   1.748 +	UNLOCK_SQLITE()  
   1.749 +	return CKR_OK;
   1.750 +    } 
   1.751 +    error = sdb_mapSQLError(sdb_p->type, sqlerr);
   1.752 +
   1.753 +loser: 
   1.754 +    if (findstmt) {
   1.755 +	sqlite3_reset(findstmt);
   1.756 +	sqlite3_finalize(findstmt);
   1.757 +    }
   1.758 +    if (sqlDB) {
   1.759 +	sdb_closeDBLocal(sdb_p, sqlDB) ;
   1.760 +    }
   1.761 +    UNLOCK_SQLITE()  
   1.762 +    return error;
   1.763 +}
   1.764 +
   1.765 +
   1.766 +CK_RV
   1.767 +sdb_FindObjects(SDB *sdb, SDBFind *sdbFind, CK_OBJECT_HANDLE *object, 
   1.768 +		CK_ULONG arraySize, CK_ULONG *count)
   1.769 +{
   1.770 +    SDBPrivate *sdb_p = sdb->private;
   1.771 +    sqlite3_stmt *stmt = sdbFind->findstmt;
   1.772 +    int sqlerr = SQLITE_OK;
   1.773 +    int retry = 0;
   1.774 +
   1.775 +    *count = 0;
   1.776 +
   1.777 +    if (arraySize == 0) {
   1.778 +	return CKR_OK;
   1.779 +    }
   1.780 +    LOCK_SQLITE()  
   1.781 +
   1.782 +    do {
   1.783 +	sqlerr = sqlite3_step(stmt);
   1.784 +	if (sqlerr == SQLITE_BUSY) {
   1.785 +	    PR_Sleep(SDB_BUSY_RETRY_TIME);
   1.786 +	}
   1.787 +	if (sqlerr == SQLITE_ROW) {
   1.788 +	    /* only care about the id */
   1.789 +	    *object++= sqlite3_column_int(stmt, 0);
   1.790 +	    arraySize--;
   1.791 +	    (*count)++;
   1.792 +	}
   1.793 +    } while (!sdb_done(sqlerr,&retry) && (arraySize > 0));
   1.794 +
   1.795 +    /* we only have some of the objects, there is probably more,
   1.796 +     * set the sqlerr to an OK value so we return CKR_OK */
   1.797 +    if (sqlerr == SQLITE_ROW && arraySize == 0) {
   1.798 +	sqlerr = SQLITE_DONE;
   1.799 +    }
   1.800 +    UNLOCK_SQLITE()  
   1.801 +
   1.802 +    return sdb_mapSQLError(sdb_p->type, sqlerr);
   1.803 +}
   1.804 +
   1.805 +CK_RV
   1.806 +sdb_FindObjectsFinal(SDB *sdb, SDBFind *sdbFind)
   1.807 +{
   1.808 +    SDBPrivate *sdb_p = sdb->private;
   1.809 +    sqlite3_stmt *stmt = sdbFind->findstmt;
   1.810 +    sqlite3 *sqlDB = sdbFind->sqlDB;
   1.811 +    int sqlerr = SQLITE_OK;
   1.812 +
   1.813 +    LOCK_SQLITE()  
   1.814 +    if (stmt) {
   1.815 +	sqlite3_reset(stmt);
   1.816 +	sqlerr = sqlite3_finalize(stmt);
   1.817 +    }
   1.818 +    if (sqlDB) {
   1.819 +	sdb_closeDBLocal(sdb_p, sqlDB) ;
   1.820 +    }
   1.821 +    PORT_Free(sdbFind);
   1.822 +
   1.823 +    UNLOCK_SQLITE()  
   1.824 +    return sdb_mapSQLError(sdb_p->type, sqlerr);
   1.825 +}
   1.826 +
   1.827 +static const char GET_ATTRIBUTE_CMD[] = "SELECT ALL %s FROM %s WHERE id=$ID;";
   1.828 +CK_RV
   1.829 +sdb_GetAttributeValueNoLock(SDB *sdb, CK_OBJECT_HANDLE object_id, 
   1.830 +				CK_ATTRIBUTE *template, CK_ULONG count)
   1.831 +{
   1.832 +    SDBPrivate *sdb_p = sdb->private;
   1.833 +    sqlite3  *sqlDB = NULL;
   1.834 +    sqlite3_stmt *stmt = NULL;
   1.835 +    char *getStr = NULL;
   1.836 +    char *newStr = NULL;
   1.837 +    const char *table = NULL;
   1.838 +    int sqlerr = SQLITE_OK;
   1.839 +    CK_RV error = CKR_OK;
   1.840 +    int found = 0;
   1.841 +    int retry = 0;
   1.842 +    int i;
   1.843 +
   1.844 +
   1.845 +    /* open a new db if necessary */
   1.846 +    error = sdb_openDBLocal(sdb_p, &sqlDB, &table);
   1.847 +    if (error != CKR_OK) {
   1.848 +	goto loser;
   1.849 +    }
   1.850 +
   1.851 +    for (i=0; i < count; i++) {
   1.852 +	getStr = sqlite3_mprintf("a%x", template[i].type);
   1.853 +
   1.854 +	if (getStr == NULL) {
   1.855 +	    error = CKR_HOST_MEMORY;
   1.856 +	    goto loser;
   1.857 +	}
   1.858 +
   1.859 +	newStr = sqlite3_mprintf(GET_ATTRIBUTE_CMD, getStr, table);
   1.860 +	sqlite3_free(getStr);
   1.861 +	getStr = NULL;
   1.862 +	if (newStr == NULL) {
   1.863 +	    error = CKR_HOST_MEMORY;
   1.864 +	    goto loser;
   1.865 +	}
   1.866 +
   1.867 +	sqlerr = sqlite3_prepare_v2(sqlDB, newStr, -1, &stmt, NULL);
   1.868 +	sqlite3_free(newStr);
   1.869 +	newStr = NULL;
   1.870 +	if (sqlerr == SQLITE_ERROR) {
   1.871 +	    template[i].ulValueLen = -1;
   1.872 +	    error = CKR_ATTRIBUTE_TYPE_INVALID;
   1.873 +	    continue;
   1.874 +	} else if (sqlerr != SQLITE_OK) { goto loser; }
   1.875 +
   1.876 +	sqlerr = sqlite3_bind_int(stmt, 1, object_id);
   1.877 +	if (sqlerr != SQLITE_OK) { goto loser; }
   1.878 +
   1.879 +	do {
   1.880 +	    sqlerr = sqlite3_step(stmt);
   1.881 +	    if (sqlerr == SQLITE_BUSY) {
   1.882 +		PR_Sleep(SDB_BUSY_RETRY_TIME);
   1.883 +	    }
   1.884 +	    if (sqlerr == SQLITE_ROW) {
   1.885 +	    	int blobSize;
   1.886 +	    	const char *blobData;
   1.887 +
   1.888 +	    	blobSize = sqlite3_column_bytes(stmt, 0);
   1.889 +		blobData = sqlite3_column_blob(stmt, 0);
   1.890 +		if (blobData == NULL) {
   1.891 +		    template[i].ulValueLen = -1;
   1.892 +		    error = CKR_ATTRIBUTE_TYPE_INVALID; 
   1.893 +		    break;
   1.894 +		}
   1.895 +		/* If the blob equals our explicit NULL value, then the 
   1.896 +		 * attribute is a NULL. */
   1.897 +		if ((blobSize == SQLITE_EXPLICIT_NULL_LEN) &&
   1.898 +		   	(PORT_Memcmp(blobData, SQLITE_EXPLICIT_NULL, 
   1.899 +			      SQLITE_EXPLICIT_NULL_LEN) == 0)) {
   1.900 +		    blobSize = 0;
   1.901 +		}
   1.902 +		if (template[i].pValue) {
   1.903 +		    if (template[i].ulValueLen < blobSize) {
   1.904 +			template[i].ulValueLen = -1;
   1.905 +		    	error = CKR_BUFFER_TOO_SMALL;
   1.906 +			break;
   1.907 +		    }
   1.908 +	    	    PORT_Memcpy(template[i].pValue, blobData, blobSize);
   1.909 +		}
   1.910 +		template[i].ulValueLen = blobSize;
   1.911 +		found = 1;
   1.912 +	    }
   1.913 +	} while (!sdb_done(sqlerr,&retry));
   1.914 +	sqlite3_reset(stmt);
   1.915 +	sqlite3_finalize(stmt);
   1.916 +	stmt = NULL;
   1.917 +    }
   1.918 +
   1.919 +loser:
   1.920 +    /* fix up the error if necessary */
   1.921 +    if (error == CKR_OK) {
   1.922 +	error = sdb_mapSQLError(sdb_p->type, sqlerr);
   1.923 +	if (!found && error == CKR_OK) {
   1.924 +	    error = CKR_OBJECT_HANDLE_INVALID;
   1.925 +	}
   1.926 +    }
   1.927 +
   1.928 +    if (stmt) {
   1.929 +	sqlite3_reset(stmt);
   1.930 +	sqlite3_finalize(stmt);
   1.931 +    }
   1.932 +
   1.933 +    /* if we had to open a new database, free it now */
   1.934 +    if (sqlDB) {
   1.935 +	sdb_closeDBLocal(sdb_p, sqlDB) ;
   1.936 +    }
   1.937 +    return error;
   1.938 +}
   1.939 +
   1.940 +CK_RV
   1.941 +sdb_GetAttributeValue(SDB *sdb, CK_OBJECT_HANDLE object_id, 
   1.942 +				CK_ATTRIBUTE *template, CK_ULONG count)
   1.943 +{
   1.944 +    CK_RV crv;
   1.945 +
   1.946 +    if (count == 0) {
   1.947 +	return CKR_OK;
   1.948 +    }
   1.949 +
   1.950 +    LOCK_SQLITE()  
   1.951 +    crv = sdb_GetAttributeValueNoLock(sdb, object_id, template, count);
   1.952 +    UNLOCK_SQLITE()  
   1.953 +    return crv;
   1.954 +}
   1.955 +   
   1.956 +static const char SET_ATTRIBUTE_CMD[] = "UPDATE %s SET %s WHERE id=$ID;";
   1.957 +CK_RV
   1.958 +sdb_SetAttributeValue(SDB *sdb, CK_OBJECT_HANDLE object_id, 
   1.959 +			const CK_ATTRIBUTE *template, CK_ULONG count)
   1.960 +{
   1.961 +    SDBPrivate *sdb_p = sdb->private;
   1.962 +    sqlite3  *sqlDB = NULL;
   1.963 +    sqlite3_stmt *stmt = NULL;
   1.964 +    char *setStr = NULL;
   1.965 +    char *newStr = NULL;
   1.966 +    int sqlerr = SQLITE_OK;
   1.967 +    int retry = 0;
   1.968 +    CK_RV error = CKR_OK;
   1.969 +    int i;
   1.970 +
   1.971 +    if ((sdb->sdb_flags & SDB_RDONLY) != 0) {
   1.972 +	return CKR_TOKEN_WRITE_PROTECTED;
   1.973 +    }
   1.974 +
   1.975 +    if (count == 0) {
   1.976 +	return CKR_OK;
   1.977 +    }
   1.978 +
   1.979 +    LOCK_SQLITE()  
   1.980 +    setStr = sqlite3_mprintf("");
   1.981 +    for (i=0; setStr && i < count; i++) {
   1.982 +	if (i==0) {
   1.983 +	    sqlite3_free(setStr);
   1.984 +   	    setStr = sqlite3_mprintf("a%x=$VALUE%d", 
   1.985 +				template[i].type, i);
   1.986 +	    continue;
   1.987 +	}
   1.988 +	newStr = sqlite3_mprintf("%s,a%x=$VALUE%d", setStr, 
   1.989 +				template[i].type, i);
   1.990 +	sqlite3_free(setStr);
   1.991 +	setStr = newStr;
   1.992 +    }
   1.993 +    newStr = NULL;
   1.994 +
   1.995 +    if (setStr == NULL) {
   1.996 +	return CKR_HOST_MEMORY;
   1.997 +    }
   1.998 +    newStr =  sqlite3_mprintf(SET_ATTRIBUTE_CMD, sdb_p->table, setStr);
   1.999 +    sqlite3_free(setStr);
  1.1000 +    if (newStr == NULL) {
  1.1001 +	UNLOCK_SQLITE()  
  1.1002 +	return CKR_HOST_MEMORY;
  1.1003 +    }
  1.1004 +    error = sdb_openDBLocal(sdb_p, &sqlDB, NULL);
  1.1005 +    if (error != CKR_OK) {
  1.1006 +	goto loser;
  1.1007 +    }
  1.1008 +    sqlerr = sqlite3_prepare_v2(sqlDB, newStr, -1, &stmt, NULL);
  1.1009 +    if (sqlerr != SQLITE_OK) goto loser;
  1.1010 +    for (i=0; i < count; i++) {
  1.1011 +	if (template[i].ulValueLen != 0) {
  1.1012 +	    sqlerr = sqlite3_bind_blob(stmt, i+1, template[i].pValue, 
  1.1013 +				template[i].ulValueLen, SQLITE_STATIC);
  1.1014 +	} else {
  1.1015 +	    sqlerr = sqlite3_bind_blob(stmt, i+2, SQLITE_EXPLICIT_NULL, 
  1.1016 +			SQLITE_EXPLICIT_NULL_LEN, SQLITE_STATIC);
  1.1017 +	}
  1.1018 +        if (sqlerr != SQLITE_OK) goto loser;
  1.1019 +    }
  1.1020 +    sqlerr = sqlite3_bind_int(stmt, i+1, object_id);
  1.1021 +    if (sqlerr != SQLITE_OK) goto loser;
  1.1022 +
  1.1023 +    do {
  1.1024 +	sqlerr = sqlite3_step(stmt);
  1.1025 +	if (sqlerr == SQLITE_BUSY) {
  1.1026 +	    PR_Sleep(SDB_BUSY_RETRY_TIME);
  1.1027 +	}
  1.1028 +    } while (!sdb_done(sqlerr,&retry));
  1.1029 +
  1.1030 +loser:
  1.1031 +    if (newStr) {
  1.1032 +	sqlite3_free(newStr);
  1.1033 +    }
  1.1034 +    if (error == CKR_OK) {
  1.1035 +	error = sdb_mapSQLError(sdb_p->type, sqlerr);
  1.1036 +    }
  1.1037 +
  1.1038 +    if (stmt) {
  1.1039 +	sqlite3_reset(stmt);
  1.1040 +	sqlite3_finalize(stmt);
  1.1041 +    }
  1.1042 +
  1.1043 +    if (sqlDB) {
  1.1044 +	sdb_closeDBLocal(sdb_p, sqlDB) ;
  1.1045 +    }
  1.1046 +
  1.1047 +    UNLOCK_SQLITE()  
  1.1048 +    return error;
  1.1049 +}
  1.1050 +
  1.1051 +/*
  1.1052 + * check to see if a candidate object handle already exists.
  1.1053 + */
  1.1054 +static PRBool
  1.1055 +sdb_objectExists(SDB *sdb, CK_OBJECT_HANDLE candidate)
  1.1056 +{
  1.1057 +    CK_RV crv;
  1.1058 +    CK_ATTRIBUTE template = { CKA_LABEL, NULL, 0 };
  1.1059 +
  1.1060 +    crv = sdb_GetAttributeValueNoLock(sdb,candidate,&template, 1);
  1.1061 +    if (crv == CKR_OBJECT_HANDLE_INVALID) {
  1.1062 +	return PR_FALSE;
  1.1063 +    }
  1.1064 +    return PR_TRUE;
  1.1065 +}
  1.1066 +
  1.1067 +/*
  1.1068 + * if we're here, we are in a transaction, so it's safe
  1.1069 + * to examine the current state of the database
  1.1070 + */
  1.1071 +static CK_OBJECT_HANDLE
  1.1072 +sdb_getObjectId(SDB *sdb)
  1.1073 +{
  1.1074 +    CK_OBJECT_HANDLE candidate;
  1.1075 +    static CK_OBJECT_HANDLE next_obj = CK_INVALID_HANDLE;
  1.1076 +    int count;
  1.1077 +    /*
  1.1078 +     * get an initial object handle to use
  1.1079 +     */
  1.1080 +    if (next_obj == CK_INVALID_HANDLE) {
  1.1081 +        PRTime time;
  1.1082 +	time = PR_Now();
  1.1083 +
  1.1084 +	next_obj = (CK_OBJECT_HANDLE)(time & 0x3fffffffL);
  1.1085 +    }
  1.1086 +    candidate = next_obj++;
  1.1087 +    /* detect that we've looped through all the handles... */
  1.1088 +    for (count = 0; count < 0x40000000; count++, candidate = next_obj++) {
  1.1089 +	/* mask off excess bits */
  1.1090 +	candidate &= 0x3fffffff;
  1.1091 +	/* if we hit zero, go to the next entry */
  1.1092 +	if (candidate == CK_INVALID_HANDLE) {
  1.1093 +	    continue;
  1.1094 +	}
  1.1095 +	/* make sure we aren't already using */
  1.1096 +	if (!sdb_objectExists(sdb, candidate)) {
  1.1097 +	    /* this one is free */
  1.1098 +	    return candidate;
  1.1099 +	}
  1.1100 +    }
  1.1101 +
  1.1102 +    /* no handle is free, fail */
  1.1103 +    return CK_INVALID_HANDLE;
  1.1104 +}
  1.1105 +
  1.1106 +static const char CREATE_CMD[] = "INSERT INTO %s (id%s) VALUES($ID%s);";
  1.1107 +CK_RV
  1.1108 +sdb_CreateObject(SDB *sdb, CK_OBJECT_HANDLE *object_id, 
  1.1109 +		 const CK_ATTRIBUTE *template, CK_ULONG count)
  1.1110 +{
  1.1111 +    SDBPrivate *sdb_p = sdb->private;
  1.1112 +    sqlite3  *sqlDB = NULL;
  1.1113 +    sqlite3_stmt *stmt = NULL;
  1.1114 +    char *columnStr = NULL;
  1.1115 +    char *valueStr = NULL;
  1.1116 +    char *newStr = NULL;
  1.1117 +    int sqlerr = SQLITE_OK;
  1.1118 +    CK_RV error = CKR_OK;
  1.1119 +    CK_OBJECT_HANDLE this_object = CK_INVALID_HANDLE;
  1.1120 +    int retry = 0;
  1.1121 +    int i;
  1.1122 +
  1.1123 +    if ((sdb->sdb_flags & SDB_RDONLY) != 0) {
  1.1124 +	return CKR_TOKEN_WRITE_PROTECTED;
  1.1125 +    }
  1.1126 +
  1.1127 +    LOCK_SQLITE()  
  1.1128 +    if ((*object_id != CK_INVALID_HANDLE) && 
  1.1129 +		!sdb_objectExists(sdb, *object_id)) {
  1.1130 +	this_object = *object_id;
  1.1131 +    } else {
  1.1132 +	this_object = sdb_getObjectId(sdb);
  1.1133 +    }
  1.1134 +    if (this_object == CK_INVALID_HANDLE) {
  1.1135 +	UNLOCK_SQLITE();
  1.1136 +	return CKR_HOST_MEMORY;
  1.1137 +    }
  1.1138 +    columnStr = sqlite3_mprintf("");
  1.1139 +    valueStr = sqlite3_mprintf("");
  1.1140 +    *object_id = this_object;
  1.1141 +    for (i=0; columnStr && valueStr && i < count; i++) {
  1.1142 +   	newStr = sqlite3_mprintf("%s,a%x", columnStr, template[i].type);
  1.1143 +	sqlite3_free(columnStr);
  1.1144 +	columnStr = newStr;
  1.1145 +   	newStr = sqlite3_mprintf("%s,$VALUE%d", valueStr, i);
  1.1146 +	sqlite3_free(valueStr);
  1.1147 +	valueStr = newStr;
  1.1148 +    }
  1.1149 +    newStr = NULL;
  1.1150 +    if ((columnStr == NULL) || (valueStr == NULL)) {
  1.1151 +	if (columnStr) {
  1.1152 +	    sqlite3_free(columnStr);
  1.1153 +	}
  1.1154 +	if (valueStr) {
  1.1155 +	    sqlite3_free(valueStr);
  1.1156 +	}
  1.1157 +	UNLOCK_SQLITE()  
  1.1158 +	return CKR_HOST_MEMORY;
  1.1159 +    }
  1.1160 +    newStr =  sqlite3_mprintf(CREATE_CMD, sdb_p->table, columnStr, valueStr);
  1.1161 +    sqlite3_free(columnStr);
  1.1162 +    sqlite3_free(valueStr);
  1.1163 +    error = sdb_openDBLocal(sdb_p, &sqlDB, NULL);
  1.1164 +    if (error != CKR_OK) {
  1.1165 +	goto loser;
  1.1166 +    }
  1.1167 +    sqlerr = sqlite3_prepare_v2(sqlDB, newStr, -1, &stmt, NULL);
  1.1168 +    if (sqlerr != SQLITE_OK) goto loser;
  1.1169 +    sqlerr = sqlite3_bind_int(stmt, 1, *object_id);
  1.1170 +    if (sqlerr != SQLITE_OK) goto loser;
  1.1171 +    for (i=0; i < count; i++) {
  1.1172 +	if (template[i].ulValueLen) {
  1.1173 +	    sqlerr = sqlite3_bind_blob(stmt, i+2, template[i].pValue, 
  1.1174 +			template[i].ulValueLen, SQLITE_STATIC);
  1.1175 +	} else {
  1.1176 +	    sqlerr = sqlite3_bind_blob(stmt, i+2, SQLITE_EXPLICIT_NULL, 
  1.1177 +			SQLITE_EXPLICIT_NULL_LEN, SQLITE_STATIC);
  1.1178 +	}
  1.1179 +        if (sqlerr != SQLITE_OK) goto loser;
  1.1180 +    }
  1.1181 +
  1.1182 +    do {
  1.1183 +	sqlerr = sqlite3_step(stmt);
  1.1184 +	if (sqlerr == SQLITE_BUSY) {
  1.1185 +	    PR_Sleep(SDB_BUSY_RETRY_TIME);
  1.1186 +	}
  1.1187 +    } while (!sdb_done(sqlerr,&retry));
  1.1188 +
  1.1189 +loser:
  1.1190 +    if (newStr) {
  1.1191 +	sqlite3_free(newStr);
  1.1192 +    }
  1.1193 +    if (error == CKR_OK) {
  1.1194 +	error = sdb_mapSQLError(sdb_p->type, sqlerr);
  1.1195 +    }
  1.1196 +
  1.1197 +    if (stmt) {
  1.1198 +	sqlite3_reset(stmt);
  1.1199 +	sqlite3_finalize(stmt);
  1.1200 +    }
  1.1201 +
  1.1202 +    if (sqlDB) {
  1.1203 +	sdb_closeDBLocal(sdb_p, sqlDB) ;
  1.1204 +    }
  1.1205 +    UNLOCK_SQLITE()  
  1.1206 +
  1.1207 +    return error;
  1.1208 +}
  1.1209 +
  1.1210 +static const char DESTROY_CMD[] = "DELETE FROM %s WHERE (id=$ID);";
  1.1211 +CK_RV
  1.1212 +sdb_DestroyObject(SDB *sdb, CK_OBJECT_HANDLE object_id)
  1.1213 +{
  1.1214 +    SDBPrivate *sdb_p = sdb->private;
  1.1215 +    sqlite3  *sqlDB = NULL;
  1.1216 +    sqlite3_stmt *stmt = NULL;
  1.1217 +    char *newStr = NULL;
  1.1218 +    int sqlerr = SQLITE_OK;
  1.1219 +    CK_RV error = CKR_OK;
  1.1220 +    int retry = 0;
  1.1221 +
  1.1222 +    if ((sdb->sdb_flags & SDB_RDONLY) != 0) {
  1.1223 +	return CKR_TOKEN_WRITE_PROTECTED;
  1.1224 +    }
  1.1225 +
  1.1226 +    LOCK_SQLITE()  
  1.1227 +    error = sdb_openDBLocal(sdb_p, &sqlDB, NULL);
  1.1228 +    if (error != CKR_OK) {
  1.1229 +	goto loser;
  1.1230 +    }
  1.1231 +    newStr =  sqlite3_mprintf(DESTROY_CMD, sdb_p->table);
  1.1232 +    if (newStr == NULL) {
  1.1233 +	error = CKR_HOST_MEMORY;
  1.1234 +	goto loser;
  1.1235 +    }
  1.1236 +    sqlerr =sqlite3_prepare_v2(sqlDB, newStr, -1, &stmt, NULL);
  1.1237 +    sqlite3_free(newStr);
  1.1238 +    if (sqlerr != SQLITE_OK) goto loser;
  1.1239 +    sqlerr =sqlite3_bind_int(stmt, 1, object_id);
  1.1240 +    if (sqlerr != SQLITE_OK) goto loser;
  1.1241 +
  1.1242 +    do {
  1.1243 +	sqlerr = sqlite3_step(stmt);
  1.1244 +	if (sqlerr == SQLITE_BUSY) {
  1.1245 +	    PR_Sleep(SDB_BUSY_RETRY_TIME);
  1.1246 +	}
  1.1247 +    } while (!sdb_done(sqlerr,&retry));
  1.1248 +
  1.1249 +loser:
  1.1250 +    if (error == CKR_OK) {
  1.1251 +	error = sdb_mapSQLError(sdb_p->type, sqlerr);
  1.1252 +    }
  1.1253 +
  1.1254 +    if (stmt) {
  1.1255 +	sqlite3_reset(stmt);
  1.1256 +	sqlite3_finalize(stmt);
  1.1257 +    }
  1.1258 +
  1.1259 +    if (sqlDB) {
  1.1260 +	sdb_closeDBLocal(sdb_p, sqlDB) ;
  1.1261 +    }
  1.1262 +
  1.1263 +    UNLOCK_SQLITE()  
  1.1264 +    return error;
  1.1265 +}
  1.1266 +   
  1.1267 +static const char BEGIN_CMD[] = "BEGIN IMMEDIATE TRANSACTION;";
  1.1268 +/*
  1.1269 + * start a transaction.
  1.1270 + *
  1.1271 + * We need to open a new database, then store that new database into
  1.1272 + * the private data structure. We open the database first, then use locks
  1.1273 + * to protect storing the data to prevent deadlocks.
  1.1274 + */
  1.1275 +CK_RV
  1.1276 +sdb_Begin(SDB *sdb)
  1.1277 +{
  1.1278 +    SDBPrivate *sdb_p = sdb->private;
  1.1279 +    sqlite3  *sqlDB = NULL;
  1.1280 +    sqlite3_stmt *stmt = NULL;
  1.1281 +    int sqlerr = SQLITE_OK;
  1.1282 +    CK_RV error = CKR_OK;
  1.1283 +    int retry = 0;
  1.1284 +
  1.1285 +
  1.1286 +    if ((sdb->sdb_flags & SDB_RDONLY) != 0) {
  1.1287 +	return CKR_TOKEN_WRITE_PROTECTED;
  1.1288 +    }
  1.1289 +
  1.1290 +
  1.1291 +    LOCK_SQLITE()  
  1.1292 +
  1.1293 +    /* get a new version that we will use for the entire transaction */
  1.1294 +    sqlerr = sdb_openDB(sdb_p->sqlDBName, &sqlDB, SDB_RDWR);
  1.1295 +    if (sqlerr != SQLITE_OK) {
  1.1296 +	goto loser;
  1.1297 +    }
  1.1298 +
  1.1299 +    sqlerr =sqlite3_prepare_v2(sqlDB, BEGIN_CMD, -1, &stmt, NULL);
  1.1300 +
  1.1301 +    do {
  1.1302 +	sqlerr = sqlite3_step(stmt);
  1.1303 +	if (sqlerr == SQLITE_BUSY) {
  1.1304 +	    PR_Sleep(SDB_BUSY_RETRY_TIME);
  1.1305 +	}
  1.1306 +    } while (!sdb_done(sqlerr,&retry));
  1.1307 +
  1.1308 +    if (stmt) {
  1.1309 +	sqlite3_reset(stmt);
  1.1310 +	sqlite3_finalize(stmt);
  1.1311 +    }
  1.1312 +
  1.1313 +loser:
  1.1314 +    error = sdb_mapSQLError(sdb_p->type, sqlerr);
  1.1315 +
  1.1316 +    /* we are starting a new transaction, 
  1.1317 +     * and if we succeeded, then save this database for the rest of
  1.1318 +     * our transaction */
  1.1319 +    if (error == CKR_OK) {
  1.1320 +	/* we hold a 'BEGIN TRANSACTION' and a sdb_p->lock. At this point
  1.1321 +	 * sdb_p->sqlXactDB MUST be null */
  1.1322 +	PR_EnterMonitor(sdb_p->dbMon);
  1.1323 +	PORT_Assert(sdb_p->sqlXactDB == NULL);
  1.1324 +	sdb_p->sqlXactDB = sqlDB;
  1.1325 +	sdb_p->sqlXactThread = PR_GetCurrentThread();
  1.1326 +        PR_ExitMonitor(sdb_p->dbMon);
  1.1327 +    } else {
  1.1328 +	/* we failed to start our transaction,
  1.1329 +	 * free any databases we opened. */
  1.1330 +	if (sqlDB) {
  1.1331 +	    sqlite3_close(sqlDB);
  1.1332 +	}
  1.1333 +    }
  1.1334 +
  1.1335 +    UNLOCK_SQLITE()  
  1.1336 +    return error;
  1.1337 +}
  1.1338 +
  1.1339 +/*
  1.1340 + * Complete a transaction. Basically undo everything we did in begin.
  1.1341 + * There are 2 flavors Abort and Commit. Basically the only differerence between
  1.1342 + * these 2 are what the database will show. (no change in to former, change in
  1.1343 + * the latter).
  1.1344 + */
  1.1345 +static CK_RV 
  1.1346 +sdb_complete(SDB *sdb, const char *cmd)
  1.1347 +{
  1.1348 +    SDBPrivate *sdb_p = sdb->private;
  1.1349 +    sqlite3  *sqlDB = NULL;
  1.1350 +    sqlite3_stmt *stmt = NULL;
  1.1351 +    int sqlerr = SQLITE_OK;
  1.1352 +    CK_RV error = CKR_OK;
  1.1353 +    int retry = 0;
  1.1354 +
  1.1355 +
  1.1356 +    if ((sdb->sdb_flags & SDB_RDONLY) != 0) {
  1.1357 +	return CKR_TOKEN_WRITE_PROTECTED;
  1.1358 +    }
  1.1359 +
  1.1360 +    /* We must have a transation database, or we shouldn't have arrived here */
  1.1361 +    PR_EnterMonitor(sdb_p->dbMon);
  1.1362 +    PORT_Assert(sdb_p->sqlXactDB);
  1.1363 +    if (sdb_p->sqlXactDB == NULL) {
  1.1364 +        PR_ExitMonitor(sdb_p->dbMon);
  1.1365 +	return CKR_GENERAL_ERROR; /* shouldn't happen */
  1.1366 +    }
  1.1367 +    PORT_Assert( sdb_p->sqlXactThread == PR_GetCurrentThread());
  1.1368 +    if ( sdb_p->sqlXactThread != PR_GetCurrentThread()) {
  1.1369 +        PR_ExitMonitor(sdb_p->dbMon);
  1.1370 +	return CKR_GENERAL_ERROR; /* shouldn't happen */
  1.1371 +    }
  1.1372 +    sqlDB = sdb_p->sqlXactDB;
  1.1373 +    sdb_p->sqlXactDB = NULL; /* no one else can get to this DB, 
  1.1374 +			      * safe to unlock */
  1.1375 +    sdb_p->sqlXactThread = NULL; 
  1.1376 +    PR_ExitMonitor(sdb_p->dbMon);
  1.1377 +
  1.1378 +    sqlerr =sqlite3_prepare_v2(sqlDB, cmd, -1, &stmt, NULL);
  1.1379 +
  1.1380 +    do {
  1.1381 +	sqlerr = sqlite3_step(stmt);
  1.1382 +	if (sqlerr == SQLITE_BUSY) {
  1.1383 +	    PR_Sleep(SDB_BUSY_RETRY_TIME);
  1.1384 +	}
  1.1385 +    } while (!sdb_done(sqlerr,&retry));
  1.1386 +
  1.1387 +    /* Pending BEGIN TRANSACTIONS Can move forward at this point. */
  1.1388 +
  1.1389 +    if (stmt) {
  1.1390 +	sqlite3_reset(stmt);
  1.1391 +	sqlite3_finalize(stmt);
  1.1392 +    }
  1.1393 +
  1.1394 +    /* we we have a cached DB image, update it as well */
  1.1395 +    if (sdb_p->cacheTable) {
  1.1396 +	PR_EnterMonitor(sdb_p->dbMon);
  1.1397 +	sdb_updateCache(sdb_p);
  1.1398 +	PR_ExitMonitor(sdb_p->dbMon);
  1.1399 +    }
  1.1400 +
  1.1401 +    error = sdb_mapSQLError(sdb_p->type, sqlerr);
  1.1402 +
  1.1403 +    /* We just finished a transaction.
  1.1404 +     * Free the database, and remove it from the list */
  1.1405 +    sqlite3_close(sqlDB);
  1.1406 +
  1.1407 +    return error;
  1.1408 +}
  1.1409 +
  1.1410 +static const char COMMIT_CMD[] = "COMMIT TRANSACTION;";
  1.1411 +CK_RV
  1.1412 +sdb_Commit(SDB *sdb)
  1.1413 +{
  1.1414 +    CK_RV crv;
  1.1415 +    LOCK_SQLITE()  
  1.1416 +    crv = sdb_complete(sdb,COMMIT_CMD);
  1.1417 +    UNLOCK_SQLITE()  
  1.1418 +    return crv;
  1.1419 +}
  1.1420 +
  1.1421 +static const char ROLLBACK_CMD[] = "ROLLBACK TRANSACTION;";
  1.1422 +CK_RV
  1.1423 +sdb_Abort(SDB *sdb)
  1.1424 +{
  1.1425 +    CK_RV crv;
  1.1426 +    LOCK_SQLITE()  
  1.1427 +    crv = sdb_complete(sdb,ROLLBACK_CMD);
  1.1428 +    UNLOCK_SQLITE()  
  1.1429 +    return crv;
  1.1430 +}
  1.1431 +
  1.1432 +static int tableExists(sqlite3 *sqlDB, const char *tableName);
  1.1433 +
  1.1434 +static const char GET_PW_CMD[] = "SELECT ALL * FROM metaData WHERE id=$ID;";
  1.1435 +CK_RV
  1.1436 +sdb_GetMetaData(SDB *sdb, const char *id, SECItem *item1, SECItem *item2)
  1.1437 +{
  1.1438 +    SDBPrivate *sdb_p = sdb->private;
  1.1439 +    sqlite3  *sqlDB = sdb_p->sqlXactDB;
  1.1440 +    sqlite3_stmt *stmt = NULL;
  1.1441 +    int sqlerr = SQLITE_OK;
  1.1442 +    CK_RV error = CKR_OK;
  1.1443 +    int found = 0;
  1.1444 +    int retry = 0;
  1.1445 +
  1.1446 +    LOCK_SQLITE()  
  1.1447 +    error = sdb_openDBLocal(sdb_p, &sqlDB, NULL);
  1.1448 +    if (error != CKR_OK) {
  1.1449 +	goto loser;
  1.1450 +    }
  1.1451 +
  1.1452 +    /* handle 'test' versions of the sqlite db */
  1.1453 +    sqlerr = sqlite3_prepare_v2(sqlDB, GET_PW_CMD, -1, &stmt, NULL);
  1.1454 +    /* Sigh, if we created a new table since we opened the database,
  1.1455 +     * the database handle will not see the new table, we need to close this
  1.1456 +     * database and reopen it. This is safe because we are holding the lock
  1.1457 +     * still. */
  1.1458 +    if (sqlerr == SQLITE_SCHEMA) {
  1.1459 +	sqlerr = sdb_reopenDBLocal(sdb_p, &sqlDB);
  1.1460 +	if (sqlerr != SQLITE_OK) {
  1.1461 +	    goto loser;
  1.1462 +	}
  1.1463 +	sqlerr = sqlite3_prepare_v2(sqlDB, GET_PW_CMD, -1, &stmt, NULL);
  1.1464 +    }
  1.1465 +    if (sqlerr != SQLITE_OK) goto loser;
  1.1466 +    sqlerr = sqlite3_bind_text(stmt, 1, id, PORT_Strlen(id), SQLITE_STATIC);
  1.1467 +    do {
  1.1468 +	sqlerr = sqlite3_step(stmt);
  1.1469 +	if (sqlerr == SQLITE_BUSY) {
  1.1470 +	    PR_Sleep(SDB_BUSY_RETRY_TIME);
  1.1471 +	}
  1.1472 +	if (sqlerr == SQLITE_ROW) {
  1.1473 +	    const char *blobData;
  1.1474 +	    unsigned int len = item1->len;
  1.1475 +	    item1->len = sqlite3_column_bytes(stmt, 1);
  1.1476 +	    if (item1->len > len) {
  1.1477 +		error = CKR_BUFFER_TOO_SMALL;
  1.1478 +		continue;
  1.1479 +	    }
  1.1480 +	    blobData = sqlite3_column_blob(stmt, 1);
  1.1481 +	    PORT_Memcpy(item1->data,blobData, item1->len);
  1.1482 +	    if (item2) {
  1.1483 +		len = item2->len;
  1.1484 +		item2->len = sqlite3_column_bytes(stmt, 2);
  1.1485 +		if (item2->len > len) {
  1.1486 +		    error = CKR_BUFFER_TOO_SMALL;
  1.1487 +		    continue;
  1.1488 +		}
  1.1489 +		blobData = sqlite3_column_blob(stmt, 2);
  1.1490 +		PORT_Memcpy(item2->data,blobData, item2->len);
  1.1491 +	    }
  1.1492 +	    found = 1;
  1.1493 +	}
  1.1494 +    } while (!sdb_done(sqlerr,&retry));
  1.1495 +
  1.1496 +loser:
  1.1497 +    /* fix up the error if necessary */
  1.1498 +    if (error == CKR_OK) {
  1.1499 +	error = sdb_mapSQLError(sdb_p->type, sqlerr);
  1.1500 +	if (!found && error == CKR_OK) {
  1.1501 +	    error = CKR_OBJECT_HANDLE_INVALID;
  1.1502 +	}
  1.1503 +    }
  1.1504 +
  1.1505 +    if (stmt) {
  1.1506 +	sqlite3_reset(stmt);
  1.1507 +	sqlite3_finalize(stmt);
  1.1508 +    }
  1.1509 +
  1.1510 +    if (sqlDB) {
  1.1511 +	sdb_closeDBLocal(sdb_p, sqlDB) ;
  1.1512 +    }
  1.1513 +    UNLOCK_SQLITE()  
  1.1514 +
  1.1515 +    return error;
  1.1516 +}
  1.1517 +
  1.1518 +static const char PW_CREATE_TABLE_CMD[] =
  1.1519 + "CREATE TABLE metaData (id PRIMARY KEY UNIQUE ON CONFLICT REPLACE, item1, item2);";
  1.1520 +static const char PW_CREATE_CMD[] =
  1.1521 + "INSERT INTO metaData (id,item1,item2) VALUES($ID,$ITEM1,$ITEM2);";
  1.1522 +static const char MD_CREATE_CMD[]  =
  1.1523 + "INSERT INTO metaData (id,item1) VALUES($ID,$ITEM1);";
  1.1524 +CK_RV
  1.1525 +sdb_PutMetaData(SDB *sdb, const char *id, const SECItem *item1, 
  1.1526 +					   const SECItem *item2)
  1.1527 +{
  1.1528 +    SDBPrivate *sdb_p = sdb->private;
  1.1529 +    sqlite3  *sqlDB = sdb_p->sqlXactDB;
  1.1530 +    sqlite3_stmt *stmt = NULL;
  1.1531 +    int sqlerr = SQLITE_OK;
  1.1532 +    CK_RV error = CKR_OK;
  1.1533 +    int retry = 0;
  1.1534 +    const char *cmd = PW_CREATE_CMD;
  1.1535 +
  1.1536 +    if ((sdb->sdb_flags & SDB_RDONLY) != 0) {
  1.1537 +	return CKR_TOKEN_WRITE_PROTECTED;
  1.1538 +    }
  1.1539 +
  1.1540 +    LOCK_SQLITE()  
  1.1541 +    error = sdb_openDBLocal(sdb_p, &sqlDB, NULL);
  1.1542 +    if (error != CKR_OK) {
  1.1543 +	goto loser;
  1.1544 +    }
  1.1545 +
  1.1546 +    if (!tableExists(sqlDB, "metaData")) {
  1.1547 +    	sqlerr = sqlite3_exec(sqlDB, PW_CREATE_TABLE_CMD, NULL, 0, NULL);
  1.1548 +        if (sqlerr != SQLITE_OK) goto loser;
  1.1549 +    }
  1.1550 +    if (item2 == NULL) {
  1.1551 +	cmd = MD_CREATE_CMD;
  1.1552 +    }
  1.1553 +    sqlerr = sqlite3_prepare_v2(sqlDB, cmd, -1, &stmt, NULL);
  1.1554 +    if (sqlerr != SQLITE_OK) goto loser;
  1.1555 +    sqlerr = sqlite3_bind_text(stmt, 1, id, PORT_Strlen(id), SQLITE_STATIC);
  1.1556 +    if (sqlerr != SQLITE_OK) goto loser;
  1.1557 +    sqlerr = sqlite3_bind_blob(stmt, 2, item1->data, item1->len, SQLITE_STATIC);
  1.1558 +    if (sqlerr != SQLITE_OK) goto loser;
  1.1559 +    if (item2) {
  1.1560 +    	sqlerr = sqlite3_bind_blob(stmt, 3, item2->data, 
  1.1561 +				   item2->len, SQLITE_STATIC);
  1.1562 +        if (sqlerr != SQLITE_OK) goto loser;
  1.1563 +    }
  1.1564 +
  1.1565 +    do {
  1.1566 +	sqlerr = sqlite3_step(stmt);
  1.1567 +	if (sqlerr == SQLITE_BUSY) {
  1.1568 +	    PR_Sleep(SDB_BUSY_RETRY_TIME);
  1.1569 +	}
  1.1570 +    } while (!sdb_done(sqlerr,&retry));
  1.1571 +
  1.1572 +loser:
  1.1573 +    /* fix up the error if necessary */
  1.1574 +    if (error == CKR_OK) {
  1.1575 +	error = sdb_mapSQLError(sdb_p->type, sqlerr);
  1.1576 +    }
  1.1577 +
  1.1578 +    if (stmt) {
  1.1579 +	sqlite3_reset(stmt);
  1.1580 +	sqlite3_finalize(stmt);
  1.1581 +    }
  1.1582 +
  1.1583 +    if (sqlDB) {
  1.1584 +	sdb_closeDBLocal(sdb_p, sqlDB) ;
  1.1585 +    }
  1.1586 +    UNLOCK_SQLITE()  
  1.1587 +
  1.1588 +    return error;
  1.1589 +}
  1.1590 +
  1.1591 +static const char RESET_CMD[] = "DROP TABLE IF EXISTS %s;";
  1.1592 +CK_RV
  1.1593 +sdb_Reset(SDB *sdb)
  1.1594 +{
  1.1595 +    SDBPrivate *sdb_p = sdb->private;
  1.1596 +    sqlite3  *sqlDB = NULL;
  1.1597 +    char *newStr;
  1.1598 +    int sqlerr = SQLITE_OK;
  1.1599 +    CK_RV error = CKR_OK;
  1.1600 +
  1.1601 +    /* only Key databases can be reset */
  1.1602 +    if (sdb_p->type != SDB_KEY) {
  1.1603 +	return CKR_OBJECT_HANDLE_INVALID;
  1.1604 +    }
  1.1605 +
  1.1606 +    LOCK_SQLITE()  
  1.1607 +    error = sdb_openDBLocal(sdb_p, &sqlDB, NULL);
  1.1608 +    if (error != CKR_OK) {
  1.1609 +	goto loser;
  1.1610 +    }
  1.1611 +
  1.1612 +    /* delete the key table */
  1.1613 +    newStr =  sqlite3_mprintf(RESET_CMD, sdb_p->table);
  1.1614 +    if (newStr == NULL) {
  1.1615 +	error = CKR_HOST_MEMORY;
  1.1616 +	goto loser;
  1.1617 +    }
  1.1618 +    sqlerr = sqlite3_exec(sqlDB, newStr, NULL, 0, NULL);
  1.1619 +    sqlite3_free(newStr);
  1.1620 +
  1.1621 +    if (sqlerr != SQLITE_OK) goto loser;
  1.1622 +
  1.1623 +    /* delete the password entry table */
  1.1624 +    sqlerr = sqlite3_exec(sqlDB, "DROP TABLE IF EXISTS metaData;", 
  1.1625 +                          NULL, 0, NULL);
  1.1626 +
  1.1627 +loser:
  1.1628 +    /* fix up the error if necessary */
  1.1629 +    if (error == CKR_OK) {
  1.1630 +	error = sdb_mapSQLError(sdb_p->type, sqlerr);
  1.1631 +    }
  1.1632 +
  1.1633 +    if (sqlDB) {
  1.1634 +	sdb_closeDBLocal(sdb_p, sqlDB) ;
  1.1635 +    }
  1.1636 +
  1.1637 +    UNLOCK_SQLITE()  
  1.1638 +    return error;
  1.1639 +}
  1.1640 +
  1.1641 +
  1.1642 +CK_RV 
  1.1643 +sdb_Close(SDB *sdb) 
  1.1644 +{
  1.1645 +    SDBPrivate *sdb_p = sdb->private;
  1.1646 +    int sqlerr = SQLITE_OK;
  1.1647 +    sdbDataType type = sdb_p->type;
  1.1648 +
  1.1649 +    sqlerr = sqlite3_close(sdb_p->sqlReadDB);
  1.1650 +    PORT_Free(sdb_p->sqlDBName);
  1.1651 +    if (sdb_p->cacheTable) {
  1.1652 +	sqlite3_free(sdb_p->cacheTable);
  1.1653 +    }
  1.1654 +    if (sdb_p->dbMon) {
  1.1655 +	PR_DestroyMonitor(sdb_p->dbMon);
  1.1656 +    }
  1.1657 +    free(sdb_p);
  1.1658 +    free(sdb);
  1.1659 +    return sdb_mapSQLError(type, sqlerr);
  1.1660 +}
  1.1661 +
  1.1662 +
  1.1663 +/*
  1.1664 + * functions to support open
  1.1665 + */
  1.1666 +
  1.1667 +static const char CHECK_TABLE_CMD[] = "SELECT ALL * FROM %s LIMIT 0;";
  1.1668 +/* return 1 if sqlDB contains table 'tableName */
  1.1669 +static int tableExists(sqlite3 *sqlDB, const char *tableName)
  1.1670 +{
  1.1671 +    char * cmd = sqlite3_mprintf(CHECK_TABLE_CMD, tableName);
  1.1672 +    int sqlerr = SQLITE_OK;
  1.1673 +
  1.1674 +    if (cmd == NULL) {
  1.1675 +	return 0;
  1.1676 +    }
  1.1677 +
  1.1678 +    sqlerr = sqlite3_exec(sqlDB, cmd, NULL, 0, 0);
  1.1679 +    sqlite3_free(cmd);
  1.1680 +
  1.1681 +    return (sqlerr == SQLITE_OK) ? 1 : 0;
  1.1682 +}
  1.1683 +
  1.1684 +void sdb_SetForkState(PRBool forked)
  1.1685 +{
  1.1686 +    /* XXXright now this is a no-op. The global fork state in the softokn3
  1.1687 +     * shared library is already taken care of at the PKCS#11 level.
  1.1688 +     * If and when we add fork state to the sqlite shared library and extern
  1.1689 +     * interface, we will need to set it and reset it from here */
  1.1690 +}
  1.1691 +
  1.1692 +/*
  1.1693 + * initialize a single database
  1.1694 + */
  1.1695 +static const char INIT_CMD[] =
  1.1696 + "CREATE TABLE %s (id PRIMARY KEY UNIQUE ON CONFLICT ABORT%s)";
  1.1697 +static const char ALTER_CMD[] = 
  1.1698 + "ALTER TABLE %s ADD COLUMN a%x";
  1.1699 +
  1.1700 +CK_RV 
  1.1701 +sdb_init(char *dbname, char *table, sdbDataType type, int *inUpdate,
  1.1702 +	 int *newInit, int flags, PRUint32 accessOps, SDB **pSdb)
  1.1703 +{
  1.1704 +    int i;
  1.1705 +    char *initStr = NULL;
  1.1706 +    char *newStr;
  1.1707 +    int inTransaction = 0;
  1.1708 +    SDB *sdb = NULL;
  1.1709 +    SDBPrivate *sdb_p = NULL;
  1.1710 +    sqlite3 *sqlDB = NULL;
  1.1711 +    int sqlerr = SQLITE_OK;
  1.1712 +    CK_RV error = CKR_OK;
  1.1713 +    char *cacheTable = NULL;
  1.1714 +    PRIntervalTime now = 0;
  1.1715 +    char *env;
  1.1716 +    PRBool enableCache = PR_FALSE;
  1.1717 +    PRBool create;
  1.1718 +
  1.1719 +    *pSdb = NULL;
  1.1720 +    *inUpdate = 0;
  1.1721 +
  1.1722 +    /* sqlite3 doesn't have a flag to specify that we want to 
  1.1723 +     * open the database read only. If the db doesn't exist,
  1.1724 +     * sqlite3 will always create it.
  1.1725 +     */
  1.1726 +    LOCK_SQLITE();
  1.1727 +    create = (PR_Access(dbname, PR_ACCESS_EXISTS) != PR_SUCCESS);
  1.1728 +    if ((flags == SDB_RDONLY) && create) {
  1.1729 +	error = sdb_mapSQLError(type, SQLITE_CANTOPEN);
  1.1730 +	goto loser;
  1.1731 +    }
  1.1732 +    sqlerr = sdb_openDB(dbname, &sqlDB, flags);
  1.1733 +    if (sqlerr != SQLITE_OK) {
  1.1734 +	error = sdb_mapSQLError(type, sqlerr); 
  1.1735 +	goto loser;
  1.1736 +    }
  1.1737 +    /* sql created the file, but it doesn't set appropriate modes for
  1.1738 +     * a database */
  1.1739 +    if (create) {
  1.1740 +	/* NO NSPR call for this? :( */
  1.1741 +	chmod (dbname, 0600);
  1.1742 +    }
  1.1743 +
  1.1744 +    if (flags != SDB_RDONLY) {
  1.1745 +	sqlerr = sqlite3_exec(sqlDB, BEGIN_CMD, NULL, 0, NULL);
  1.1746 +	if (sqlerr != SQLITE_OK) {
  1.1747 +	    error = sdb_mapSQLError(type, sqlerr);
  1.1748 +	    goto loser;
  1.1749 +	}
  1.1750 +	inTransaction = 1;
  1.1751 +    }
  1.1752 +    if (!tableExists(sqlDB,table)) {
  1.1753 +	*newInit = 1;
  1.1754 +	if (flags != SDB_CREATE) {
  1.1755 +	    error = sdb_mapSQLError(type, SQLITE_CANTOPEN);
  1.1756 +	    goto loser;
  1.1757 +	}
  1.1758 +	initStr = sqlite3_mprintf("");
  1.1759 +	for (i=0; initStr && i < known_attributes_size; i++) {
  1.1760 +	    newStr = sqlite3_mprintf("%s, a%x",initStr, known_attributes[i]);
  1.1761 +	    sqlite3_free(initStr);
  1.1762 +	    initStr = newStr;
  1.1763 +	}
  1.1764 +	if (initStr == NULL) {
  1.1765 +	    error = CKR_HOST_MEMORY;
  1.1766 +	    goto loser;
  1.1767 +	}
  1.1768 +
  1.1769 +	newStr = sqlite3_mprintf(INIT_CMD, table, initStr);
  1.1770 +	sqlite3_free(initStr);
  1.1771 +	if (newStr == NULL) {
  1.1772 +            error = CKR_HOST_MEMORY;
  1.1773 +	    goto loser;
  1.1774 +	}
  1.1775 +	sqlerr = sqlite3_exec(sqlDB, newStr, NULL, 0, NULL);
  1.1776 +	sqlite3_free(newStr);
  1.1777 +	if (sqlerr != SQLITE_OK) {
  1.1778 +            error = sdb_mapSQLError(type, sqlerr); 
  1.1779 +	    goto loser;
  1.1780 +	}
  1.1781 +
  1.1782 +	newStr = sqlite3_mprintf(CREATE_ISSUER_INDEX_CMD, table);
  1.1783 +	if (newStr == NULL) {
  1.1784 +            error = CKR_HOST_MEMORY;
  1.1785 +	    goto loser;
  1.1786 +	}
  1.1787 +	sqlerr = sqlite3_exec(sqlDB, newStr, NULL, 0, NULL);
  1.1788 +	sqlite3_free(newStr);
  1.1789 +	if (sqlerr != SQLITE_OK) {
  1.1790 +            error = sdb_mapSQLError(type, sqlerr); 
  1.1791 +	    goto loser;
  1.1792 +	}
  1.1793 +
  1.1794 +	newStr = sqlite3_mprintf(CREATE_SUBJECT_INDEX_CMD, table);
  1.1795 +	if (newStr == NULL) {
  1.1796 +            error = CKR_HOST_MEMORY;
  1.1797 +	    goto loser;
  1.1798 +	}
  1.1799 +	sqlerr = sqlite3_exec(sqlDB, newStr, NULL, 0, NULL);
  1.1800 +	sqlite3_free(newStr);
  1.1801 +	if (sqlerr != SQLITE_OK) {
  1.1802 +            error = sdb_mapSQLError(type, sqlerr); 
  1.1803 +	    goto loser;
  1.1804 +	}
  1.1805 +
  1.1806 +	newStr = sqlite3_mprintf(CREATE_LABEL_INDEX_CMD, table);
  1.1807 +	if (newStr == NULL) {
  1.1808 +            error = CKR_HOST_MEMORY;
  1.1809 +	    goto loser;
  1.1810 +	}
  1.1811 +	sqlerr = sqlite3_exec(sqlDB, newStr, NULL, 0, NULL);
  1.1812 +	sqlite3_free(newStr);
  1.1813 +	if (sqlerr != SQLITE_OK) {
  1.1814 +            error = sdb_mapSQLError(type, sqlerr); 
  1.1815 +	    goto loser;
  1.1816 +	}
  1.1817 +
  1.1818 +	newStr = sqlite3_mprintf(CREATE_ID_INDEX_CMD, table);
  1.1819 +	if (newStr == NULL) {
  1.1820 +            error = CKR_HOST_MEMORY;
  1.1821 +	    goto loser;
  1.1822 +	}
  1.1823 +	sqlerr = sqlite3_exec(sqlDB, newStr, NULL, 0, NULL);
  1.1824 +	sqlite3_free(newStr);
  1.1825 +	if (sqlerr != SQLITE_OK) {
  1.1826 +            error = sdb_mapSQLError(type, sqlerr); 
  1.1827 +	    goto loser;
  1.1828 +	}
  1.1829 +    }
  1.1830 +    /*
  1.1831 +     * detect the case where we have created the database, but have
  1.1832 +     * not yet updated it.
  1.1833 +     *
  1.1834 +     * We only check the Key database because only the key database has
  1.1835 +     * a metaData table. The metaData table is created when a password
  1.1836 +     * is set, or in the case of update, when a password is supplied.
  1.1837 +     * If no key database exists, then the update would have happened immediately
  1.1838 +     * on noticing that the cert database didn't exist (see newInit set above).
  1.1839 +     */
  1.1840 +    if (type == SDB_KEY && !tableExists(sqlDB, "metaData")) {
  1.1841 +	*newInit = 1;
  1.1842 +    }
  1.1843 +    
  1.1844 +    /* access to network filesystems are significantly slower than local ones
  1.1845 +     * for database operations. In those cases we need to create a cached copy
  1.1846 +     * of the database in a temporary location on the local disk. SQLITE
  1.1847 +     * already provides a way to create a temporary table and initialize it,
  1.1848 +     * so we use it for the cache (see sdb_buildCache for how it's done).*/
  1.1849 +
  1.1850 +     /* 
  1.1851 +      * we decide whether or not to use the cache based on the following input.
  1.1852 +      *
  1.1853 +      * NSS_SDB_USE_CACHE environment variable is non-existant or set to 
  1.1854 +      *   anything other than "no" or "yes" ("auto", for instance).
  1.1855 +      *   This is the normal case. NSS will measure the performance of access
  1.1856 +      *   to the temp database versus the access to the users passed in 
  1.1857 +      *   database location. If the temp database location is "significantly"
  1.1858 +      *   faster we will use the cache.
  1.1859 +      *
  1.1860 +      * NSS_SDB_USE_CACHE environment variable is set to "no": cache will not
  1.1861 +      *   be used.
  1.1862 +      *
  1.1863 +      * NSS_SDB_USE_CACHE environment variable is set to "yes": cache will
  1.1864 +      *   always be used.
  1.1865 +      *
  1.1866 +      * It is expected that most applications would use the "auto" selection,
  1.1867 +      * the environment variable is primarily to simplify testing, and to 
  1.1868 +      * correct potential corner cases where  */
  1.1869 +
  1.1870 +     env = PR_GetEnv("NSS_SDB_USE_CACHE");
  1.1871 +
  1.1872 +     if (env && PORT_Strcasecmp(env,"no") == 0) {
  1.1873 +	enableCache = PR_FALSE;
  1.1874 +     } else if (env && PORT_Strcasecmp(env,"yes") == 0) {
  1.1875 +	enableCache = PR_TRUE;
  1.1876 +     } else {
  1.1877 +	char *tempDir = NULL;
  1.1878 +	PRUint32 tempOps = 0;
  1.1879 +	/*
  1.1880 +	 *  Use PR_Access to determine how expensive it
  1.1881 +	 * is to check for the existance of a local file compared to the same
  1.1882 +	 * check in the temp directory. If the temp directory is faster, cache
  1.1883 +	 * the database there. */
  1.1884 +	tempDir = sdb_getTempDir(sqlDB);
  1.1885 +	if (tempDir) {
  1.1886 +	    tempOps = sdb_measureAccess(tempDir);
  1.1887 +	    PORT_Free(tempDir);
  1.1888 +
  1.1889 +	    /* There is a cost to continually copying the database. 
  1.1890 +	     * Account for that cost  with the arbitrary factor of 10 */
  1.1891 +	    enableCache = (PRBool)(tempOps > accessOps * 10);
  1.1892 +	}
  1.1893 +    }
  1.1894 +
  1.1895 +    if (enableCache) {
  1.1896 +	/* try to set the temp store to memory.*/
  1.1897 +	sqlite3_exec(sqlDB, "PRAGMA temp_store=MEMORY", NULL, 0, NULL);
  1.1898 +	/* Failure to set the temp store to memory is not fatal,
  1.1899 +         * ignore the error */
  1.1900 +
  1.1901 +	cacheTable = sqlite3_mprintf("%sCache",table);
  1.1902 +	if (cacheTable == NULL) {
  1.1903 +	    error = CKR_HOST_MEMORY;
  1.1904 +	    goto loser;
  1.1905 +	}
  1.1906 +	/* build the cache table */
  1.1907 +	error = sdb_buildCache(sqlDB, type, cacheTable, table);
  1.1908 +	if (error != CKR_OK) {
  1.1909 +	    goto loser;
  1.1910 +	}
  1.1911 +	/* initialize the last cache build time */
  1.1912 +	now = PR_IntervalNow();
  1.1913 +    }
  1.1914 +
  1.1915 +    sdb = (SDB *) malloc(sizeof(SDB));
  1.1916 +    sdb_p = (SDBPrivate *) malloc(sizeof(SDBPrivate));
  1.1917 +
  1.1918 +    /* invariant fields */
  1.1919 +    sdb_p->sqlDBName = PORT_Strdup(dbname);
  1.1920 +    sdb_p->type = type;
  1.1921 +    sdb_p->table = table;
  1.1922 +    sdb_p->cacheTable = cacheTable;
  1.1923 +    sdb_p->lastUpdateTime = now;
  1.1924 +    /* set the cache delay time. This is how long we will wait before we
  1.1925 +     * decide the existing cache is stale. Currently set to 10 sec */
  1.1926 +    sdb_p->updateInterval = PR_SecondsToInterval(10); 
  1.1927 +    sdb_p->dbMon = PR_NewMonitor();
  1.1928 +    /* these fields are protected by the lock */
  1.1929 +    sdb_p->sqlXactDB = NULL;
  1.1930 +    sdb_p->sqlXactThread = NULL;
  1.1931 +    sdb->private = sdb_p;
  1.1932 +    sdb->version = 0;
  1.1933 +    sdb->sdb_flags = flags | SDB_HAS_META;
  1.1934 +    sdb->app_private = NULL;
  1.1935 +    sdb->sdb_FindObjectsInit = sdb_FindObjectsInit;
  1.1936 +    sdb->sdb_FindObjects = sdb_FindObjects;
  1.1937 +    sdb->sdb_FindObjectsFinal = sdb_FindObjectsFinal;
  1.1938 +    sdb->sdb_GetAttributeValue = sdb_GetAttributeValue;
  1.1939 +    sdb->sdb_SetAttributeValue = sdb_SetAttributeValue;
  1.1940 +    sdb->sdb_CreateObject = sdb_CreateObject;
  1.1941 +    sdb->sdb_DestroyObject = sdb_DestroyObject;
  1.1942 +    sdb->sdb_GetMetaData = sdb_GetMetaData;
  1.1943 +    sdb->sdb_PutMetaData = sdb_PutMetaData;
  1.1944 +    sdb->sdb_Begin = sdb_Begin;
  1.1945 +    sdb->sdb_Commit = sdb_Commit;
  1.1946 +    sdb->sdb_Abort = sdb_Abort;
  1.1947 +    sdb->sdb_Reset = sdb_Reset;
  1.1948 +    sdb->sdb_Close = sdb_Close;
  1.1949 +    sdb->sdb_SetForkState = sdb_SetForkState;
  1.1950 +
  1.1951 +    if (inTransaction) {
  1.1952 +	sqlerr = sqlite3_exec(sqlDB, COMMIT_CMD, NULL, 0, NULL);
  1.1953 +	if (sqlerr != SQLITE_OK) {
  1.1954 +	    error = sdb_mapSQLError(sdb_p->type, sqlerr);
  1.1955 +	    goto loser;
  1.1956 +	}
  1.1957 +	inTransaction = 0;
  1.1958 +    }
  1.1959 +
  1.1960 +    sdb_p->sqlReadDB = sqlDB;
  1.1961 +
  1.1962 +    *pSdb = sdb;
  1.1963 +    UNLOCK_SQLITE();
  1.1964 +    return CKR_OK;
  1.1965 +
  1.1966 +loser:
  1.1967 +    /* lots of stuff to do */
  1.1968 +    if (inTransaction) {
  1.1969 +	sqlite3_exec(sqlDB, ROLLBACK_CMD, NULL, 0, NULL);
  1.1970 +    }
  1.1971 +    if (sdb) {
  1.1972 +	free(sdb);
  1.1973 +    }
  1.1974 +    if (sdb_p) {
  1.1975 +	free(sdb_p);
  1.1976 +    }
  1.1977 +    if (sqlDB) {
  1.1978 +	sqlite3_close(sqlDB);
  1.1979 +    }
  1.1980 +    UNLOCK_SQLITE();
  1.1981 +    return error;
  1.1982 +
  1.1983 +}
  1.1984 +
  1.1985 +
  1.1986 +/* sdbopen */
  1.1987 +CK_RV
  1.1988 +s_open(const char *directory, const char *certPrefix, const char *keyPrefix,
  1.1989 +	int cert_version, int key_version, int flags, 
  1.1990 +	SDB **certdb, SDB **keydb, int *newInit)
  1.1991 +{
  1.1992 +    char *cert = sdb_BuildFileName(directory, certPrefix,
  1.1993 +				   "cert", cert_version);
  1.1994 +    char *key = sdb_BuildFileName(directory, keyPrefix,
  1.1995 +				   "key", key_version);
  1.1996 +    CK_RV error = CKR_OK;
  1.1997 +    int inUpdate;
  1.1998 +    PRUint32 accessOps;
  1.1999 +
  1.2000 +    if (certdb) 
  1.2001 +	*certdb = NULL;
  1.2002 +    if (keydb) 
  1.2003 +	*keydb = NULL;
  1.2004 +    *newInit = 0;
  1.2005 +
  1.2006 +#ifdef SQLITE_UNSAFE_THREADS
  1.2007 +    if (sqlite_lock == NULL) {
  1.2008 +	sqlite_lock = PR_NewLock();
  1.2009 +	if (sqlite_lock == NULL) {
  1.2010 +	    error = CKR_HOST_MEMORY;
  1.2011 +	    goto loser;
  1.2012 +	}
  1.2013 +    }
  1.2014 +#endif
  1.2015 +
  1.2016 +    /* how long does it take to test for a non-existant file in our working
  1.2017 +     * directory? Allows us to test if we may be on a network file system */
  1.2018 +    accessOps = 1;
  1.2019 +    {
  1.2020 +        char *env;
  1.2021 +        env = PR_GetEnv("NSS_SDB_USE_CACHE");
  1.2022 +        /* If the environment variable is set to yes or no, sdb_init() will
  1.2023 +         * ignore the value of accessOps, and we can skip the measuring.*/
  1.2024 +        if (!env || ((PORT_Strcasecmp(env, "no") != 0) &&
  1.2025 +                     (PORT_Strcasecmp(env, "yes") != 0))){
  1.2026 +           accessOps = sdb_measureAccess(directory);
  1.2027 +        }
  1.2028 +    }
  1.2029 +
  1.2030 +    /*
  1.2031 +     * open the cert data base
  1.2032 +     */
  1.2033 +    if (certdb) {
  1.2034 +	/* initialize Certificate database */
  1.2035 +	error = sdb_init(cert, "nssPublic", SDB_CERT, &inUpdate,
  1.2036 +			 newInit, flags, accessOps, certdb);
  1.2037 +	if (error != CKR_OK) {
  1.2038 +	    goto loser;
  1.2039 +	}
  1.2040 +    }
  1.2041 +
  1.2042 +    /*
  1.2043 +     * open the key data base: 
  1.2044 +     *  NOTE:if we want to implement a single database, we open
  1.2045 +     *  the same database file as the certificate here.
  1.2046 +     *
  1.2047 +     *  cert an key db's have different tables, so they will not
  1.2048 +     *  conflict.
  1.2049 +     */
  1.2050 +    if (keydb) {
  1.2051 +	/* initialize the Key database */
  1.2052 +	error = sdb_init(key, "nssPrivate", SDB_KEY, &inUpdate, 
  1.2053 +			newInit, flags, accessOps, keydb);
  1.2054 +	if (error != CKR_OK) {
  1.2055 +	    goto loser;
  1.2056 +	} 
  1.2057 +    }
  1.2058 +
  1.2059 +
  1.2060 +loser:
  1.2061 +    if (cert) {
  1.2062 +	sqlite3_free(cert);
  1.2063 +    }
  1.2064 +    if (key) {
  1.2065 +	sqlite3_free(key);
  1.2066 +    }
  1.2067 +
  1.2068 +    if (error != CKR_OK) {
  1.2069 +	/* currently redundant, but could be necessary if more code is added
  1.2070 +	 * just before loser */
  1.2071 +	if (keydb && *keydb) {
  1.2072 +	    sdb_Close(*keydb);
  1.2073 +	}
  1.2074 +	if (certdb && *certdb) {
  1.2075 +	    sdb_Close(*certdb);
  1.2076 +	}
  1.2077 +    }
  1.2078 +
  1.2079 +    return error;
  1.2080 +}
  1.2081 +
  1.2082 +CK_RV
  1.2083 +s_shutdown()
  1.2084 +{
  1.2085 +#ifdef SQLITE_UNSAFE_THREADS
  1.2086 +    if (sqlite_lock) {
  1.2087 +	PR_DestroyLock(sqlite_lock);
  1.2088 +	sqlite_lock = NULL;
  1.2089 +    }
  1.2090 +#endif
  1.2091 +    return CKR_OK;
  1.2092 +}

mercurial