Blame nss/lib/softoken/sdb.c

Packit 40b132
/* This Source Code Form is subject to the terms of the Mozilla Public
Packit 40b132
 * License, v. 2.0. If a copy of the MPL was not distributed with this
Packit 40b132
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
Packit 40b132
/*
Packit 40b132
 * This file implements PKCS 11 on top of our existing security modules
Packit 40b132
 *
Packit 40b132
 * For more information about PKCS 11 See PKCS 11 Token Inteface Standard.
Packit 40b132
 *   This implementation has two slots:
Packit 40b132
 *	slot 1 is our generic crypto support. It does not require login.
Packit 40b132
 *   It supports Public Key ops, and all they bulk ciphers and hashes. 
Packit 40b132
 *   It can also support Private Key ops for imported Private keys. It does 
Packit 40b132
 *   not have any token storage.
Packit 40b132
 *	slot 2 is our private key support. It requires a login before use. It
Packit 40b132
 *   can store Private Keys and Certs as token objects. Currently only private
Packit 40b132
 *   keys and their associated Certificates are saved on the token.
Packit 40b132
 *
Packit 40b132
 *   In this implementation, session objects are only visible to the session
Packit 40b132
 *   that created or generated them.
Packit 40b132
 */
Packit 40b132
Packit 40b132
#include "sdb.h"
Packit 40b132
#include "pkcs11t.h"
Packit 40b132
#include "seccomon.h"
Packit 40b132
#include <sqlite3.h>
Packit 40b132
#include "prthread.h"
Packit 40b132
#include "prio.h"
Packit 40b132
#include <stdio.h>
Packit 40b132
#include "secport.h"
Packit 40b132
#include "prmon.h"
Packit 40b132
#include "prenv.h"
Packit 40b132
#include "prprf.h"
Packit 40b132
#include "prsystem.h" /* for PR_GetDirectorySeparator() */
Packit 40b132
#include <sys/stat.h>
Packit 40b132
#if defined(_WIN32)
Packit 40b132
#include <io.h>
Packit 40b132
#include <windows.h>
Packit 40b132
#elif defined(XP_UNIX)
Packit 40b132
#include <unistd.h>
Packit 40b132
#endif
Packit 40b132
Packit 40b132
#ifdef SQLITE_UNSAFE_THREADS
Packit 40b132
#include "prlock.h"
Packit 40b132
/*
Packit 40b132
 * SQLite can be compiled to be thread safe or not.
Packit 40b132
 * turn on SQLITE_UNSAFE_THREADS if the OS does not support
Packit 40b132
 * a thread safe version of sqlite.
Packit 40b132
 */
Packit 40b132
static PRLock *sqlite_lock = NULL;
Packit 40b132
Packit 40b132
#define LOCK_SQLITE()  PR_Lock(sqlite_lock);
Packit 40b132
#define UNLOCK_SQLITE()  PR_Unlock(sqlite_lock);
Packit 40b132
#else
Packit 40b132
#define LOCK_SQLITE()  
Packit 40b132
#define UNLOCK_SQLITE()  
Packit 40b132
#endif
Packit 40b132
Packit 40b132
typedef enum {
Packit 40b132
	SDB_CERT = 1,
Packit 40b132
	SDB_KEY = 2
Packit 40b132
} sdbDataType;
Packit 40b132
Packit 40b132
/*
Packit 40b132
 * defines controlling how long we wait to acquire locks.
Packit 40b132
 *
Packit 40b132
 * SDB_SQLITE_BUSY_TIMEOUT specifies how long (in milliseconds)
Packit 40b132
 *  sqlite will wait on lock. If that timeout expires, sqlite will
Packit 40b132
 *  return SQLITE_BUSY.
Packit 40b132
 * SDB_BUSY_RETRY_TIME specifies how many seconds the sdb_ code waits
Packit 40b132
 *  after receiving a busy before retrying.
Packit 40b132
 * SDB_MAX_BUSY_RETRIES specifies how many times the sdb_ will retry on
Packit 40b132
 *  a busy condition.
Packit 40b132
 *
Packit 40b132
 * SDB_SQLITE_BUSY_TIMEOUT affects all opertions, both manual 
Packit 40b132
 *   (prepare/step/reset/finalize) and automatic (sqlite3_exec()).
Packit 40b132
 * SDB_BUSY_RETRY_TIME and SDB_MAX_BUSY_RETRIES only affect manual operations
Packit 40b132
 * 
Packit 40b132
 * total wait time for automatic operations: 
Packit 40b132
 *   1 second (SDB_SQLITE_BUSY_TIMEOUT/1000).
Packit 40b132
 * total wait time for manual operations: 
Packit 40b132
 *   (1 second + 5 seconds) * 10 = 60 seconds.
Packit 40b132
 * (SDB_SQLITE_BUSY_TIMEOUT/1000 + SDB_BUSY_RETRY_TIME)*SDB_MAX_BUSY_RETRIES
Packit 40b132
 */
Packit 40b132
#define SDB_SQLITE_BUSY_TIMEOUT 1000 /* milliseconds */
Packit 40b132
#define SDB_BUSY_RETRY_TIME        5 /* seconds */
Packit 40b132
#define SDB_MAX_BUSY_RETRIES      10
Packit 40b132
Packit 40b132
/*
Packit 40b132
 * Note on use of sqlReadDB: Only one thread at a time may have an actual
Packit 40b132
 * operation going on given sqlite3 * database. An operation is defined as
Packit 40b132
 * the time from a sqlite3_prepare() until the sqlite3_finalize().
Packit 40b132
 * Multiple sqlite3 * databases can be open and have simultaneous operations
Packit 40b132
 * going. We use the sqlXactDB for all write operations. This database
Packit 40b132
 * is only opened when we first create a transaction and closed when the
Packit 40b132
 * transaction is complete. sqlReadDB is open when we first opened the database
Packit 40b132
 * and is used for all read operation. It's use is protected by a monitor. This
Packit 40b132
 * is because an operation can span the use of FindObjectsInit() through the
Packit 40b132
 * call to FindObjectsFinal(). In the intermediate time it is possible to call
Packit 40b132
 * other operations like NSC_GetAttributeValue */
Packit 40b132
Packit 40b132
struct SDBPrivateStr {
Packit 40b132
    char *sqlDBName;		/* invariant, path to this database */
Packit 40b132
    sqlite3 *sqlXactDB;		/* access protected by dbMon, use protected
Packit 40b132
                                 * by the transaction. Current transaction db*/
Packit 40b132
    PRThread *sqlXactThread;	/* protected by dbMon,
Packit 40b132
			         * current transaction thread */
Packit 40b132
    sqlite3 *sqlReadDB;		/* use protected by dbMon, value invariant */
Packit 40b132
    PRIntervalTime lastUpdateTime;  /* last time the cache was updated */
Packit 40b132
    PRIntervalTime updateInterval;  /* how long the cache can go before it 
Packit 40b132
                                     * must be updated again */
Packit 40b132
    sdbDataType type;		/* invariant, database type */
Packit 40b132
    char *table;	        /* invariant, SQL table which contains the db */
Packit 40b132
    char *cacheTable;	        /* invariant, SQL table cache of db */
Packit 40b132
    PRMonitor *dbMon;		/* invariant, monitor to protect 
Packit 40b132
				 * sqlXact* fields, and use of the sqlReadDB */
Packit 40b132
};
Packit 40b132
Packit 40b132
typedef struct SDBPrivateStr SDBPrivate;
Packit 40b132
Packit 40b132
/*
Packit 40b132
 * known attributes
Packit 40b132
 */
Packit 40b132
static const CK_ATTRIBUTE_TYPE known_attributes[] = {
Packit 40b132
    CKA_CLASS, CKA_TOKEN, CKA_PRIVATE, CKA_LABEL, CKA_APPLICATION,
Packit 40b132
    CKA_VALUE, CKA_OBJECT_ID, CKA_CERTIFICATE_TYPE, CKA_ISSUER,
Packit 40b132
    CKA_SERIAL_NUMBER, CKA_AC_ISSUER, CKA_OWNER, CKA_ATTR_TYPES, CKA_TRUSTED,
Packit 40b132
    CKA_CERTIFICATE_CATEGORY, CKA_JAVA_MIDP_SECURITY_DOMAIN, CKA_URL,
Packit 40b132
    CKA_HASH_OF_SUBJECT_PUBLIC_KEY, CKA_HASH_OF_ISSUER_PUBLIC_KEY,
Packit 40b132
    CKA_CHECK_VALUE, CKA_KEY_TYPE, CKA_SUBJECT, CKA_ID, CKA_SENSITIVE,
Packit 40b132
    CKA_ENCRYPT, CKA_DECRYPT, CKA_WRAP, CKA_UNWRAP, CKA_SIGN, CKA_SIGN_RECOVER,
Packit 40b132
    CKA_VERIFY, CKA_VERIFY_RECOVER, CKA_DERIVE, CKA_START_DATE, CKA_END_DATE,
Packit 40b132
    CKA_MODULUS, CKA_MODULUS_BITS, CKA_PUBLIC_EXPONENT, CKA_PRIVATE_EXPONENT,
Packit 40b132
    CKA_PRIME_1, CKA_PRIME_2, CKA_EXPONENT_1, CKA_EXPONENT_2, CKA_COEFFICIENT,
Packit 40b132
    CKA_PRIME, CKA_SUBPRIME, CKA_BASE, CKA_PRIME_BITS, 
Packit 40b132
    CKA_SUB_PRIME_BITS, CKA_VALUE_BITS, CKA_VALUE_LEN, CKA_EXTRACTABLE,
Packit 40b132
    CKA_LOCAL, CKA_NEVER_EXTRACTABLE, CKA_ALWAYS_SENSITIVE,
Packit 40b132
    CKA_KEY_GEN_MECHANISM, CKA_MODIFIABLE, CKA_EC_PARAMS,
Packit 40b132
    CKA_EC_POINT, CKA_SECONDARY_AUTH, CKA_AUTH_PIN_FLAGS,
Packit 40b132
    CKA_ALWAYS_AUTHENTICATE, CKA_WRAP_WITH_TRUSTED, CKA_WRAP_TEMPLATE,
Packit 40b132
    CKA_UNWRAP_TEMPLATE, CKA_HW_FEATURE_TYPE, CKA_RESET_ON_INIT,
Packit 40b132
    CKA_HAS_RESET, CKA_PIXEL_X, CKA_PIXEL_Y, CKA_RESOLUTION, CKA_CHAR_ROWS,
Packit 40b132
    CKA_CHAR_COLUMNS, CKA_COLOR, CKA_BITS_PER_PIXEL, CKA_CHAR_SETS,
Packit 40b132
    CKA_ENCODING_METHODS, CKA_MIME_TYPES, CKA_MECHANISM_TYPE,
Packit 40b132
    CKA_REQUIRED_CMS_ATTRIBUTES, CKA_DEFAULT_CMS_ATTRIBUTES,
Packit 40b132
    CKA_SUPPORTED_CMS_ATTRIBUTES, CKA_NETSCAPE_URL, CKA_NETSCAPE_EMAIL,
Packit 40b132
    CKA_NETSCAPE_SMIME_INFO, CKA_NETSCAPE_SMIME_TIMESTAMP,
Packit 40b132
    CKA_NETSCAPE_PKCS8_SALT, CKA_NETSCAPE_PASSWORD_CHECK, CKA_NETSCAPE_EXPIRES,
Packit 40b132
    CKA_NETSCAPE_KRL, CKA_NETSCAPE_PQG_COUNTER, CKA_NETSCAPE_PQG_SEED,
Packit 40b132
    CKA_NETSCAPE_PQG_H, CKA_NETSCAPE_PQG_SEED_BITS, CKA_NETSCAPE_MODULE_SPEC,
Packit 40b132
    CKA_TRUST_DIGITAL_SIGNATURE, CKA_TRUST_NON_REPUDIATION,
Packit 40b132
    CKA_TRUST_KEY_ENCIPHERMENT, CKA_TRUST_DATA_ENCIPHERMENT,
Packit 40b132
    CKA_TRUST_KEY_AGREEMENT, CKA_TRUST_KEY_CERT_SIGN, CKA_TRUST_CRL_SIGN,
Packit 40b132
    CKA_TRUST_SERVER_AUTH, CKA_TRUST_CLIENT_AUTH, CKA_TRUST_CODE_SIGNING,
Packit 40b132
    CKA_TRUST_EMAIL_PROTECTION, CKA_TRUST_IPSEC_END_SYSTEM,
Packit 40b132
    CKA_TRUST_IPSEC_TUNNEL, CKA_TRUST_IPSEC_USER, CKA_TRUST_TIME_STAMPING,
Packit 40b132
    CKA_TRUST_STEP_UP_APPROVED, CKA_CERT_SHA1_HASH, CKA_CERT_MD5_HASH,
Packit 40b132
    CKA_NETSCAPE_DB, CKA_NETSCAPE_TRUST, CKA_NSS_OVERRIDE_EXTENSIONS
Packit 40b132
};
Packit 40b132
Packit 40b132
static int known_attributes_size= sizeof(known_attributes)/
Packit 40b132
			   sizeof(known_attributes[0]);
Packit 40b132
Packit 40b132
/* Magic for an explicit NULL. NOTE: ideally this should be
Packit 40b132
 * out of band data. Since it's not completely out of band, pick
Packit 40b132
 * a value that has no meaning to any existing PKCS #11 attributes.
Packit 40b132
 * This value is 1) not a valid string (imbedded '\0'). 2) not a U_LONG
Packit 40b132
 * or a normal key (too short). 3) not a bool (too long). 4) not an RSA
Packit 40b132
 * public exponent (too many bits).
Packit 40b132
 */
Packit 40b132
const unsigned char SQLITE_EXPLICIT_NULL[] = { 0xa5, 0x0, 0x5a };
Packit 40b132
#define SQLITE_EXPLICIT_NULL_LEN 3
Packit 40b132
Packit 40b132
/*
Packit 40b132
 * determine when we've completed our tasks
Packit 40b132
 */
Packit 40b132
static int 
Packit 40b132
sdb_done(int err, int *count)
Packit 40b132
{
Packit 40b132
    /* allow as many rows as the database wants to give */
Packit 40b132
    if (err == SQLITE_ROW) {
Packit 40b132
	*count = 0;
Packit 40b132
	return 0;
Packit 40b132
    }
Packit 40b132
    if (err != SQLITE_BUSY) {
Packit 40b132
	return 1;
Packit 40b132
    }
Packit 40b132
    /* err == SQLITE_BUSY, Dont' retry forever in this case */
Packit 40b132
    if (++(*count) >= SDB_MAX_BUSY_RETRIES) {
Packit 40b132
	return 1;
Packit 40b132
    }
Packit 40b132
    return 0;
Packit 40b132
}
Packit 40b132
Packit 40b132
/*
Packit 40b132
 * find out where sqlite stores the temp tables. We do this by replicating
Packit 40b132
 * the logic from sqlite.
Packit 40b132
 */
Packit 40b132
#if defined(_WIN32)
Packit 40b132
static char *
Packit 40b132
sdb_getFallbackTempDir(void)
Packit 40b132
{
Packit 40b132
    /* sqlite uses sqlite3_temp_directory if it is not NULL. We don't have
Packit 40b132
     * access to sqlite3_temp_directory because it is not exported from
Packit 40b132
     * sqlite3.dll. Assume sqlite3_win32_set_directory isn't called and
Packit 40b132
     * sqlite3_temp_directory is NULL.
Packit 40b132
     */
Packit 40b132
    char path[MAX_PATH];
Packit 40b132
    DWORD rv;
Packit 40b132
    size_t len;
Packit 40b132
Packit 40b132
    rv = GetTempPathA(MAX_PATH, path);
Packit 40b132
    if (rv > MAX_PATH || rv == 0)
Packit 40b132
        return NULL;
Packit 40b132
    len = strlen(path);
Packit 40b132
    if (len == 0)
Packit 40b132
        return NULL;
Packit 40b132
    /* The returned string ends with a backslash, for example, "C:\TEMP\". */
Packit 40b132
    if (path[len - 1] == '\\')
Packit 40b132
        path[len - 1] = '\0';
Packit 40b132
    return PORT_Strdup(path);
Packit 40b132
}
Packit 40b132
#elif defined(XP_UNIX)
Packit 40b132
static char *
Packit 40b132
sdb_getFallbackTempDir(void)
Packit 40b132
{
Packit 40b132
    const char *azDirs[] = {
Packit 40b132
        NULL,
Packit 40b132
        NULL,
Packit 40b132
        "/var/tmp",
Packit 40b132
        "/usr/tmp",
Packit 40b132
        "/tmp",
Packit 40b132
        NULL     /* List terminator */
Packit 40b132
    };
Packit 40b132
    unsigned int i;
Packit 40b132
    struct stat buf;
Packit 40b132
    const char *zDir = NULL;
Packit 40b132
Packit 40b132
    azDirs[0] = sqlite3_temp_directory;
Packit 40b132
    azDirs[1] = getenv("TMPDIR");
Packit 40b132
Packit 40b132
    for (i = 0; i < PR_ARRAY_SIZE(azDirs); i++) {
Packit 40b132
        zDir = azDirs[i];
Packit 40b132
        if (zDir == NULL) continue;
Packit 40b132
        if (stat(zDir, &buf)) continue;
Packit 40b132
        if (!S_ISDIR(buf.st_mode)) continue;
Packit 40b132
        if (access(zDir, 07)) continue;
Packit 40b132
        break;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    if (zDir == NULL)
Packit 40b132
        return NULL;
Packit 40b132
    return PORT_Strdup(zDir);
Packit 40b132
}
Packit 40b132
#else
Packit 40b132
#error "sdb_getFallbackTempDir not implemented"
Packit 40b132
#endif
Packit 40b132
Packit 40b132
#ifndef SQLITE_FCNTL_TEMPFILENAME
Packit 40b132
/* SQLITE_FCNTL_TEMPFILENAME was added in SQLite 3.7.15 */
Packit 40b132
#define SQLITE_FCNTL_TEMPFILENAME 16
Packit 40b132
#endif
Packit 40b132
Packit 40b132
static char *
Packit 40b132
sdb_getTempDir(sqlite3 *sqlDB)
Packit 40b132
{
Packit 40b132
    int sqlrv;
Packit 40b132
    char *result = NULL;
Packit 40b132
    char *tempName = NULL;
Packit 40b132
    char *foundSeparator = NULL;
Packit 40b132
Packit 40b132
    /* Obtain temporary filename in sqlite's directory for temporary tables */
Packit 40b132
    sqlrv = sqlite3_file_control(sqlDB, 0, SQLITE_FCNTL_TEMPFILENAME,
Packit 40b132
				 (void*)&tempName);
Packit 40b132
    if (sqlrv == SQLITE_NOTFOUND) {
Packit 40b132
	/* SQLITE_FCNTL_TEMPFILENAME not implemented because we are using
Packit 40b132
	 * an older SQLite. */
Packit 40b132
	return sdb_getFallbackTempDir();
Packit 40b132
    }
Packit 40b132
    if (sqlrv != SQLITE_OK) {
Packit 40b132
	return NULL;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    /* We'll extract the temporary directory from tempName */
Packit 40b132
    foundSeparator = PORT_Strrchr(tempName, PR_GetDirectorySeparator());
Packit 40b132
    if (foundSeparator) {
Packit 40b132
	/* We shorten the temp filename string to contain only
Packit 40b132
	  * the directory name (including the trailing separator).
Packit 40b132
	  * We know the byte after the foundSeparator position is
Packit 40b132
	  * safe to use, in the shortest scenario it contains the
Packit 40b132
	  * end-of-string byte.
Packit 40b132
	  * By keeping the separator at the found position, it will
Packit 40b132
	  * even work if tempDir consists of the separator, only.
Packit 40b132
	  * (In this case the toplevel directory will be used for
Packit 40b132
	  * access speed testing). */
Packit 40b132
	++foundSeparator;
Packit 40b132
	*foundSeparator = 0;
Packit 40b132
Packit 40b132
	/* Now we copy the directory name for our caller */
Packit 40b132
	result = PORT_Strdup(tempName);
Packit 40b132
    }
Packit 40b132
Packit 40b132
    sqlite3_free(tempName);
Packit 40b132
    return result;
Packit 40b132
}
Packit 40b132
Packit 40b132
/*
Packit 40b132
 * Map SQL_LITE errors to PKCS #11 errors as best we can.
Packit 40b132
 */
Packit 40b132
static CK_RV
Packit 40b132
sdb_mapSQLError(sdbDataType type, int sqlerr)
Packit 40b132
{
Packit 40b132
    switch (sqlerr) {
Packit 40b132
    /* good matches */
Packit 40b132
    case SQLITE_OK:
Packit 40b132
    case SQLITE_DONE:
Packit 40b132
	return CKR_OK;
Packit 40b132
    case SQLITE_NOMEM:
Packit 40b132
	return CKR_HOST_MEMORY;
Packit 40b132
    case SQLITE_READONLY:
Packit 40b132
	return CKR_TOKEN_WRITE_PROTECTED;
Packit 40b132
    /* close matches */
Packit 40b132
    case SQLITE_AUTH:
Packit 40b132
    case SQLITE_PERM:
Packit 40b132
	/*return CKR_USER_NOT_LOGGED_IN; */
Packit 40b132
    case SQLITE_CANTOPEN:
Packit 40b132
    case SQLITE_NOTFOUND:
Packit 40b132
	/* NSS distiguishes between failure to open the cert and the key db */
Packit 40b132
	return type == SDB_CERT ? 
Packit 40b132
		CKR_NETSCAPE_CERTDB_FAILED : CKR_NETSCAPE_KEYDB_FAILED;
Packit 40b132
    case SQLITE_IOERR:
Packit 40b132
	return CKR_DEVICE_ERROR;
Packit 40b132
    default:
Packit 40b132
	break;
Packit 40b132
    }
Packit 40b132
    return CKR_GENERAL_ERROR;
Packit 40b132
}
Packit 40b132
Packit 40b132
Packit 40b132
/*
Packit 40b132
 * build up database name from a directory, prefix, name, version and flags.
Packit 40b132
 */
Packit 40b132
static char *sdb_BuildFileName(const char * directory, 
Packit 40b132
			const char *prefix, const char *type, 
Packit 40b132
			int version)
Packit 40b132
{
Packit 40b132
    char *dbname = NULL;
Packit 40b132
    /* build the full dbname */
Packit 40b132
    dbname = sqlite3_mprintf("%s%c%s%s%d.db", directory,
Packit 40b132
			     (int)(unsigned char)PR_GetDirectorySeparator(),
Packit 40b132
			     prefix, type, version);
Packit 40b132
    return dbname;
Packit 40b132
}
Packit 40b132
Packit 40b132
Packit 40b132
/*
Packit 40b132
 * find out how expensive the access system call is for non-existant files
Packit 40b132
 * in the given directory.  Return the number of operations done in 33 ms.
Packit 40b132
 */
Packit 40b132
static PRUint32
Packit 40b132
sdb_measureAccess(const char *directory)
Packit 40b132
{
Packit 40b132
    PRUint32 i;
Packit 40b132
    PRIntervalTime time;
Packit 40b132
    PRIntervalTime delta;
Packit 40b132
    PRIntervalTime duration = PR_MillisecondsToInterval(33);
Packit 40b132
    const char *doesntExistName = "_dOeSnotExist_.db";
Packit 40b132
    char *temp, *tempStartOfFilename;
Packit 40b132
    size_t maxTempLen, maxFileNameLen, directoryLength;
Packit 40b132
Packit 40b132
    /* no directory, just return one */
Packit 40b132
    if (directory == NULL) {
Packit 40b132
	return 1;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    /* our calculation assumes time is a 4 bytes == 32 bit integer */
Packit 40b132
    PORT_Assert(sizeof(time) == 4);
Packit 40b132
Packit 40b132
    directoryLength = strlen(directory);
Packit 40b132
Packit 40b132
    maxTempLen = directoryLength + strlen(doesntExistName)
Packit 40b132
		 + 1 /* potential additional separator char */
Packit 40b132
		 + 11 /* max chars for 32 bit int plus potential sign */
Packit 40b132
		 + 1; /* zero terminator */
Packit 40b132
Packit 40b132
    temp = PORT_Alloc(maxTempLen);
Packit 40b132
    if (!temp) {
Packit 40b132
        return 1;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    /* We'll copy directory into temp just once, then ensure it ends
Packit 40b132
     * with the directory separator, then remember the position after
Packit 40b132
     * the separator, and calculate the number of remaining bytes. */
Packit 40b132
Packit 40b132
    strcpy(temp, directory);
Packit 40b132
    if (directory[directoryLength - 1] != PR_GetDirectorySeparator()) {
Packit 40b132
	temp[directoryLength++] = PR_GetDirectorySeparator();
Packit 40b132
    }
Packit 40b132
    tempStartOfFilename = temp + directoryLength;
Packit 40b132
    maxFileNameLen = maxTempLen - directoryLength;
Packit 40b132
Packit 40b132
    /* measure number of Access operations that can be done in 33 milliseconds
Packit 40b132
     * (1/30'th of a second), or 10000 operations, which ever comes first.
Packit 40b132
     */
Packit 40b132
    time =  PR_IntervalNow();
Packit 40b132
    for (i=0; i < 10000u; i++) { 
Packit 40b132
	PRIntervalTime next;
Packit 40b132
Packit 40b132
	/* We'll use the variable part first in the filename string, just in
Packit 40b132
	 * case it's longer than assumed, so if anything gets cut off, it
Packit 40b132
	 * will be cut off from the constant part.
Packit 40b132
	 * This code assumes the directory name at the beginning of
Packit 40b132
	 * temp remains unchanged during our loop. */
Packit 40b132
        PR_snprintf(tempStartOfFilename, maxFileNameLen,
Packit 40b132
		    ".%lu%s", (PRUint32)(time+i), doesntExistName);
Packit 40b132
	PR_Access(temp,PR_ACCESS_EXISTS);
Packit 40b132
	next = PR_IntervalNow();
Packit 40b132
	delta = next - time;
Packit 40b132
	if (delta >= duration)
Packit 40b132
	    break;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    PORT_Free(temp);
Packit 40b132
Packit 40b132
    /* always return 1 or greater */
Packit 40b132
    return i ? i : 1u;
Packit 40b132
}
Packit 40b132
Packit 40b132
/*
Packit 40b132
 * some file sytems are very slow to run sqlite3 on, particularly if the
Packit 40b132
 * access count is pretty high. On these filesystems is faster to create
Packit 40b132
 * a temporary database on the local filesystem and access that. This
Packit 40b132
 * code uses a temporary table to create that cache. Temp tables are
Packit 40b132
 * automatically cleared when the database handle it was created on
Packit 40b132
 * Is freed.
Packit 40b132
 */
Packit 40b132
static const char DROP_CACHE_CMD[] = "DROP TABLE %s";
Packit 40b132
static const char CREATE_CACHE_CMD[] =
Packit 40b132
  "CREATE TEMPORARY TABLE %s AS SELECT * FROM %s";
Packit 40b132
static const char CREATE_ISSUER_INDEX_CMD[] = 
Packit 40b132
  "CREATE INDEX issuer ON %s (a81)";
Packit 40b132
static const char CREATE_SUBJECT_INDEX_CMD[] = 
Packit 40b132
  "CREATE INDEX subject ON %s (a101)";
Packit 40b132
static const char CREATE_LABEL_INDEX_CMD[] = "CREATE INDEX label ON %s (a3)";
Packit 40b132
static const char CREATE_ID_INDEX_CMD[] = "CREATE INDEX ckaid ON %s (a102)";
Packit 40b132
Packit 40b132
static CK_RV
Packit 40b132
sdb_buildCache(sqlite3 *sqlDB, sdbDataType type, 
Packit 40b132
		const char *cacheTable, const char *table)
Packit 40b132
{
Packit 40b132
    char *newStr;
Packit 40b132
    int sqlerr = SQLITE_OK;
Packit 40b132
Packit 40b132
    newStr = sqlite3_mprintf(CREATE_CACHE_CMD, cacheTable, table);
Packit 40b132
    if (newStr == NULL) {
Packit 40b132
	return CKR_HOST_MEMORY;
Packit 40b132
    }
Packit 40b132
    sqlerr = sqlite3_exec(sqlDB, newStr, NULL, 0, NULL);
Packit 40b132
    sqlite3_free(newStr);
Packit 40b132
    if (sqlerr != SQLITE_OK) {
Packit 40b132
	return sdb_mapSQLError(type, sqlerr); 
Packit 40b132
    }
Packit 40b132
    /* failure to create the indexes is not an issue */
Packit 40b132
    newStr = sqlite3_mprintf(CREATE_ISSUER_INDEX_CMD, cacheTable);
Packit 40b132
    if (newStr == NULL) {
Packit 40b132
	return CKR_OK;
Packit 40b132
    }
Packit 40b132
    sqlerr = sqlite3_exec(sqlDB, newStr, NULL, 0, NULL);
Packit 40b132
    sqlite3_free(newStr);
Packit 40b132
    newStr = sqlite3_mprintf(CREATE_SUBJECT_INDEX_CMD, cacheTable);
Packit 40b132
    if (newStr == NULL) {
Packit 40b132
	return CKR_OK;
Packit 40b132
    }
Packit 40b132
    sqlerr = sqlite3_exec(sqlDB, newStr, NULL, 0, NULL);
Packit 40b132
    sqlite3_free(newStr);
Packit 40b132
    newStr = sqlite3_mprintf(CREATE_LABEL_INDEX_CMD, cacheTable);
Packit 40b132
    if (newStr == NULL) {
Packit 40b132
	return CKR_OK;
Packit 40b132
    }
Packit 40b132
    sqlerr = sqlite3_exec(sqlDB, newStr, NULL, 0, NULL);
Packit 40b132
    sqlite3_free(newStr);
Packit 40b132
    newStr = sqlite3_mprintf(CREATE_ID_INDEX_CMD, cacheTable);
Packit 40b132
    if (newStr == NULL) {
Packit 40b132
	return CKR_OK;
Packit 40b132
    }
Packit 40b132
    sqlerr = sqlite3_exec(sqlDB, newStr, NULL, 0, NULL);
Packit 40b132
    sqlite3_free(newStr);
Packit 40b132
    return CKR_OK;
Packit 40b132
}
Packit 40b132
Packit 40b132
/*
Packit 40b132
 * update the cache and the data records describing it.
Packit 40b132
 *  The cache is updated by dropping the temp database and recreating it.
Packit 40b132
 */
Packit 40b132
static CK_RV
Packit 40b132
sdb_updateCache(SDBPrivate *sdb_p)
Packit 40b132
{
Packit 40b132
    int sqlerr = SQLITE_OK;
Packit 40b132
    CK_RV error = CKR_OK;
Packit 40b132
    char *newStr;
Packit 40b132
Packit 40b132
    /* drop the old table */
Packit 40b132
    newStr = sqlite3_mprintf(DROP_CACHE_CMD, sdb_p->cacheTable);
Packit 40b132
    if (newStr == NULL) {
Packit 40b132
	return CKR_HOST_MEMORY;
Packit 40b132
    }
Packit 40b132
    sqlerr = sqlite3_exec(sdb_p->sqlReadDB, newStr, NULL, 0, NULL);
Packit 40b132
    sqlite3_free(newStr);
Packit 40b132
    if ((sqlerr != SQLITE_OK) && (sqlerr != SQLITE_ERROR )) {
Packit 40b132
        /* something went wrong with the drop, don't try to refresh...
Packit 40b132
         * NOTE: SQLITE_ERROR is returned if the table doesn't exist. In
Packit 40b132
         * that case, we just continue on and try to reload it */
Packit 40b132
	return sdb_mapSQLError(sdb_p->type, sqlerr); 
Packit 40b132
    }
Packit 40b132
	
Packit 40b132
Packit 40b132
    /* set up the new table */
Packit 40b132
    error = sdb_buildCache(sdb_p->sqlReadDB,sdb_p->type,
Packit 40b132
				sdb_p->cacheTable,sdb_p->table );
Packit 40b132
    if (error == CKR_OK) {
Packit 40b132
	/* we have a new cache! */
Packit 40b132
	sdb_p->lastUpdateTime = PR_IntervalNow();
Packit 40b132
    }
Packit 40b132
    return error;
Packit 40b132
}
Packit 40b132
Packit 40b132
/*
Packit 40b132
 *  The sharing of sqlite3 handles across threads is tricky. Older versions
Packit 40b132
 *  couldn't at all, but newer ones can under strict conditions. Basically
Packit 40b132
 *  no 2 threads can use the same handle while another thread has an open
Packit 40b132
 *  stmt running. Once the sqlite3_stmt is finalized, another thread can then
Packit 40b132
 *  use the database handle.
Packit 40b132
 *
Packit 40b132
 *  We use monitors to protect against trying to use a database before
Packit 40b132
 *  it's sqlite3_stmt is finalized. This is preferable to the opening and
Packit 40b132
 *  closing the database each operation because there is significant overhead
Packit 40b132
 *  in the open and close. Also continually opening and closing the database
Packit 40b132
 *  defeats the cache code as the cache table is lost on close (thus
Packit 40b132
 *  requiring us to have to reinitialize the cache every operation).
Packit 40b132
 * 
Packit 40b132
 *  An execption to the shared handle is transations. All writes happen
Packit 40b132
 *  through a transaction. When we are in  a transaction, we must use the 
Packit 40b132
 *  same database pointer for that entire transation. In this case we save 
Packit 40b132
 *  the transaction database and use it for all accesses on the transaction 
Packit 40b132
 *  thread. Other threads use the common database.  
Packit 40b132
 *
Packit 40b132
 *  There can only be once active transaction on the database at a time.
Packit 40b132
 *
Packit 40b132
 *  sdb_openDBLocal() provides us with a valid database handle for whatever
Packit 40b132
 *  state we are in (reading or in a transaction), and acquires any locks
Packit 40b132
 *  appropriate to that state. It also decides when it's time to refresh
Packit 40b132
 *  the cache before we start an operation. Any database handle returned
Packit 40b132
 *  just eventually be closed with sdb_closeDBLocal().
Packit 40b132
 *
Packit 40b132
 *  The table returned either points to the database's physical table, or
Packit 40b132
 *  to the cached shadow. Tranactions always return the physical table
Packit 40b132
 *  and read operations return either the physical table or the cache
Packit 40b132
 *  depending on whether or not the cache exists.
Packit 40b132
 */
Packit 40b132
static CK_RV 
Packit 40b132
sdb_openDBLocal(SDBPrivate *sdb_p, sqlite3 **sqlDB, const char **table)
Packit 40b132
{
Packit 40b132
    *sqlDB = NULL;
Packit 40b132
Packit 40b132
    PR_EnterMonitor(sdb_p->dbMon);
Packit 40b132
Packit 40b132
    if (table) {
Packit 40b132
	*table = sdb_p->table;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    /* We're in a transaction, use the transaction DB */
Packit 40b132
    if ((sdb_p->sqlXactDB) && (sdb_p->sqlXactThread == PR_GetCurrentThread())) {
Packit 40b132
	*sqlDB =sdb_p->sqlXactDB;
Packit 40b132
	/* only one thread can get here, safe to unlock */
Packit 40b132
        PR_ExitMonitor(sdb_p->dbMon);
Packit 40b132
	return CKR_OK;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    /*
Packit 40b132
     * if we are just reading from the table, we may have the table
Packit 40b132
     * cached in a temporary table (especially if it's on a shared FS).
Packit 40b132
     * In that case we want to see updates to the table, the the granularity
Packit 40b132
     * is on order of human scale, not computer scale.
Packit 40b132
     */
Packit 40b132
    if (table && sdb_p->cacheTable) {
Packit 40b132
	PRIntervalTime now = PR_IntervalNow();
Packit 40b132
	if ((now - sdb_p->lastUpdateTime) > sdb_p->updateInterval) {
Packit 40b132
	       sdb_updateCache(sdb_p);
Packit 40b132
        }
Packit 40b132
	*table = sdb_p->cacheTable;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    *sqlDB = sdb_p->sqlReadDB;
Packit 40b132
Packit 40b132
    /* leave holding the lock. only one thread can actually use a given
Packit 40b132
     * database connection at once */
Packit 40b132
	
Packit 40b132
    return CKR_OK;
Packit 40b132
}
Packit 40b132
Packit 40b132
/* closing the local database currenly means unlocking the monitor */
Packit 40b132
static CK_RV 
Packit 40b132
sdb_closeDBLocal(SDBPrivate *sdb_p, sqlite3 *sqlDB) 
Packit 40b132
{
Packit 40b132
   if (sdb_p->sqlXactDB != sqlDB) {
Packit 40b132
	/* if we weren't in a transaction, we got a lock */
Packit 40b132
        PR_ExitMonitor(sdb_p->dbMon);
Packit 40b132
   }
Packit 40b132
   return CKR_OK;
Packit 40b132
}
Packit 40b132
Packit 40b132
Packit 40b132
/*
Packit 40b132
 * wrapper to sqlite3_open which also sets the busy_timeout
Packit 40b132
 */
Packit 40b132
static int
Packit 40b132
sdb_openDB(const char *name, sqlite3 **sqlDB, int flags)
Packit 40b132
{
Packit 40b132
    int sqlerr;
Packit 40b132
    /*
Packit 40b132
     * in sqlite3 3.5.0, there is a new open call that allows us
Packit 40b132
     * to specify read only. Most new OS's are still on 3.3.x (including
Packit 40b132
     * NSS's internal version and the version shipped with Firefox).
Packit 40b132
     */
Packit 40b132
    *sqlDB = NULL;
Packit 40b132
    sqlerr = sqlite3_open(name, sqlDB);
Packit 40b132
    if (sqlerr != SQLITE_OK) {
Packit 40b132
	return sqlerr;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    sqlerr = sqlite3_busy_timeout(*sqlDB, SDB_SQLITE_BUSY_TIMEOUT);
Packit 40b132
    if (sqlerr != SQLITE_OK) {
Packit 40b132
	sqlite3_close(*sqlDB);
Packit 40b132
	*sqlDB = NULL;
Packit 40b132
	return sqlerr;
Packit 40b132
    }
Packit 40b132
    return SQLITE_OK;
Packit 40b132
}
Packit 40b132
Packit 40b132
/* Sigh, if we created a new table since we opened the database,
Packit 40b132
 * the database handle will not see the new table, we need to close this
Packit 40b132
 * database and reopen it. Caller must be in a transaction or holding
Packit 40b132
 * the dbMon. sqlDB is changed on success. */
Packit 40b132
static int 
Packit 40b132
sdb_reopenDBLocal(SDBPrivate *sdb_p, sqlite3 **sqlDB) {
Packit 40b132
    sqlite3 *newDB;
Packit 40b132
    int sqlerr;
Packit 40b132
Packit 40b132
    /* open a new database */
Packit 40b132
    sqlerr = sdb_openDB(sdb_p->sqlDBName, &newDB, SDB_RDONLY);
Packit 40b132
    if (sqlerr != SQLITE_OK) {
Packit 40b132
	return sqlerr;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    /* if we are in a transaction, we may not be holding the monitor.
Packit 40b132
     * grab it before we update the transaction database. This is
Packit 40b132
     * safe since are using monitors. */
Packit 40b132
    PR_EnterMonitor(sdb_p->dbMon);
Packit 40b132
    /* update our view of the database */
Packit 40b132
    if (sdb_p->sqlReadDB == *sqlDB) {
Packit 40b132
	sdb_p->sqlReadDB = newDB;
Packit 40b132
    } else if (sdb_p->sqlXactDB == *sqlDB) {
Packit 40b132
	sdb_p->sqlXactDB = newDB;
Packit 40b132
    }
Packit 40b132
    PR_ExitMonitor(sdb_p->dbMon);
Packit 40b132
Packit 40b132
    /* close the old one */
Packit 40b132
    sqlite3_close(*sqlDB);
Packit 40b132
Packit 40b132
    *sqlDB = newDB;
Packit 40b132
    return SQLITE_OK;
Packit 40b132
}
Packit 40b132
Packit 40b132
struct SDBFindStr {
Packit 40b132
    sqlite3 *sqlDB;
Packit 40b132
    sqlite3_stmt *findstmt;
Packit 40b132
};
Packit 40b132
Packit 40b132
Packit 40b132
static const char FIND_OBJECTS_CMD[] =  "SELECT ALL * FROM %s WHERE %s;";
Packit 40b132
static const char FIND_OBJECTS_ALL_CMD[] = "SELECT ALL * FROM %s;";
Packit 40b132
CK_RV
Packit 40b132
sdb_FindObjectsInit(SDB *sdb, const CK_ATTRIBUTE *template, CK_ULONG count, 
Packit 40b132
				SDBFind **find)
Packit 40b132
{
Packit 40b132
    SDBPrivate *sdb_p = sdb->private;
Packit 40b132
    sqlite3  *sqlDB = NULL;
Packit 40b132
    const char *table;
Packit 40b132
    char *newStr, *findStr = NULL;
Packit 40b132
    sqlite3_stmt *findstmt = NULL;
Packit 40b132
    char *join="";
Packit 40b132
    int sqlerr = SQLITE_OK;
Packit 40b132
    CK_RV error = CKR_OK;
Packit 40b132
    int i;
Packit 40b132
Packit 40b132
    LOCK_SQLITE()
Packit 40b132
    *find = NULL;
Packit 40b132
    error = sdb_openDBLocal(sdb_p, &sqlDB, &table);
Packit 40b132
    if (error != CKR_OK) {
Packit 40b132
	goto loser;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    findStr = sqlite3_mprintf("");
Packit 40b132
    for (i=0; findStr && i < count; i++) {
Packit 40b132
	newStr = sqlite3_mprintf("%s%sa%x=$DATA%d", findStr, join,
Packit 40b132
				template[i].type, i);
Packit 40b132
        join=" AND ";
Packit 40b132
	sqlite3_free(findStr);
Packit 40b132
	findStr = newStr;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    if (findStr == NULL) {
Packit 40b132
	error = CKR_HOST_MEMORY;
Packit 40b132
	goto loser;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    if (count == 0) {
Packit 40b132
	newStr = sqlite3_mprintf(FIND_OBJECTS_ALL_CMD, table);
Packit 40b132
    } else {
Packit 40b132
	newStr = sqlite3_mprintf(FIND_OBJECTS_CMD, table, findStr);
Packit 40b132
    }
Packit 40b132
    sqlite3_free(findStr);
Packit 40b132
    if (newStr == NULL) {
Packit 40b132
	error = CKR_HOST_MEMORY;
Packit 40b132
	goto loser;
Packit 40b132
    }
Packit 40b132
    sqlerr = sqlite3_prepare_v2(sqlDB, newStr, -1, &findstmt, NULL);
Packit 40b132
    sqlite3_free(newStr);
Packit 40b132
    for (i=0; sqlerr == SQLITE_OK && i < count; i++) {
Packit 40b132
	const void *blobData = template[i].pValue;
Packit 40b132
	unsigned int blobSize = template[i].ulValueLen;
Packit 40b132
	if (blobSize == 0) {
Packit 40b132
	    blobSize = SQLITE_EXPLICIT_NULL_LEN;
Packit 40b132
	    blobData = SQLITE_EXPLICIT_NULL;
Packit 40b132
	}
Packit 40b132
	sqlerr = sqlite3_bind_blob(findstmt, i+1, blobData, blobSize,
Packit 40b132
				   SQLITE_TRANSIENT);
Packit 40b132
    }
Packit 40b132
    if (sqlerr == SQLITE_OK) {
Packit 40b132
	*find = PORT_New(SDBFind);
Packit 40b132
	if (*find == NULL) {
Packit 40b132
	    error = CKR_HOST_MEMORY;
Packit 40b132
	    goto loser;
Packit 40b132
	}
Packit 40b132
	(*find)->findstmt = findstmt;
Packit 40b132
	(*find)->sqlDB = sqlDB;
Packit 40b132
	UNLOCK_SQLITE()  
Packit 40b132
	return CKR_OK;
Packit 40b132
    } 
Packit 40b132
    error = sdb_mapSQLError(sdb_p->type, sqlerr);
Packit 40b132
Packit 40b132
loser: 
Packit 40b132
    if (findstmt) {
Packit 40b132
	sqlite3_reset(findstmt);
Packit 40b132
	sqlite3_finalize(findstmt);
Packit 40b132
    }
Packit 40b132
    if (sqlDB) {
Packit 40b132
	sdb_closeDBLocal(sdb_p, sqlDB) ;
Packit 40b132
    }
Packit 40b132
    UNLOCK_SQLITE()  
Packit 40b132
    return error;
Packit 40b132
}
Packit 40b132
Packit 40b132
Packit 40b132
CK_RV
Packit 40b132
sdb_FindObjects(SDB *sdb, SDBFind *sdbFind, CK_OBJECT_HANDLE *object, 
Packit 40b132
		CK_ULONG arraySize, CK_ULONG *count)
Packit 40b132
{
Packit 40b132
    SDBPrivate *sdb_p = sdb->private;
Packit 40b132
    sqlite3_stmt *stmt = sdbFind->findstmt;
Packit 40b132
    int sqlerr = SQLITE_OK;
Packit 40b132
    int retry = 0;
Packit 40b132
Packit 40b132
    *count = 0;
Packit 40b132
Packit 40b132
    if (arraySize == 0) {
Packit 40b132
	return CKR_OK;
Packit 40b132
    }
Packit 40b132
    LOCK_SQLITE()  
Packit 40b132
Packit 40b132
    do {
Packit 40b132
	sqlerr = sqlite3_step(stmt);
Packit 40b132
	if (sqlerr == SQLITE_BUSY) {
Packit 40b132
	    PR_Sleep(SDB_BUSY_RETRY_TIME);
Packit 40b132
	}
Packit 40b132
	if (sqlerr == SQLITE_ROW) {
Packit 40b132
	    /* only care about the id */
Packit 40b132
	    *object++= sqlite3_column_int(stmt, 0);
Packit 40b132
	    arraySize--;
Packit 40b132
	    (*count)++;
Packit 40b132
	}
Packit 40b132
    } while (!sdb_done(sqlerr,&retry) && (arraySize > 0));
Packit 40b132
Packit 40b132
    /* we only have some of the objects, there is probably more,
Packit 40b132
     * set the sqlerr to an OK value so we return CKR_OK */
Packit 40b132
    if (sqlerr == SQLITE_ROW && arraySize == 0) {
Packit 40b132
	sqlerr = SQLITE_DONE;
Packit 40b132
    }
Packit 40b132
    UNLOCK_SQLITE()  
Packit 40b132
Packit 40b132
    return sdb_mapSQLError(sdb_p->type, sqlerr);
Packit 40b132
}
Packit 40b132
Packit 40b132
CK_RV
Packit 40b132
sdb_FindObjectsFinal(SDB *sdb, SDBFind *sdbFind)
Packit 40b132
{
Packit 40b132
    SDBPrivate *sdb_p = sdb->private;
Packit 40b132
    sqlite3_stmt *stmt = sdbFind->findstmt;
Packit 40b132
    sqlite3 *sqlDB = sdbFind->sqlDB;
Packit 40b132
    int sqlerr = SQLITE_OK;
Packit 40b132
Packit 40b132
    LOCK_SQLITE()  
Packit 40b132
    if (stmt) {
Packit 40b132
	sqlite3_reset(stmt);
Packit 40b132
	sqlerr = sqlite3_finalize(stmt);
Packit 40b132
    }
Packit 40b132
    if (sqlDB) {
Packit 40b132
	sdb_closeDBLocal(sdb_p, sqlDB) ;
Packit 40b132
    }
Packit 40b132
    PORT_Free(sdbFind);
Packit 40b132
Packit 40b132
    UNLOCK_SQLITE()  
Packit 40b132
    return sdb_mapSQLError(sdb_p->type, sqlerr);
Packit 40b132
}
Packit 40b132
Packit 40b132
static const char GET_ATTRIBUTE_CMD[] = "SELECT ALL %s FROM %s WHERE id=$ID;";
Packit 40b132
CK_RV
Packit 40b132
sdb_GetAttributeValueNoLock(SDB *sdb, CK_OBJECT_HANDLE object_id, 
Packit 40b132
				CK_ATTRIBUTE *template, CK_ULONG count)
Packit 40b132
{
Packit 40b132
    SDBPrivate *sdb_p = sdb->private;
Packit 40b132
    sqlite3  *sqlDB = NULL;
Packit 40b132
    sqlite3_stmt *stmt = NULL;
Packit 40b132
    char *getStr = NULL;
Packit 40b132
    char *newStr = NULL;
Packit 40b132
    const char *table = NULL;
Packit 40b132
    int sqlerr = SQLITE_OK;
Packit 40b132
    CK_RV error = CKR_OK;
Packit 40b132
    int found = 0;
Packit 40b132
    int retry = 0;
Packit 40b132
    int i;
Packit 40b132
Packit 40b132
Packit 40b132
    /* open a new db if necessary */
Packit 40b132
    error = sdb_openDBLocal(sdb_p, &sqlDB, &table);
Packit 40b132
    if (error != CKR_OK) {
Packit 40b132
	goto loser;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    for (i=0; i < count; i++) {
Packit 40b132
	getStr = sqlite3_mprintf("a%x", template[i].type);
Packit 40b132
Packit 40b132
	if (getStr == NULL) {
Packit 40b132
	    error = CKR_HOST_MEMORY;
Packit 40b132
	    goto loser;
Packit 40b132
	}
Packit 40b132
Packit 40b132
	newStr = sqlite3_mprintf(GET_ATTRIBUTE_CMD, getStr, table);
Packit 40b132
	sqlite3_free(getStr);
Packit 40b132
	getStr = NULL;
Packit 40b132
	if (newStr == NULL) {
Packit 40b132
	    error = CKR_HOST_MEMORY;
Packit 40b132
	    goto loser;
Packit 40b132
	}
Packit 40b132
Packit 40b132
	sqlerr = sqlite3_prepare_v2(sqlDB, newStr, -1, &stmt, NULL);
Packit 40b132
	sqlite3_free(newStr);
Packit 40b132
	newStr = NULL;
Packit 40b132
	if (sqlerr == SQLITE_ERROR) {
Packit 40b132
	    template[i].ulValueLen = -1;
Packit 40b132
	    error = CKR_ATTRIBUTE_TYPE_INVALID;
Packit 40b132
	    continue;
Packit 40b132
	} else if (sqlerr != SQLITE_OK) { goto loser; }
Packit 40b132
Packit 40b132
	sqlerr = sqlite3_bind_int(stmt, 1, object_id);
Packit 40b132
	if (sqlerr != SQLITE_OK) { goto loser; }
Packit 40b132
Packit 40b132
	do {
Packit 40b132
	    sqlerr = sqlite3_step(stmt);
Packit 40b132
	    if (sqlerr == SQLITE_BUSY) {
Packit 40b132
		PR_Sleep(SDB_BUSY_RETRY_TIME);
Packit 40b132
	    }
Packit 40b132
	    if (sqlerr == SQLITE_ROW) {
Packit 40b132
	    	int blobSize;
Packit 40b132
	    	const char *blobData;
Packit 40b132
Packit 40b132
	    	blobSize = sqlite3_column_bytes(stmt, 0);
Packit 40b132
		blobData = sqlite3_column_blob(stmt, 0);
Packit 40b132
		if (blobData == NULL) {
Packit 40b132
		    template[i].ulValueLen = -1;
Packit 40b132
		    error = CKR_ATTRIBUTE_TYPE_INVALID; 
Packit 40b132
		    break;
Packit 40b132
		}
Packit 40b132
		/* If the blob equals our explicit NULL value, then the 
Packit 40b132
		 * attribute is a NULL. */
Packit 40b132
		if ((blobSize == SQLITE_EXPLICIT_NULL_LEN) &&
Packit 40b132
		   	(PORT_Memcmp(blobData, SQLITE_EXPLICIT_NULL, 
Packit 40b132
			      SQLITE_EXPLICIT_NULL_LEN) == 0)) {
Packit 40b132
		    blobSize = 0;
Packit 40b132
		}
Packit 40b132
		if (template[i].pValue) {
Packit 40b132
		    if (template[i].ulValueLen < blobSize) {
Packit 40b132
			template[i].ulValueLen = -1;
Packit 40b132
		    	error = CKR_BUFFER_TOO_SMALL;
Packit 40b132
			break;
Packit 40b132
		    }
Packit 40b132
	    	    PORT_Memcpy(template[i].pValue, blobData, blobSize);
Packit 40b132
		}
Packit 40b132
		template[i].ulValueLen = blobSize;
Packit 40b132
		found = 1;
Packit 40b132
	    }
Packit 40b132
	} while (!sdb_done(sqlerr,&retry));
Packit 40b132
	sqlite3_reset(stmt);
Packit 40b132
	sqlite3_finalize(stmt);
Packit 40b132
	stmt = NULL;
Packit 40b132
    }
Packit 40b132
Packit 40b132
loser:
Packit 40b132
    /* fix up the error if necessary */
Packit 40b132
    if (error == CKR_OK) {
Packit 40b132
	error = sdb_mapSQLError(sdb_p->type, sqlerr);
Packit 40b132
	if (!found && error == CKR_OK) {
Packit 40b132
	    error = CKR_OBJECT_HANDLE_INVALID;
Packit 40b132
	}
Packit 40b132
    }
Packit 40b132
Packit 40b132
    if (stmt) {
Packit 40b132
	sqlite3_reset(stmt);
Packit 40b132
	sqlite3_finalize(stmt);
Packit 40b132
    }
Packit 40b132
Packit 40b132
    /* if we had to open a new database, free it now */
Packit 40b132
    if (sqlDB) {
Packit 40b132
	sdb_closeDBLocal(sdb_p, sqlDB) ;
Packit 40b132
    }
Packit 40b132
    return error;
Packit 40b132
}
Packit 40b132
Packit 40b132
CK_RV
Packit 40b132
sdb_GetAttributeValue(SDB *sdb, CK_OBJECT_HANDLE object_id, 
Packit 40b132
				CK_ATTRIBUTE *template, CK_ULONG count)
Packit 40b132
{
Packit 40b132
    CK_RV crv;
Packit 40b132
Packit 40b132
    if (count == 0) {
Packit 40b132
	return CKR_OK;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    LOCK_SQLITE()  
Packit 40b132
    crv = sdb_GetAttributeValueNoLock(sdb, object_id, template, count);
Packit 40b132
    UNLOCK_SQLITE()  
Packit 40b132
    return crv;
Packit 40b132
}
Packit 40b132
   
Packit 40b132
static const char SET_ATTRIBUTE_CMD[] = "UPDATE %s SET %s WHERE id=$ID;";
Packit 40b132
CK_RV
Packit 40b132
sdb_SetAttributeValue(SDB *sdb, CK_OBJECT_HANDLE object_id, 
Packit 40b132
			const CK_ATTRIBUTE *template, CK_ULONG count)
Packit 40b132
{
Packit 40b132
    SDBPrivate *sdb_p = sdb->private;
Packit 40b132
    sqlite3  *sqlDB = NULL;
Packit 40b132
    sqlite3_stmt *stmt = NULL;
Packit 40b132
    char *setStr = NULL;
Packit 40b132
    char *newStr = NULL;
Packit 40b132
    int sqlerr = SQLITE_OK;
Packit 40b132
    int retry = 0;
Packit 40b132
    CK_RV error = CKR_OK;
Packit 40b132
    int i;
Packit 40b132
Packit 40b132
    if ((sdb->sdb_flags & SDB_RDONLY) != 0) {
Packit 40b132
	return CKR_TOKEN_WRITE_PROTECTED;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    if (count == 0) {
Packit 40b132
	return CKR_OK;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    LOCK_SQLITE()  
Packit 40b132
    setStr = sqlite3_mprintf("");
Packit 40b132
    for (i=0; setStr && i < count; i++) {
Packit 40b132
	if (i==0) {
Packit 40b132
	    sqlite3_free(setStr);
Packit 40b132
   	    setStr = sqlite3_mprintf("a%x=$VALUE%d", 
Packit 40b132
				template[i].type, i);
Packit 40b132
	    continue;
Packit 40b132
	}
Packit 40b132
	newStr = sqlite3_mprintf("%s,a%x=$VALUE%d", setStr, 
Packit 40b132
				template[i].type, i);
Packit 40b132
	sqlite3_free(setStr);
Packit 40b132
	setStr = newStr;
Packit 40b132
    }
Packit 40b132
    newStr = NULL;
Packit 40b132
Packit 40b132
    if (setStr == NULL) {
Packit 40b132
	return CKR_HOST_MEMORY;
Packit 40b132
    }
Packit 40b132
    newStr =  sqlite3_mprintf(SET_ATTRIBUTE_CMD, sdb_p->table, setStr);
Packit 40b132
    sqlite3_free(setStr);
Packit 40b132
    if (newStr == NULL) {
Packit 40b132
	UNLOCK_SQLITE()  
Packit 40b132
	return CKR_HOST_MEMORY;
Packit 40b132
    }
Packit 40b132
    error = sdb_openDBLocal(sdb_p, &sqlDB, NULL);
Packit 40b132
    if (error != CKR_OK) {
Packit 40b132
	goto loser;
Packit 40b132
    }
Packit 40b132
    sqlerr = sqlite3_prepare_v2(sqlDB, newStr, -1, &stmt, NULL);
Packit 40b132
    if (sqlerr != SQLITE_OK) goto loser;
Packit 40b132
    for (i=0; i < count; i++) {
Packit 40b132
	if (template[i].ulValueLen != 0) {
Packit 40b132
	    sqlerr = sqlite3_bind_blob(stmt, i+1, template[i].pValue, 
Packit 40b132
				template[i].ulValueLen, SQLITE_STATIC);
Packit 40b132
	} else {
Packit 40b132
	    sqlerr = sqlite3_bind_blob(stmt, i+2, SQLITE_EXPLICIT_NULL, 
Packit 40b132
			SQLITE_EXPLICIT_NULL_LEN, SQLITE_STATIC);
Packit 40b132
	}
Packit 40b132
        if (sqlerr != SQLITE_OK) goto loser;
Packit 40b132
    }
Packit 40b132
    sqlerr = sqlite3_bind_int(stmt, i+1, object_id);
Packit 40b132
    if (sqlerr != SQLITE_OK) goto loser;
Packit 40b132
Packit 40b132
    do {
Packit 40b132
	sqlerr = sqlite3_step(stmt);
Packit 40b132
	if (sqlerr == SQLITE_BUSY) {
Packit 40b132
	    PR_Sleep(SDB_BUSY_RETRY_TIME);
Packit 40b132
	}
Packit 40b132
    } while (!sdb_done(sqlerr,&retry));
Packit 40b132
Packit 40b132
loser:
Packit 40b132
    if (newStr) {
Packit 40b132
	sqlite3_free(newStr);
Packit 40b132
    }
Packit 40b132
    if (error == CKR_OK) {
Packit 40b132
	error = sdb_mapSQLError(sdb_p->type, sqlerr);
Packit 40b132
    }
Packit 40b132
Packit 40b132
    if (stmt) {
Packit 40b132
	sqlite3_reset(stmt);
Packit 40b132
	sqlite3_finalize(stmt);
Packit 40b132
    }
Packit 40b132
Packit 40b132
    if (sqlDB) {
Packit 40b132
	sdb_closeDBLocal(sdb_p, sqlDB) ;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    UNLOCK_SQLITE()  
Packit 40b132
    return error;
Packit 40b132
}
Packit 40b132
Packit 40b132
/*
Packit 40b132
 * check to see if a candidate object handle already exists.
Packit 40b132
 */
Packit 40b132
static PRBool
Packit 40b132
sdb_objectExists(SDB *sdb, CK_OBJECT_HANDLE candidate)
Packit 40b132
{
Packit 40b132
    CK_RV crv;
Packit 40b132
    CK_ATTRIBUTE template = { CKA_LABEL, NULL, 0 };
Packit 40b132
Packit 40b132
    crv = sdb_GetAttributeValueNoLock(sdb,candidate,&template, 1);
Packit 40b132
    if (crv == CKR_OBJECT_HANDLE_INVALID) {
Packit 40b132
	return PR_FALSE;
Packit 40b132
    }
Packit 40b132
    return PR_TRUE;
Packit 40b132
}
Packit 40b132
Packit 40b132
/*
Packit 40b132
 * if we're here, we are in a transaction, so it's safe
Packit 40b132
 * to examine the current state of the database
Packit 40b132
 */
Packit 40b132
static CK_OBJECT_HANDLE
Packit 40b132
sdb_getObjectId(SDB *sdb)
Packit 40b132
{
Packit 40b132
    CK_OBJECT_HANDLE candidate;
Packit 40b132
    static CK_OBJECT_HANDLE next_obj = CK_INVALID_HANDLE;
Packit 40b132
    int count;
Packit 40b132
    /*
Packit 40b132
     * get an initial object handle to use
Packit 40b132
     */
Packit 40b132
    if (next_obj == CK_INVALID_HANDLE) {
Packit 40b132
        PRTime time;
Packit 40b132
	time = PR_Now();
Packit 40b132
Packit 40b132
	next_obj = (CK_OBJECT_HANDLE)(time & 0x3fffffffL);
Packit 40b132
    }
Packit 40b132
    candidate = next_obj++;
Packit 40b132
    /* detect that we've looped through all the handles... */
Packit 40b132
    for (count = 0; count < 0x40000000; count++, candidate = next_obj++) {
Packit 40b132
	/* mask off excess bits */
Packit 40b132
	candidate &= 0x3fffffff;
Packit 40b132
	/* if we hit zero, go to the next entry */
Packit 40b132
	if (candidate == CK_INVALID_HANDLE) {
Packit 40b132
	    continue;
Packit 40b132
	}
Packit 40b132
	/* make sure we aren't already using */
Packit 40b132
	if (!sdb_objectExists(sdb, candidate)) {
Packit 40b132
	    /* this one is free */
Packit 40b132
	    return candidate;
Packit 40b132
	}
Packit 40b132
    }
Packit 40b132
Packit 40b132
    /* no handle is free, fail */
Packit 40b132
    return CK_INVALID_HANDLE;
Packit 40b132
}
Packit 40b132
Packit 40b132
static const char CREATE_CMD[] = "INSERT INTO %s (id%s) VALUES($ID%s);";
Packit 40b132
CK_RV
Packit 40b132
sdb_CreateObject(SDB *sdb, CK_OBJECT_HANDLE *object_id, 
Packit 40b132
		 const CK_ATTRIBUTE *template, CK_ULONG count)
Packit 40b132
{
Packit 40b132
    SDBPrivate *sdb_p = sdb->private;
Packit 40b132
    sqlite3  *sqlDB = NULL;
Packit 40b132
    sqlite3_stmt *stmt = NULL;
Packit 40b132
    char *columnStr = NULL;
Packit 40b132
    char *valueStr = NULL;
Packit 40b132
    char *newStr = NULL;
Packit 40b132
    int sqlerr = SQLITE_OK;
Packit 40b132
    CK_RV error = CKR_OK;
Packit 40b132
    CK_OBJECT_HANDLE this_object = CK_INVALID_HANDLE;
Packit 40b132
    int retry = 0;
Packit 40b132
    int i;
Packit 40b132
Packit 40b132
    if ((sdb->sdb_flags & SDB_RDONLY) != 0) {
Packit 40b132
	return CKR_TOKEN_WRITE_PROTECTED;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    LOCK_SQLITE()  
Packit 40b132
    if ((*object_id != CK_INVALID_HANDLE) && 
Packit 40b132
		!sdb_objectExists(sdb, *object_id)) {
Packit 40b132
	this_object = *object_id;
Packit 40b132
    } else {
Packit 40b132
	this_object = sdb_getObjectId(sdb);
Packit 40b132
    }
Packit 40b132
    if (this_object == CK_INVALID_HANDLE) {
Packit 40b132
	UNLOCK_SQLITE();
Packit 40b132
	return CKR_HOST_MEMORY;
Packit 40b132
    }
Packit 40b132
    columnStr = sqlite3_mprintf("");
Packit 40b132
    valueStr = sqlite3_mprintf("");
Packit 40b132
    *object_id = this_object;
Packit 40b132
    for (i=0; columnStr && valueStr && i < count; i++) {
Packit 40b132
   	newStr = sqlite3_mprintf("%s,a%x", columnStr, template[i].type);
Packit 40b132
	sqlite3_free(columnStr);
Packit 40b132
	columnStr = newStr;
Packit 40b132
   	newStr = sqlite3_mprintf("%s,$VALUE%d", valueStr, i);
Packit 40b132
	sqlite3_free(valueStr);
Packit 40b132
	valueStr = newStr;
Packit 40b132
    }
Packit 40b132
    newStr = NULL;
Packit 40b132
    if ((columnStr == NULL) || (valueStr == NULL)) {
Packit 40b132
	if (columnStr) {
Packit 40b132
	    sqlite3_free(columnStr);
Packit 40b132
	}
Packit 40b132
	if (valueStr) {
Packit 40b132
	    sqlite3_free(valueStr);
Packit 40b132
	}
Packit 40b132
	UNLOCK_SQLITE()  
Packit 40b132
	return CKR_HOST_MEMORY;
Packit 40b132
    }
Packit 40b132
    newStr =  sqlite3_mprintf(CREATE_CMD, sdb_p->table, columnStr, valueStr);
Packit 40b132
    sqlite3_free(columnStr);
Packit 40b132
    sqlite3_free(valueStr);
Packit 40b132
    error = sdb_openDBLocal(sdb_p, &sqlDB, NULL);
Packit 40b132
    if (error != CKR_OK) {
Packit 40b132
	goto loser;
Packit 40b132
    }
Packit 40b132
    sqlerr = sqlite3_prepare_v2(sqlDB, newStr, -1, &stmt, NULL);
Packit 40b132
    if (sqlerr != SQLITE_OK) goto loser;
Packit 40b132
    sqlerr = sqlite3_bind_int(stmt, 1, *object_id);
Packit 40b132
    if (sqlerr != SQLITE_OK) goto loser;
Packit 40b132
    for (i=0; i < count; i++) {
Packit 40b132
	if (template[i].ulValueLen) {
Packit 40b132
	    sqlerr = sqlite3_bind_blob(stmt, i+2, template[i].pValue, 
Packit 40b132
			template[i].ulValueLen, SQLITE_STATIC);
Packit 40b132
	} else {
Packit 40b132
	    sqlerr = sqlite3_bind_blob(stmt, i+2, SQLITE_EXPLICIT_NULL, 
Packit 40b132
			SQLITE_EXPLICIT_NULL_LEN, SQLITE_STATIC);
Packit 40b132
	}
Packit 40b132
        if (sqlerr != SQLITE_OK) goto loser;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    do {
Packit 40b132
	sqlerr = sqlite3_step(stmt);
Packit 40b132
	if (sqlerr == SQLITE_BUSY) {
Packit 40b132
	    PR_Sleep(SDB_BUSY_RETRY_TIME);
Packit 40b132
	}
Packit 40b132
    } while (!sdb_done(sqlerr,&retry));
Packit 40b132
Packit 40b132
loser:
Packit 40b132
    if (newStr) {
Packit 40b132
	sqlite3_free(newStr);
Packit 40b132
    }
Packit 40b132
    if (error == CKR_OK) {
Packit 40b132
	error = sdb_mapSQLError(sdb_p->type, sqlerr);
Packit 40b132
    }
Packit 40b132
Packit 40b132
    if (stmt) {
Packit 40b132
	sqlite3_reset(stmt);
Packit 40b132
	sqlite3_finalize(stmt);
Packit 40b132
    }
Packit 40b132
Packit 40b132
    if (sqlDB) {
Packit 40b132
	sdb_closeDBLocal(sdb_p, sqlDB) ;
Packit 40b132
    }
Packit 40b132
    UNLOCK_SQLITE()  
Packit 40b132
Packit 40b132
    return error;
Packit 40b132
}
Packit 40b132
Packit 40b132
static const char DESTROY_CMD[] = "DELETE FROM %s WHERE (id=$ID);";
Packit 40b132
CK_RV
Packit 40b132
sdb_DestroyObject(SDB *sdb, CK_OBJECT_HANDLE object_id)
Packit 40b132
{
Packit 40b132
    SDBPrivate *sdb_p = sdb->private;
Packit 40b132
    sqlite3  *sqlDB = NULL;
Packit 40b132
    sqlite3_stmt *stmt = NULL;
Packit 40b132
    char *newStr = NULL;
Packit 40b132
    int sqlerr = SQLITE_OK;
Packit 40b132
    CK_RV error = CKR_OK;
Packit 40b132
    int retry = 0;
Packit 40b132
Packit 40b132
    if ((sdb->sdb_flags & SDB_RDONLY) != 0) {
Packit 40b132
	return CKR_TOKEN_WRITE_PROTECTED;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    LOCK_SQLITE()  
Packit 40b132
    error = sdb_openDBLocal(sdb_p, &sqlDB, NULL);
Packit 40b132
    if (error != CKR_OK) {
Packit 40b132
	goto loser;
Packit 40b132
    }
Packit 40b132
    newStr =  sqlite3_mprintf(DESTROY_CMD, sdb_p->table);
Packit 40b132
    if (newStr == NULL) {
Packit 40b132
	error = CKR_HOST_MEMORY;
Packit 40b132
	goto loser;
Packit 40b132
    }
Packit 40b132
    sqlerr =sqlite3_prepare_v2(sqlDB, newStr, -1, &stmt, NULL);
Packit 40b132
    sqlite3_free(newStr);
Packit 40b132
    if (sqlerr != SQLITE_OK) goto loser;
Packit 40b132
    sqlerr =sqlite3_bind_int(stmt, 1, object_id);
Packit 40b132
    if (sqlerr != SQLITE_OK) goto loser;
Packit 40b132
Packit 40b132
    do {
Packit 40b132
	sqlerr = sqlite3_step(stmt);
Packit 40b132
	if (sqlerr == SQLITE_BUSY) {
Packit 40b132
	    PR_Sleep(SDB_BUSY_RETRY_TIME);
Packit 40b132
	}
Packit 40b132
    } while (!sdb_done(sqlerr,&retry));
Packit 40b132
Packit 40b132
loser:
Packit 40b132
    if (error == CKR_OK) {
Packit 40b132
	error = sdb_mapSQLError(sdb_p->type, sqlerr);
Packit 40b132
    }
Packit 40b132
Packit 40b132
    if (stmt) {
Packit 40b132
	sqlite3_reset(stmt);
Packit 40b132
	sqlite3_finalize(stmt);
Packit 40b132
    }
Packit 40b132
Packit 40b132
    if (sqlDB) {
Packit 40b132
	sdb_closeDBLocal(sdb_p, sqlDB) ;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    UNLOCK_SQLITE()  
Packit 40b132
    return error;
Packit 40b132
}
Packit 40b132
   
Packit 40b132
static const char BEGIN_CMD[] = "BEGIN IMMEDIATE TRANSACTION;";
Packit 40b132
/*
Packit 40b132
 * start a transaction.
Packit 40b132
 *
Packit 40b132
 * We need to open a new database, then store that new database into
Packit 40b132
 * the private data structure. We open the database first, then use locks
Packit 40b132
 * to protect storing the data to prevent deadlocks.
Packit 40b132
 */
Packit 40b132
CK_RV
Packit 40b132
sdb_Begin(SDB *sdb)
Packit 40b132
{
Packit 40b132
    SDBPrivate *sdb_p = sdb->private;
Packit 40b132
    sqlite3  *sqlDB = NULL;
Packit 40b132
    sqlite3_stmt *stmt = NULL;
Packit 40b132
    int sqlerr = SQLITE_OK;
Packit 40b132
    CK_RV error = CKR_OK;
Packit 40b132
    int retry = 0;
Packit 40b132
Packit 40b132
Packit 40b132
    if ((sdb->sdb_flags & SDB_RDONLY) != 0) {
Packit 40b132
	return CKR_TOKEN_WRITE_PROTECTED;
Packit 40b132
    }
Packit 40b132
Packit 40b132
Packit 40b132
    LOCK_SQLITE()  
Packit 40b132
Packit 40b132
    /* get a new version that we will use for the entire transaction */
Packit 40b132
    sqlerr = sdb_openDB(sdb_p->sqlDBName, &sqlDB, SDB_RDWR);
Packit 40b132
    if (sqlerr != SQLITE_OK) {
Packit 40b132
	goto loser;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    sqlerr =sqlite3_prepare_v2(sqlDB, BEGIN_CMD, -1, &stmt, NULL);
Packit 40b132
Packit 40b132
    do {
Packit 40b132
	sqlerr = sqlite3_step(stmt);
Packit 40b132
	if (sqlerr == SQLITE_BUSY) {
Packit 40b132
	    PR_Sleep(SDB_BUSY_RETRY_TIME);
Packit 40b132
	}
Packit 40b132
    } while (!sdb_done(sqlerr,&retry));
Packit 40b132
Packit 40b132
    if (stmt) {
Packit 40b132
	sqlite3_reset(stmt);
Packit 40b132
	sqlite3_finalize(stmt);
Packit 40b132
    }
Packit 40b132
Packit 40b132
loser:
Packit 40b132
    error = sdb_mapSQLError(sdb_p->type, sqlerr);
Packit 40b132
Packit 40b132
    /* we are starting a new transaction, 
Packit 40b132
     * and if we succeeded, then save this database for the rest of
Packit 40b132
     * our transaction */
Packit 40b132
    if (error == CKR_OK) {
Packit 40b132
	/* we hold a 'BEGIN TRANSACTION' and a sdb_p->lock. At this point
Packit 40b132
	 * sdb_p->sqlXactDB MUST be null */
Packit 40b132
	PR_EnterMonitor(sdb_p->dbMon);
Packit 40b132
	PORT_Assert(sdb_p->sqlXactDB == NULL);
Packit 40b132
	sdb_p->sqlXactDB = sqlDB;
Packit 40b132
	sdb_p->sqlXactThread = PR_GetCurrentThread();
Packit 40b132
        PR_ExitMonitor(sdb_p->dbMon);
Packit 40b132
    } else {
Packit 40b132
	/* we failed to start our transaction,
Packit 40b132
	 * free any databases we opened. */
Packit 40b132
	if (sqlDB) {
Packit 40b132
	    sqlite3_close(sqlDB);
Packit 40b132
	}
Packit 40b132
    }
Packit 40b132
Packit 40b132
    UNLOCK_SQLITE()  
Packit 40b132
    return error;
Packit 40b132
}
Packit 40b132
Packit 40b132
/*
Packit 40b132
 * Complete a transaction. Basically undo everything we did in begin.
Packit 40b132
 * There are 2 flavors Abort and Commit. Basically the only differerence between
Packit 40b132
 * these 2 are what the database will show. (no change in to former, change in
Packit 40b132
 * the latter).
Packit 40b132
 */
Packit 40b132
static CK_RV 
Packit 40b132
sdb_complete(SDB *sdb, const char *cmd)
Packit 40b132
{
Packit 40b132
    SDBPrivate *sdb_p = sdb->private;
Packit 40b132
    sqlite3  *sqlDB = NULL;
Packit 40b132
    sqlite3_stmt *stmt = NULL;
Packit 40b132
    int sqlerr = SQLITE_OK;
Packit 40b132
    CK_RV error = CKR_OK;
Packit 40b132
    int retry = 0;
Packit 40b132
Packit 40b132
Packit 40b132
    if ((sdb->sdb_flags & SDB_RDONLY) != 0) {
Packit 40b132
	return CKR_TOKEN_WRITE_PROTECTED;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    /* We must have a transation database, or we shouldn't have arrived here */
Packit 40b132
    PR_EnterMonitor(sdb_p->dbMon);
Packit 40b132
    PORT_Assert(sdb_p->sqlXactDB);
Packit 40b132
    if (sdb_p->sqlXactDB == NULL) {
Packit 40b132
        PR_ExitMonitor(sdb_p->dbMon);
Packit 40b132
	return CKR_GENERAL_ERROR; /* shouldn't happen */
Packit 40b132
    }
Packit 40b132
    PORT_Assert( sdb_p->sqlXactThread == PR_GetCurrentThread());
Packit 40b132
    if ( sdb_p->sqlXactThread != PR_GetCurrentThread()) {
Packit 40b132
        PR_ExitMonitor(sdb_p->dbMon);
Packit 40b132
	return CKR_GENERAL_ERROR; /* shouldn't happen */
Packit 40b132
    }
Packit 40b132
    sqlDB = sdb_p->sqlXactDB;
Packit 40b132
    sdb_p->sqlXactDB = NULL; /* no one else can get to this DB, 
Packit 40b132
			      * safe to unlock */
Packit 40b132
    sdb_p->sqlXactThread = NULL; 
Packit 40b132
    PR_ExitMonitor(sdb_p->dbMon);
Packit 40b132
Packit 40b132
    sqlerr =sqlite3_prepare_v2(sqlDB, cmd, -1, &stmt, NULL);
Packit 40b132
Packit 40b132
    do {
Packit 40b132
	sqlerr = sqlite3_step(stmt);
Packit 40b132
	if (sqlerr == SQLITE_BUSY) {
Packit 40b132
	    PR_Sleep(SDB_BUSY_RETRY_TIME);
Packit 40b132
	}
Packit 40b132
    } while (!sdb_done(sqlerr,&retry));
Packit 40b132
Packit 40b132
    /* Pending BEGIN TRANSACTIONS Can move forward at this point. */
Packit 40b132
Packit 40b132
    if (stmt) {
Packit 40b132
	sqlite3_reset(stmt);
Packit 40b132
	sqlite3_finalize(stmt);
Packit 40b132
    }
Packit 40b132
Packit 40b132
    /* we we have a cached DB image, update it as well */
Packit 40b132
    if (sdb_p->cacheTable) {
Packit 40b132
	PR_EnterMonitor(sdb_p->dbMon);
Packit 40b132
	sdb_updateCache(sdb_p);
Packit 40b132
	PR_ExitMonitor(sdb_p->dbMon);
Packit 40b132
    }
Packit 40b132
Packit 40b132
    error = sdb_mapSQLError(sdb_p->type, sqlerr);
Packit 40b132
Packit 40b132
    /* We just finished a transaction.
Packit 40b132
     * Free the database, and remove it from the list */
Packit 40b132
    sqlite3_close(sqlDB);
Packit 40b132
Packit 40b132
    return error;
Packit 40b132
}
Packit 40b132
Packit 40b132
static const char COMMIT_CMD[] = "COMMIT TRANSACTION;";
Packit 40b132
CK_RV
Packit 40b132
sdb_Commit(SDB *sdb)
Packit 40b132
{
Packit 40b132
    CK_RV crv;
Packit 40b132
    LOCK_SQLITE()  
Packit 40b132
    crv = sdb_complete(sdb,COMMIT_CMD);
Packit 40b132
    UNLOCK_SQLITE()  
Packit 40b132
    return crv;
Packit 40b132
}
Packit 40b132
Packit 40b132
static const char ROLLBACK_CMD[] = "ROLLBACK TRANSACTION;";
Packit 40b132
CK_RV
Packit 40b132
sdb_Abort(SDB *sdb)
Packit 40b132
{
Packit 40b132
    CK_RV crv;
Packit 40b132
    LOCK_SQLITE()  
Packit 40b132
    crv = sdb_complete(sdb,ROLLBACK_CMD);
Packit 40b132
    UNLOCK_SQLITE()  
Packit 40b132
    return crv;
Packit 40b132
}
Packit 40b132
Packit 40b132
static int tableExists(sqlite3 *sqlDB, const char *tableName);
Packit 40b132
Packit 40b132
static const char GET_PW_CMD[] = "SELECT ALL * FROM metaData WHERE id=$ID;";
Packit 40b132
CK_RV
Packit 40b132
sdb_GetMetaData(SDB *sdb, const char *id, SECItem *item1, SECItem *item2)
Packit 40b132
{
Packit 40b132
    SDBPrivate *sdb_p = sdb->private;
Packit 40b132
    sqlite3  *sqlDB = sdb_p->sqlXactDB;
Packit 40b132
    sqlite3_stmt *stmt = NULL;
Packit 40b132
    int sqlerr = SQLITE_OK;
Packit 40b132
    CK_RV error = CKR_OK;
Packit 40b132
    int found = 0;
Packit 40b132
    int retry = 0;
Packit 40b132
Packit 40b132
    LOCK_SQLITE()  
Packit 40b132
    error = sdb_openDBLocal(sdb_p, &sqlDB, NULL);
Packit 40b132
    if (error != CKR_OK) {
Packit 40b132
	goto loser;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    /* handle 'test' versions of the sqlite db */
Packit 40b132
    sqlerr = sqlite3_prepare_v2(sqlDB, GET_PW_CMD, -1, &stmt, NULL);
Packit 40b132
    /* Sigh, if we created a new table since we opened the database,
Packit 40b132
     * the database handle will not see the new table, we need to close this
Packit 40b132
     * database and reopen it. This is safe because we are holding the lock
Packit 40b132
     * still. */
Packit 40b132
    if (sqlerr == SQLITE_SCHEMA) {
Packit 40b132
	sqlerr = sdb_reopenDBLocal(sdb_p, &sqlDB);
Packit 40b132
	if (sqlerr != SQLITE_OK) {
Packit 40b132
	    goto loser;
Packit 40b132
	}
Packit 40b132
	sqlerr = sqlite3_prepare_v2(sqlDB, GET_PW_CMD, -1, &stmt, NULL);
Packit 40b132
    }
Packit 40b132
    if (sqlerr != SQLITE_OK) goto loser;
Packit 40b132
    sqlerr = sqlite3_bind_text(stmt, 1, id, PORT_Strlen(id), SQLITE_STATIC);
Packit 40b132
    do {
Packit 40b132
	sqlerr = sqlite3_step(stmt);
Packit 40b132
	if (sqlerr == SQLITE_BUSY) {
Packit 40b132
	    PR_Sleep(SDB_BUSY_RETRY_TIME);
Packit 40b132
	}
Packit 40b132
	if (sqlerr == SQLITE_ROW) {
Packit 40b132
	    const char *blobData;
Packit 40b132
	    unsigned int len = item1->len;
Packit 40b132
	    item1->len = sqlite3_column_bytes(stmt, 1);
Packit 40b132
	    if (item1->len > len) {
Packit 40b132
		error = CKR_BUFFER_TOO_SMALL;
Packit 40b132
		continue;
Packit 40b132
	    }
Packit 40b132
	    blobData = sqlite3_column_blob(stmt, 1);
Packit 40b132
	    PORT_Memcpy(item1->data,blobData, item1->len);
Packit 40b132
	    if (item2) {
Packit 40b132
		len = item2->len;
Packit 40b132
		item2->len = sqlite3_column_bytes(stmt, 2);
Packit 40b132
		if (item2->len > len) {
Packit 40b132
		    error = CKR_BUFFER_TOO_SMALL;
Packit 40b132
		    continue;
Packit 40b132
		}
Packit 40b132
		blobData = sqlite3_column_blob(stmt, 2);
Packit 40b132
		PORT_Memcpy(item2->data,blobData, item2->len);
Packit 40b132
	    }
Packit 40b132
	    found = 1;
Packit 40b132
	}
Packit 40b132
    } while (!sdb_done(sqlerr,&retry));
Packit 40b132
Packit 40b132
loser:
Packit 40b132
    /* fix up the error if necessary */
Packit 40b132
    if (error == CKR_OK) {
Packit 40b132
	error = sdb_mapSQLError(sdb_p->type, sqlerr);
Packit 40b132
	if (!found && error == CKR_OK) {
Packit 40b132
	    error = CKR_OBJECT_HANDLE_INVALID;
Packit 40b132
	}
Packit 40b132
    }
Packit 40b132
Packit 40b132
    if (stmt) {
Packit 40b132
	sqlite3_reset(stmt);
Packit 40b132
	sqlite3_finalize(stmt);
Packit 40b132
    }
Packit 40b132
Packit 40b132
    if (sqlDB) {
Packit 40b132
	sdb_closeDBLocal(sdb_p, sqlDB) ;
Packit 40b132
    }
Packit 40b132
    UNLOCK_SQLITE()  
Packit 40b132
Packit 40b132
    return error;
Packit 40b132
}
Packit 40b132
Packit 40b132
static const char PW_CREATE_TABLE_CMD[] =
Packit 40b132
 "CREATE TABLE metaData (id PRIMARY KEY UNIQUE ON CONFLICT REPLACE, item1, item2);";
Packit 40b132
static const char PW_CREATE_CMD[] =
Packit 40b132
 "INSERT INTO metaData (id,item1,item2) VALUES($ID,$ITEM1,$ITEM2);";
Packit 40b132
static const char MD_CREATE_CMD[]  =
Packit 40b132
 "INSERT INTO metaData (id,item1) VALUES($ID,$ITEM1);";
Packit 40b132
CK_RV
Packit 40b132
sdb_PutMetaData(SDB *sdb, const char *id, const SECItem *item1, 
Packit 40b132
					   const SECItem *item2)
Packit 40b132
{
Packit 40b132
    SDBPrivate *sdb_p = sdb->private;
Packit 40b132
    sqlite3  *sqlDB = sdb_p->sqlXactDB;
Packit 40b132
    sqlite3_stmt *stmt = NULL;
Packit 40b132
    int sqlerr = SQLITE_OK;
Packit 40b132
    CK_RV error = CKR_OK;
Packit 40b132
    int retry = 0;
Packit 40b132
    const char *cmd = PW_CREATE_CMD;
Packit 40b132
Packit 40b132
    if ((sdb->sdb_flags & SDB_RDONLY) != 0) {
Packit 40b132
	return CKR_TOKEN_WRITE_PROTECTED;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    LOCK_SQLITE()  
Packit 40b132
    error = sdb_openDBLocal(sdb_p, &sqlDB, NULL);
Packit 40b132
    if (error != CKR_OK) {
Packit 40b132
	goto loser;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    if (!tableExists(sqlDB, "metaData")) {
Packit 40b132
    	sqlerr = sqlite3_exec(sqlDB, PW_CREATE_TABLE_CMD, NULL, 0, NULL);
Packit 40b132
        if (sqlerr != SQLITE_OK) goto loser;
Packit 40b132
    }
Packit 40b132
    if (item2 == NULL) {
Packit 40b132
	cmd = MD_CREATE_CMD;
Packit 40b132
    }
Packit 40b132
    sqlerr = sqlite3_prepare_v2(sqlDB, cmd, -1, &stmt, NULL);
Packit 40b132
    if (sqlerr != SQLITE_OK) goto loser;
Packit 40b132
    sqlerr = sqlite3_bind_text(stmt, 1, id, PORT_Strlen(id), SQLITE_STATIC);
Packit 40b132
    if (sqlerr != SQLITE_OK) goto loser;
Packit 40b132
    sqlerr = sqlite3_bind_blob(stmt, 2, item1->data, item1->len, SQLITE_STATIC);
Packit 40b132
    if (sqlerr != SQLITE_OK) goto loser;
Packit 40b132
    if (item2) {
Packit 40b132
    	sqlerr = sqlite3_bind_blob(stmt, 3, item2->data, 
Packit 40b132
				   item2->len, SQLITE_STATIC);
Packit 40b132
        if (sqlerr != SQLITE_OK) goto loser;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    do {
Packit 40b132
	sqlerr = sqlite3_step(stmt);
Packit 40b132
	if (sqlerr == SQLITE_BUSY) {
Packit 40b132
	    PR_Sleep(SDB_BUSY_RETRY_TIME);
Packit 40b132
	}
Packit 40b132
    } while (!sdb_done(sqlerr,&retry));
Packit 40b132
Packit 40b132
loser:
Packit 40b132
    /* fix up the error if necessary */
Packit 40b132
    if (error == CKR_OK) {
Packit 40b132
	error = sdb_mapSQLError(sdb_p->type, sqlerr);
Packit 40b132
    }
Packit 40b132
Packit 40b132
    if (stmt) {
Packit 40b132
	sqlite3_reset(stmt);
Packit 40b132
	sqlite3_finalize(stmt);
Packit 40b132
    }
Packit 40b132
Packit 40b132
    if (sqlDB) {
Packit 40b132
	sdb_closeDBLocal(sdb_p, sqlDB) ;
Packit 40b132
    }
Packit 40b132
    UNLOCK_SQLITE()  
Packit 40b132
Packit 40b132
    return error;
Packit 40b132
}
Packit 40b132
Packit 40b132
static const char RESET_CMD[] = "DROP TABLE IF EXISTS %s;";
Packit 40b132
CK_RV
Packit 40b132
sdb_Reset(SDB *sdb)
Packit 40b132
{
Packit 40b132
    SDBPrivate *sdb_p = sdb->private;
Packit 40b132
    sqlite3  *sqlDB = NULL;
Packit 40b132
    char *newStr;
Packit 40b132
    int sqlerr = SQLITE_OK;
Packit 40b132
    CK_RV error = CKR_OK;
Packit 40b132
Packit 40b132
    /* only Key databases can be reset */
Packit 40b132
    if (sdb_p->type != SDB_KEY) {
Packit 40b132
	return CKR_OBJECT_HANDLE_INVALID;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    LOCK_SQLITE()  
Packit 40b132
    error = sdb_openDBLocal(sdb_p, &sqlDB, NULL);
Packit 40b132
    if (error != CKR_OK) {
Packit 40b132
	goto loser;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    /* delete the key table */
Packit 40b132
    newStr =  sqlite3_mprintf(RESET_CMD, sdb_p->table);
Packit 40b132
    if (newStr == NULL) {
Packit 40b132
	error = CKR_HOST_MEMORY;
Packit 40b132
	goto loser;
Packit 40b132
    }
Packit 40b132
    sqlerr = sqlite3_exec(sqlDB, newStr, NULL, 0, NULL);
Packit 40b132
    sqlite3_free(newStr);
Packit 40b132
Packit 40b132
    if (sqlerr != SQLITE_OK) goto loser;
Packit 40b132
Packit 40b132
    /* delete the password entry table */
Packit 40b132
    sqlerr = sqlite3_exec(sqlDB, "DROP TABLE IF EXISTS metaData;", 
Packit 40b132
                          NULL, 0, NULL);
Packit 40b132
Packit 40b132
loser:
Packit 40b132
    /* fix up the error if necessary */
Packit 40b132
    if (error == CKR_OK) {
Packit 40b132
	error = sdb_mapSQLError(sdb_p->type, sqlerr);
Packit 40b132
    }
Packit 40b132
Packit 40b132
    if (sqlDB) {
Packit 40b132
	sdb_closeDBLocal(sdb_p, sqlDB) ;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    UNLOCK_SQLITE()  
Packit 40b132
    return error;
Packit 40b132
}
Packit 40b132
Packit 40b132
Packit 40b132
CK_RV 
Packit 40b132
sdb_Close(SDB *sdb) 
Packit 40b132
{
Packit 40b132
    SDBPrivate *sdb_p = sdb->private;
Packit 40b132
    int sqlerr = SQLITE_OK;
Packit 40b132
    sdbDataType type = sdb_p->type;
Packit 40b132
Packit 40b132
    sqlerr = sqlite3_close(sdb_p->sqlReadDB);
Packit 40b132
    PORT_Free(sdb_p->sqlDBName);
Packit 40b132
    if (sdb_p->cacheTable) {
Packit 40b132
	sqlite3_free(sdb_p->cacheTable);
Packit 40b132
    }
Packit 40b132
    if (sdb_p->dbMon) {
Packit 40b132
	PR_DestroyMonitor(sdb_p->dbMon);
Packit 40b132
    }
Packit 40b132
    free(sdb_p);
Packit 40b132
    free(sdb);
Packit 40b132
    return sdb_mapSQLError(type, sqlerr);
Packit 40b132
}
Packit 40b132
Packit 40b132
Packit 40b132
/*
Packit 40b132
 * functions to support open
Packit 40b132
 */
Packit 40b132
Packit 40b132
static const char CHECK_TABLE_CMD[] = "SELECT ALL * FROM %s LIMIT 0;";
Packit 40b132
/* return 1 if sqlDB contains table 'tableName */
Packit 40b132
static int tableExists(sqlite3 *sqlDB, const char *tableName)
Packit 40b132
{
Packit 40b132
    char * cmd = sqlite3_mprintf(CHECK_TABLE_CMD, tableName);
Packit 40b132
    int sqlerr = SQLITE_OK;
Packit 40b132
Packit 40b132
    if (cmd == NULL) {
Packit 40b132
	return 0;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    sqlerr = sqlite3_exec(sqlDB, cmd, NULL, 0, 0);
Packit 40b132
    sqlite3_free(cmd);
Packit 40b132
Packit 40b132
    return (sqlerr == SQLITE_OK) ? 1 : 0;
Packit 40b132
}
Packit 40b132
Packit 40b132
void sdb_SetForkState(PRBool forked)
Packit 40b132
{
Packit 40b132
    /* XXXright now this is a no-op. The global fork state in the softokn3
Packit 40b132
     * shared library is already taken care of at the PKCS#11 level.
Packit 40b132
     * If and when we add fork state to the sqlite shared library and extern
Packit 40b132
     * interface, we will need to set it and reset it from here */
Packit 40b132
}
Packit 40b132
Packit 40b132
/*
Packit 40b132
 * initialize a single database
Packit 40b132
 */
Packit 40b132
static const char INIT_CMD[] =
Packit 40b132
 "CREATE TABLE %s (id PRIMARY KEY UNIQUE ON CONFLICT ABORT%s)";
Packit 40b132
Packit 40b132
CK_RV 
Packit 40b132
sdb_init(char *dbname, char *table, sdbDataType type, int *inUpdate,
Packit 40b132
	 int *newInit, int flags, PRUint32 accessOps, SDB **pSdb)
Packit 40b132
{
Packit 40b132
    int i;
Packit 40b132
    char *initStr = NULL;
Packit 40b132
    char *newStr;
Packit 40b132
    int inTransaction = 0;
Packit 40b132
    SDB *sdb = NULL;
Packit 40b132
    SDBPrivate *sdb_p = NULL;
Packit 40b132
    sqlite3 *sqlDB = NULL;
Packit 40b132
    int sqlerr = SQLITE_OK;
Packit 40b132
    CK_RV error = CKR_OK;
Packit 40b132
    char *cacheTable = NULL;
Packit 40b132
    PRIntervalTime now = 0;
Packit 40b132
    char *env;
Packit 40b132
    PRBool enableCache = PR_FALSE;
Packit 40b132
    PRBool create;
Packit 40b132
Packit 40b132
    *pSdb = NULL;
Packit 40b132
    *inUpdate = 0;
Packit 40b132
Packit 40b132
    /* sqlite3 doesn't have a flag to specify that we want to 
Packit 40b132
     * open the database read only. If the db doesn't exist,
Packit 40b132
     * sqlite3 will always create it.
Packit 40b132
     */
Packit 40b132
    LOCK_SQLITE();
Packit 40b132
    create = (PR_Access(dbname, PR_ACCESS_EXISTS) != PR_SUCCESS);
Packit 40b132
    if ((flags == SDB_RDONLY) && create) {
Packit 40b132
	error = sdb_mapSQLError(type, SQLITE_CANTOPEN);
Packit 40b132
	goto loser;
Packit 40b132
    }
Packit 40b132
    sqlerr = sdb_openDB(dbname, &sqlDB, flags);
Packit 40b132
    if (sqlerr != SQLITE_OK) {
Packit 40b132
	error = sdb_mapSQLError(type, sqlerr); 
Packit 40b132
	goto loser;
Packit 40b132
    }
Packit 40b132
    /* sql created the file, but it doesn't set appropriate modes for
Packit 40b132
     * a database */
Packit 40b132
    if (create) {
Packit 40b132
	/* NO NSPR call for this? :( */
Packit 40b132
	chmod (dbname, 0600);
Packit 40b132
    }
Packit 40b132
Packit 40b132
    if (flags != SDB_RDONLY) {
Packit 40b132
	sqlerr = sqlite3_exec(sqlDB, BEGIN_CMD, NULL, 0, NULL);
Packit 40b132
	if (sqlerr != SQLITE_OK) {
Packit 40b132
	    error = sdb_mapSQLError(type, sqlerr);
Packit 40b132
	    goto loser;
Packit 40b132
	}
Packit 40b132
	inTransaction = 1;
Packit 40b132
    }
Packit 40b132
    if (!tableExists(sqlDB,table)) {
Packit 40b132
	*newInit = 1;
Packit 40b132
	if (flags != SDB_CREATE) {
Packit 40b132
	    error = sdb_mapSQLError(type, SQLITE_CANTOPEN);
Packit 40b132
	    goto loser;
Packit 40b132
	}
Packit 40b132
	initStr = sqlite3_mprintf("");
Packit 40b132
	for (i=0; initStr && i < known_attributes_size; i++) {
Packit 40b132
	    newStr = sqlite3_mprintf("%s, a%x",initStr, known_attributes[i]);
Packit 40b132
	    sqlite3_free(initStr);
Packit 40b132
	    initStr = newStr;
Packit 40b132
	}
Packit 40b132
	if (initStr == NULL) {
Packit 40b132
	    error = CKR_HOST_MEMORY;
Packit 40b132
	    goto loser;
Packit 40b132
	}
Packit 40b132
Packit 40b132
	newStr = sqlite3_mprintf(INIT_CMD, table, initStr);
Packit 40b132
	sqlite3_free(initStr);
Packit 40b132
	if (newStr == NULL) {
Packit 40b132
            error = CKR_HOST_MEMORY;
Packit 40b132
	    goto loser;
Packit 40b132
	}
Packit 40b132
	sqlerr = sqlite3_exec(sqlDB, newStr, NULL, 0, NULL);
Packit 40b132
	sqlite3_free(newStr);
Packit 40b132
	if (sqlerr != SQLITE_OK) {
Packit 40b132
            error = sdb_mapSQLError(type, sqlerr); 
Packit 40b132
	    goto loser;
Packit 40b132
	}
Packit 40b132
Packit 40b132
	newStr = sqlite3_mprintf(CREATE_ISSUER_INDEX_CMD, table);
Packit 40b132
	if (newStr == NULL) {
Packit 40b132
            error = CKR_HOST_MEMORY;
Packit 40b132
	    goto loser;
Packit 40b132
	}
Packit 40b132
	sqlerr = sqlite3_exec(sqlDB, newStr, NULL, 0, NULL);
Packit 40b132
	sqlite3_free(newStr);
Packit 40b132
	if (sqlerr != SQLITE_OK) {
Packit 40b132
            error = sdb_mapSQLError(type, sqlerr); 
Packit 40b132
	    goto loser;
Packit 40b132
	}
Packit 40b132
Packit 40b132
	newStr = sqlite3_mprintf(CREATE_SUBJECT_INDEX_CMD, table);
Packit 40b132
	if (newStr == NULL) {
Packit 40b132
            error = CKR_HOST_MEMORY;
Packit 40b132
	    goto loser;
Packit 40b132
	}
Packit 40b132
	sqlerr = sqlite3_exec(sqlDB, newStr, NULL, 0, NULL);
Packit 40b132
	sqlite3_free(newStr);
Packit 40b132
	if (sqlerr != SQLITE_OK) {
Packit 40b132
            error = sdb_mapSQLError(type, sqlerr); 
Packit 40b132
	    goto loser;
Packit 40b132
	}
Packit 40b132
Packit 40b132
	newStr = sqlite3_mprintf(CREATE_LABEL_INDEX_CMD, table);
Packit 40b132
	if (newStr == NULL) {
Packit 40b132
            error = CKR_HOST_MEMORY;
Packit 40b132
	    goto loser;
Packit 40b132
	}
Packit 40b132
	sqlerr = sqlite3_exec(sqlDB, newStr, NULL, 0, NULL);
Packit 40b132
	sqlite3_free(newStr);
Packit 40b132
	if (sqlerr != SQLITE_OK) {
Packit 40b132
            error = sdb_mapSQLError(type, sqlerr); 
Packit 40b132
	    goto loser;
Packit 40b132
	}
Packit 40b132
Packit 40b132
	newStr = sqlite3_mprintf(CREATE_ID_INDEX_CMD, table);
Packit 40b132
	if (newStr == NULL) {
Packit 40b132
            error = CKR_HOST_MEMORY;
Packit 40b132
	    goto loser;
Packit 40b132
	}
Packit 40b132
	sqlerr = sqlite3_exec(sqlDB, newStr, NULL, 0, NULL);
Packit 40b132
	sqlite3_free(newStr);
Packit 40b132
	if (sqlerr != SQLITE_OK) {
Packit 40b132
            error = sdb_mapSQLError(type, sqlerr); 
Packit 40b132
	    goto loser;
Packit 40b132
	}
Packit 40b132
    }
Packit 40b132
    /*
Packit 40b132
     * detect the case where we have created the database, but have
Packit 40b132
     * not yet updated it.
Packit 40b132
     *
Packit 40b132
     * We only check the Key database because only the key database has
Packit 40b132
     * a metaData table. The metaData table is created when a password
Packit 40b132
     * is set, or in the case of update, when a password is supplied.
Packit 40b132
     * If no key database exists, then the update would have happened immediately
Packit 40b132
     * on noticing that the cert database didn't exist (see newInit set above).
Packit 40b132
     */
Packit 40b132
    if (type == SDB_KEY && !tableExists(sqlDB, "metaData")) {
Packit 40b132
	*newInit = 1;
Packit 40b132
    }
Packit 40b132
    
Packit 40b132
    /* access to network filesystems are significantly slower than local ones
Packit 40b132
     * for database operations. In those cases we need to create a cached copy
Packit 40b132
     * of the database in a temporary location on the local disk. SQLITE
Packit 40b132
     * already provides a way to create a temporary table and initialize it,
Packit 40b132
     * so we use it for the cache (see sdb_buildCache for how it's done).*/
Packit 40b132
Packit 40b132
     /* 
Packit 40b132
      * we decide whether or not to use the cache based on the following input.
Packit 40b132
      *
Packit 40b132
      * NSS_SDB_USE_CACHE environment variable is non-existant or set to 
Packit 40b132
      *   anything other than "no" or "yes" ("auto", for instance).
Packit 40b132
      *   This is the normal case. NSS will measure the performance of access
Packit 40b132
      *   to the temp database versus the access to the users passed in 
Packit 40b132
      *   database location. If the temp database location is "significantly"
Packit 40b132
      *   faster we will use the cache.
Packit 40b132
      *
Packit 40b132
      * NSS_SDB_USE_CACHE environment variable is set to "no": cache will not
Packit 40b132
      *   be used.
Packit 40b132
      *
Packit 40b132
      * NSS_SDB_USE_CACHE environment variable is set to "yes": cache will
Packit 40b132
      *   always be used.
Packit 40b132
      *
Packit 40b132
      * It is expected that most applications would use the "auto" selection,
Packit 40b132
      * the environment variable is primarily to simplify testing, and to 
Packit 40b132
      * correct potential corner cases where  */
Packit 40b132
Packit 40b132
     env = PR_GetEnv("NSS_SDB_USE_CACHE");
Packit 40b132
Packit 40b132
     if (env && PORT_Strcasecmp(env,"no") == 0) {
Packit 40b132
	enableCache = PR_FALSE;
Packit 40b132
     } else if (env && PORT_Strcasecmp(env,"yes") == 0) {
Packit 40b132
	enableCache = PR_TRUE;
Packit 40b132
     } else {
Packit 40b132
	char *tempDir = NULL;
Packit 40b132
	PRUint32 tempOps = 0;
Packit 40b132
	/*
Packit 40b132
	 *  Use PR_Access to determine how expensive it
Packit 40b132
	 * is to check for the existance of a local file compared to the same
Packit 40b132
	 * check in the temp directory. If the temp directory is faster, cache
Packit 40b132
	 * the database there. */
Packit 40b132
	tempDir = sdb_getTempDir(sqlDB);
Packit 40b132
	if (tempDir) {
Packit 40b132
	    tempOps = sdb_measureAccess(tempDir);
Packit 40b132
	    PORT_Free(tempDir);
Packit 40b132
Packit 40b132
	    /* There is a cost to continually copying the database. 
Packit 40b132
	     * Account for that cost  with the arbitrary factor of 10 */
Packit 40b132
	    enableCache = (PRBool)(tempOps > accessOps * 10);
Packit 40b132
	}
Packit 40b132
    }
Packit 40b132
Packit 40b132
    if (enableCache) {
Packit 40b132
	/* try to set the temp store to memory.*/
Packit 40b132
	sqlite3_exec(sqlDB, "PRAGMA temp_store=MEMORY", NULL, 0, NULL);
Packit 40b132
	/* Failure to set the temp store to memory is not fatal,
Packit 40b132
         * ignore the error */
Packit 40b132
Packit 40b132
	cacheTable = sqlite3_mprintf("%sCache",table);
Packit 40b132
	if (cacheTable == NULL) {
Packit 40b132
	    error = CKR_HOST_MEMORY;
Packit 40b132
	    goto loser;
Packit 40b132
	}
Packit 40b132
	/* build the cache table */
Packit 40b132
	error = sdb_buildCache(sqlDB, type, cacheTable, table);
Packit 40b132
	if (error != CKR_OK) {
Packit 40b132
	    goto loser;
Packit 40b132
	}
Packit 40b132
	/* initialize the last cache build time */
Packit 40b132
	now = PR_IntervalNow();
Packit 40b132
    }
Packit 40b132
Packit 40b132
    sdb = (SDB *) malloc(sizeof(SDB));
Packit 40b132
    sdb_p = (SDBPrivate *) malloc(sizeof(SDBPrivate));
Packit 40b132
Packit 40b132
    /* invariant fields */
Packit 40b132
    sdb_p->sqlDBName = PORT_Strdup(dbname);
Packit 40b132
    sdb_p->type = type;
Packit 40b132
    sdb_p->table = table;
Packit 40b132
    sdb_p->cacheTable = cacheTable;
Packit 40b132
    sdb_p->lastUpdateTime = now;
Packit 40b132
    /* set the cache delay time. This is how long we will wait before we
Packit 40b132
     * decide the existing cache is stale. Currently set to 10 sec */
Packit 40b132
    sdb_p->updateInterval = PR_SecondsToInterval(10); 
Packit 40b132
    sdb_p->dbMon = PR_NewMonitor();
Packit 40b132
    /* these fields are protected by the lock */
Packit 40b132
    sdb_p->sqlXactDB = NULL;
Packit 40b132
    sdb_p->sqlXactThread = NULL;
Packit 40b132
    sdb->private = sdb_p;
Packit 40b132
    sdb->version = 0;
Packit 40b132
    sdb->sdb_flags = flags | SDB_HAS_META;
Packit 40b132
    sdb->app_private = NULL;
Packit 40b132
    sdb->sdb_FindObjectsInit = sdb_FindObjectsInit;
Packit 40b132
    sdb->sdb_FindObjects = sdb_FindObjects;
Packit 40b132
    sdb->sdb_FindObjectsFinal = sdb_FindObjectsFinal;
Packit 40b132
    sdb->sdb_GetAttributeValue = sdb_GetAttributeValue;
Packit 40b132
    sdb->sdb_SetAttributeValue = sdb_SetAttributeValue;
Packit 40b132
    sdb->sdb_CreateObject = sdb_CreateObject;
Packit 40b132
    sdb->sdb_DestroyObject = sdb_DestroyObject;
Packit 40b132
    sdb->sdb_GetMetaData = sdb_GetMetaData;
Packit 40b132
    sdb->sdb_PutMetaData = sdb_PutMetaData;
Packit 40b132
    sdb->sdb_Begin = sdb_Begin;
Packit 40b132
    sdb->sdb_Commit = sdb_Commit;
Packit 40b132
    sdb->sdb_Abort = sdb_Abort;
Packit 40b132
    sdb->sdb_Reset = sdb_Reset;
Packit 40b132
    sdb->sdb_Close = sdb_Close;
Packit 40b132
    sdb->sdb_SetForkState = sdb_SetForkState;
Packit 40b132
Packit 40b132
    if (inTransaction) {
Packit 40b132
	sqlerr = sqlite3_exec(sqlDB, COMMIT_CMD, NULL, 0, NULL);
Packit 40b132
	if (sqlerr != SQLITE_OK) {
Packit 40b132
	    error = sdb_mapSQLError(sdb_p->type, sqlerr);
Packit 40b132
	    goto loser;
Packit 40b132
	}
Packit 40b132
	inTransaction = 0;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    sdb_p->sqlReadDB = sqlDB;
Packit 40b132
Packit 40b132
    *pSdb = sdb;
Packit 40b132
    UNLOCK_SQLITE();
Packit 40b132
    return CKR_OK;
Packit 40b132
Packit 40b132
loser:
Packit 40b132
    /* lots of stuff to do */
Packit 40b132
    if (inTransaction) {
Packit 40b132
	sqlite3_exec(sqlDB, ROLLBACK_CMD, NULL, 0, NULL);
Packit 40b132
    }
Packit 40b132
    if (sdb) {
Packit 40b132
	free(sdb);
Packit 40b132
    }
Packit 40b132
    if (sdb_p) {
Packit 40b132
	free(sdb_p);
Packit 40b132
    }
Packit 40b132
    if (sqlDB) {
Packit 40b132
	sqlite3_close(sqlDB);
Packit 40b132
    }
Packit 40b132
    UNLOCK_SQLITE();
Packit 40b132
    return error;
Packit 40b132
Packit 40b132
}
Packit 40b132
Packit 40b132
Packit 40b132
/* sdbopen */
Packit 40b132
CK_RV
Packit 40b132
s_open(const char *directory, const char *certPrefix, const char *keyPrefix,
Packit 40b132
	int cert_version, int key_version, int flags, 
Packit 40b132
	SDB **certdb, SDB **keydb, int *newInit)
Packit 40b132
{
Packit 40b132
    char *cert = sdb_BuildFileName(directory, certPrefix,
Packit 40b132
				   "cert", cert_version);
Packit 40b132
    char *key = sdb_BuildFileName(directory, keyPrefix,
Packit 40b132
				   "key", key_version);
Packit 40b132
    CK_RV error = CKR_OK;
Packit 40b132
    int inUpdate;
Packit 40b132
    PRUint32 accessOps;
Packit 40b132
Packit 40b132
    if (certdb) 
Packit 40b132
	*certdb = NULL;
Packit 40b132
    if (keydb) 
Packit 40b132
	*keydb = NULL;
Packit 40b132
    *newInit = 0;
Packit 40b132
Packit 40b132
#ifdef SQLITE_UNSAFE_THREADS
Packit 40b132
    if (sqlite_lock == NULL) {
Packit 40b132
	sqlite_lock = PR_NewLock();
Packit 40b132
	if (sqlite_lock == NULL) {
Packit 40b132
	    error = CKR_HOST_MEMORY;
Packit 40b132
	    goto loser;
Packit 40b132
	}
Packit 40b132
    }
Packit 40b132
#endif
Packit 40b132
Packit 40b132
    /* how long does it take to test for a non-existant file in our working
Packit 40b132
     * directory? Allows us to test if we may be on a network file system */
Packit 40b132
    accessOps = 1;
Packit 40b132
    {
Packit 40b132
        char *env;
Packit 40b132
        env = PR_GetEnv("NSS_SDB_USE_CACHE");
Packit 40b132
        /* If the environment variable is set to yes or no, sdb_init() will
Packit 40b132
         * ignore the value of accessOps, and we can skip the measuring.*/
Packit 40b132
        if (!env || ((PORT_Strcasecmp(env, "no") != 0) &&
Packit 40b132
                     (PORT_Strcasecmp(env, "yes") != 0))){
Packit 40b132
           accessOps = sdb_measureAccess(directory);
Packit 40b132
        }
Packit 40b132
    }
Packit 40b132
Packit 40b132
    /*
Packit 40b132
     * open the cert data base
Packit 40b132
     */
Packit 40b132
    if (certdb) {
Packit 40b132
	/* initialize Certificate database */
Packit 40b132
	error = sdb_init(cert, "nssPublic", SDB_CERT, &inUpdate,
Packit 40b132
			 newInit, flags, accessOps, certdb);
Packit 40b132
	if (error != CKR_OK) {
Packit 40b132
	    goto loser;
Packit 40b132
	}
Packit 40b132
    }
Packit 40b132
Packit 40b132
    /*
Packit 40b132
     * open the key data base: 
Packit 40b132
     *  NOTE:if we want to implement a single database, we open
Packit 40b132
     *  the same database file as the certificate here.
Packit 40b132
     *
Packit 40b132
     *  cert an key db's have different tables, so they will not
Packit 40b132
     *  conflict.
Packit 40b132
     */
Packit 40b132
    if (keydb) {
Packit 40b132
	/* initialize the Key database */
Packit 40b132
	error = sdb_init(key, "nssPrivate", SDB_KEY, &inUpdate, 
Packit 40b132
			newInit, flags, accessOps, keydb);
Packit 40b132
	if (error != CKR_OK) {
Packit 40b132
	    goto loser;
Packit 40b132
	} 
Packit 40b132
    }
Packit 40b132
Packit 40b132
Packit 40b132
loser:
Packit 40b132
    if (cert) {
Packit 40b132
	sqlite3_free(cert);
Packit 40b132
    }
Packit 40b132
    if (key) {
Packit 40b132
	sqlite3_free(key);
Packit 40b132
    }
Packit 40b132
Packit 40b132
    if (error != CKR_OK) {
Packit 40b132
	/* currently redundant, but could be necessary if more code is added
Packit 40b132
	 * just before loser */
Packit 40b132
	if (keydb && *keydb) {
Packit 40b132
	    sdb_Close(*keydb);
Packit 40b132
	}
Packit 40b132
	if (certdb && *certdb) {
Packit 40b132
	    sdb_Close(*certdb);
Packit 40b132
	}
Packit 40b132
    }
Packit 40b132
Packit 40b132
    return error;
Packit 40b132
}
Packit 40b132
Packit 40b132
CK_RV
Packit 40b132
s_shutdown()
Packit 40b132
{
Packit 40b132
#ifdef SQLITE_UNSAFE_THREADS
Packit 40b132
    if (sqlite_lock) {
Packit 40b132
	PR_DestroyLock(sqlite_lock);
Packit 40b132
	sqlite_lock = NULL;
Packit 40b132
    }
Packit 40b132
#endif
Packit 40b132
    return CKR_OK;
Packit 40b132
}