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