/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* *ThisfileimplementsPKCS11ontopofourexistingsecuritymodules * *FormoreinformationaboutPKCS11SeePKCS11TokenIntefaceStandard. *Thisimplementationhastwoslots: *slot1isourgenericcryptosupport.Itdoesnotrequirelogin. *ItsupportsPublicKeyops,andalltheybulkciphersandhashes. *ItcanalsosupportPrivateKeyopsforimportedPrivatekeys.Itdoes *nothaveanytokenstorage. *slot2isourprivatekeysupport.Itrequiresaloginbeforeuse.It *canstorePrivateKeysandCertsastokenobjects.Currentlyonlyprivate *keysandtheirassociatedCertificatesaresavedonthetoken. * *Inthisimplementation,sessionobjectsareonlyvisibletothesession *thatcreatedorgeneratedthem.
*/
/* *NoteonuseofsqlReadDB:Onlyonethreadatatimemayhaveanactual *operationgoingongivensqlite3*database.Anoperationisdefinedas *thetimefromasqlite3_prepare()untilthesqlite3_finalize(). *Multiplesqlite3*databasescanbeopenandhavesimultaneousoperations *going.WeusethesqlXactDBforallwriteoperations.Thisdatabase *isonlyopenedwhenwefirstcreateatransactionandclosedwhenthe *transactioniscomplete.sqlReadDBisopenwhenwefirstopenedthedatabase *andisusedforallreadoperation.It'suseisprotectedbyamonitor.This *isbecauseanoperationcanspantheuseofFindObjectsInit()throughthe *calltoFindObjectsFinal().Intheintermediatetimeitispossibletocall
* other operations like NSC_GetAttributeValue */
struct SDBPrivateStr { char *sqlDBName; /* invariant, path to this database */
sqlite3 *sqlXactDB; /* access protected by dbMon, use protected
* by the transaction. Current transaction db*/
PRThread *sqlXactThread; /* protected by dbMon,
* current transaction thread */
sqlite3 *sqlReadDB; /* use protected by dbMon, value invariant */
PRIntervalTime lastUpdateTime; /* last time the cache was updated */
PRIntervalTime updateInterval; /* how long the cache can go before it
* must be updated again */
sdbDataType type; /* invariant, database type */ char *table; /* invariant, SQL table which contains the db */ char *cacheTable; /* invariant, SQL table cache of db */
PRMonitor *dbMon; /* invariant, monitor to protect
* sqlXact* fields, and use of the sqlReadDB */
CK_ATTRIBUTE_TYPE *schemaAttrs; /* Attribute columns that exist in the table. */ unsignedint numSchemaAttrs;
};
typedefstruct SDBPrivateStr SDBPrivate;
/* Magic for an explicit NULL. NOTE: ideally this should be *outofbanddata.Sinceit'snotcompletelyoutofband,pick *avaluethathasnomeaningtoanyexistingPKCS#11attributes. *Thisvalueis1)notavalidstring(imbedded'\0').2)notaU_LONG *oranormalkey(tooshort).3)notabool(toolong).4)notanRSA *publicexponent(toomanybits).
*/ constunsignedchar SQLITE_EXPLICIT_NULL[] = { 0xa5, 0x0, 0x5a }; #define SQLITE_EXPLICIT_NULL_LEN 3
/* *determinewhenwe'vecompletedourtasks
*/ staticint
sdb_done(int err, int *count)
{ /* allow as many rows as the database wants to give */ if (err == SQLITE_ROW) {
*count = 0; return0;
} if (err != SQLITE_BUSY) { return1;
} /* err == SQLITE_BUSY, Dont' retry forever in this case */ if (++(*count) >= SDB_MAX_BUSY_RETRIES) { return1;
} return0;
}
for (i = 0; i < PR_ARRAY_SIZE(azDirs); i++) {
zDir = azDirs[i]; if (zDir == NULL) continue; if (stat(zDir, &buf)) continue; if (!S_ISDIR(buf.st_mode)) continue; if (access(zDir, 07)) continue; break;
}
if (zDir == NULL) return NULL; return PORT_Strdup(zDir);
} #else #error"sdb_getFallbackTempDir not implemented" #endif
#ifndef SQLITE_FCNTL_TEMPFILENAME /* SQLITE_FCNTL_TEMPFILENAME was added in SQLite 3.7.15 */ #define SQLITE_FCNTL_TEMPFILENAME 16 #endif
/* Obtain temporary filename in sqlite's directory for temporary tables */
sqlrv = sqlite3_file_control(sqlDB, 0, SQLITE_FCNTL_TEMPFILENAME,
(void *)&tempName); if (sqlrv == SQLITE_NOTFOUND) { /* SQLITE_FCNTL_TEMPFILENAME not implemented because we are using
* an older SQLite. */ return sdb_getFallbackTempDir();
} if (sqlrv != SQLITE_OK) { return NULL;
}
/* We'll extract the temporary directory from tempName */
foundSeparator = PORT_Strrchr(tempName, PR_GetDirectorySeparator()); if (foundSeparator) { /* We shorten the temp filename string to contain only *thedirectoryname(includingthetrailingseparator). *WeknowthebyteafterthefoundSeparatorpositionis *safetouse,intheshortestscenarioitcontainsthe *end-of-stringbyte. *Bykeepingtheseparatoratthefoundposition,itwill *evenworkiftempDirconsistsoftheseparator,only. *(Inthiscasethetopleveldirectorywillbeusedfor
* access speed testing). */
++foundSeparator;
*foundSeparator = 0;
/* Now we copy the directory name for our caller */
result = PORT_Strdup(tempName);
}
sqlite3_free(tempName); return result;
}
/* *MapSQL_LITEerrorstoPKCS#11errorsasbestwecan.
*/ static CK_RV
sdb_mapSQLError(sdbDataType type, int sqlerr)
{ switch (sqlerr) { /* good matches */ case SQLITE_OK: case SQLITE_DONE: return CKR_OK; case SQLITE_NOMEM: return CKR_HOST_MEMORY; case SQLITE_READONLY: return CKR_TOKEN_WRITE_PROTECTED; /* close matches */ case SQLITE_AUTH: case SQLITE_PERM: /*return CKR_USER_NOT_LOGGED_IN; */ case SQLITE_CANTOPEN: case SQLITE_NOTFOUND: /* NSS distiguishes between failure to open the cert and the key db */ return type == SDB_CERT ? CKR_NSS_CERTDB_FAILED : CKR_NSS_KEYDB_FAILED; case SQLITE_IOERR: return CKR_DEVICE_ERROR; default: break;
} return CKR_GENERAL_ERROR;
}
/* our calculation assumes time is a 4 bytes == 32 bit integer */
PORT_Assert(sizeof(time) == 4);
directoryLength = strlen(directory);
maxTempLen = directoryLength + 1/* dirname + / */
+ tmpdirLength /* tmpdirname includes / */
+ strlen(doesntExistName) /* filename base */
+ 11/* max chars for 32 bit int plus potential sign */
+ 1; /* zero terminator */
temp = PORT_ZAlloc(maxTempLen); if (!temp) { return1;
}
/* We'll copy directory into temp just once, then ensure it ends
* with the directory separator. */
#ifdef SDB_MEASURE_USE_TEMP_DIR /* add the template for a temporary subdir, and create it */
strcat(temp, template); if (!mkdtemp(temp)) {
PORT_Free(temp); return1;
} /* and terminate that tmp subdir with a / */
strcat(temp, "/"); #endif
/* Remember the position after the last separator, and calculate the
* number of remaining bytes. */
tempStartOfFilename = temp + directoryLength + tmpdirLength;
maxFileNameLen = maxTempLen - directoryLength;
/* measure number of Access operations that can be done in 33 milliseconds *(1/30'thofasecond),or10000operations,whichevercomesfirst.
*/
time = PR_IntervalNow(); for (i = 0; i < 10000u; i++) {
PRIntervalTime next;
/* We'll use the variable part first in the filename string, just in *caseit'slongerthanassumed,soifanythinggetscutoff,it *willbecutofffromtheconstantpart. *Thiscodeassumesthedirectorynameatthebeginningof
* temp remains unchanged during our loop. */
PR_snprintf(tempStartOfFilename, maxFileNameLen, ".%lu%s", (PRUint32)(time + i), doesntExistName);
PR_Access(temp, PR_ACCESS_EXISTS);
next = PR_IntervalNow();
delta = next - time; if (delta >= duration) break;
}
#ifdef SDB_MEASURE_USE_TEMP_DIR /* turn temp back into our tmpdir path by removing doesntExistName, and
* remove the tmp dir */
*tempStartOfFilename = '\0';
(void)rmdir(temp); #endif
PORT_Free(temp);
/* always return 1 or greater */ return i ? i : 1u;
}
/* *somefilesytemsareveryslowtorunsqlite3on,particularlyifthe *accesscountisprettyhigh.Onthesefilesystemsisfastertocreate *atemporarydatabaseonthelocalfilesystemandaccessthat.This *codeusesatemporarytabletocreatethatcache.Temptablesare *automaticallyclearedwhenthedatabasehandleitwascreatedon *Isfreed.
*/ staticconstchar DROP_CACHE_CMD[] = "DROP TABLE %s"; staticconstchar CREATE_CACHE_CMD[] = "CREATE TEMPORARY TABLE %s AS SELECT * FROM %s"; staticconstchar CREATE_ISSUER_INDEX_CMD[] = "CREATE INDEX issuer ON %s (a81)"; staticconstchar CREATE_SUBJECT_INDEX_CMD[] = "CREATE INDEX subject ON %s (a101)"; staticconstchar CREATE_LABEL_INDEX_CMD[] = "CREATE INDEX label ON %s (a3)"; staticconstchar CREATE_ID_INDEX_CMD[] = "CREATE INDEX ckaid ON %s (a102)";
/* drop the old table */
newStr = sqlite3_mprintf(DROP_CACHE_CMD, sdb_p->cacheTable); if (newStr == NULL) { return CKR_HOST_MEMORY;
}
sqlerr = sqlite3_exec(sdb_p->sqlReadDB, newStr, NULL, 0, NULL);
sqlite3_free(newStr); if ((sqlerr != SQLITE_OK) && (sqlerr != SQLITE_ERROR)) { /* something went wrong with the drop, don't try to refresh... *NOTE:SQLITE_ERRORisreturnedifthetabledoesn'texist.In
* that case, we just continue on and try to reload it */ return sdb_mapSQLError(sdb_p->type, sqlerr);
}
/* set up the new table */
error = sdb_buildCache(sdb_p->sqlReadDB, sdb_p->type,
sdb_p->cacheTable, sdb_p->table); if (error == CKR_OK) { /* we have a new cache! */
sdb_p->lastUpdateTime = PR_IntervalNow();
} return error;
}
/* We're in a transaction, use the transaction DB */ if ((sdb_p->sqlXactDB) && (sdb_p->sqlXactThread == PR_GetCurrentThread())) {
*sqlDB = sdb_p->sqlXactDB; /* only one thread can get here, safe to unlock */
PR_ExitMonitor(sdb_p->dbMon); return CKR_OK;
}
/* *ifwearejustreadingfromthetable,wemayhavethetable *cachedinatemporarytable(especiallyifit'sonasharedFS). *Inthatcasewewanttoseeupdatestothetable,thethegranularity *isonorderofhumanscale,notcomputerscale.
*/ if (table && sdb_p->cacheTable) {
PRIntervalTime now = PR_IntervalNow(); if ((now - sdb_p->lastUpdateTime) > sdb_p->updateInterval) {
sdb_updateCache(sdb_p);
}
*table = sdb_p->cacheTable;
}
*sqlDB = sdb_p->sqlReadDB;
/* leave holding the lock. only one thread can actually use a given
* database connection at once */
return CKR_OK;
}
/* closing the local database currenly means unlocking the monitor */ static CK_RV
sdb_closeDBLocal(SDBPrivate *sdb_p, sqlite3 *sqlDB)
{ if (sdb_p->sqlXactDB != sqlDB) { /* if we weren't in a transaction, we got a lock */
PR_ExitMonitor(sdb_p->dbMon);
} return CKR_OK;
}
/* *wrappertosqlite3_openwhichalsosetsthebusy_timeout
*/ staticint
sdb_openDB(constchar *name, sqlite3 **sqlDB, int flags)
{ int sqlerr; int openFlags;
*sqlDB = NULL;
if (flags & SDB_RDONLY) {
openFlags = SQLITE_OPEN_READONLY;
} else {
openFlags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE; /* sqlite 3.34 seem to incorrectly open readwrite.
* when the file is readonly. Explicitly reject that issue here */ if ((_NSSUTIL_Access(name, PR_ACCESS_EXISTS) == PR_SUCCESS) && (_NSSUTIL_Access(name, PR_ACCESS_WRITE_OK) != PR_SUCCESS)) { return SQLITE_READONLY;
}
}
/* Sigh, if we created a new table since we opened the database, *thedatabasehandlewillnotseethenewtable,weneedtoclosethis *databaseandreopenit.Callermustbeinatransactionorholding
* the dbMon. sqlDB is changed on success. */ staticint
sdb_reopenDBLocal(SDBPrivate *sdb_p, sqlite3 **sqlDB)
{
sqlite3 *newDB; int sqlerr;
/* open a new database */
sqlerr = sdb_openDB(sdb_p->sqlDBName, &newDB, SDB_RDONLY); if (sqlerr != SQLITE_OK) { return sqlerr;
}
/* if we are in a transaction, we may not be holding the monitor. *grabitbeforeweupdatethetransactiondatabase.Thisis
* safe since are using monitors. */
PR_EnterMonitor(sdb_p->dbMon); /* update our view of the database */ if (sdb_p->sqlReadDB == *sqlDB) {
sdb_p->sqlReadDB = newDB;
} elseif (sdb_p->sqlXactDB == *sqlDB) {
sdb_p->sqlXactDB = newDB;
}
PR_ExitMonitor(sdb_p->dbMon);
if (arraySize == 0) { return CKR_OK;
}
LOCK_SQLITE()
do {
sqlerr = sqlite3_step(stmt); if (sqlerr == SQLITE_BUSY) {
PR_Sleep(SDB_BUSY_RETRY_TIME);
} if (sqlerr == SQLITE_ROW) { /* only care about the id */
*object++ = sqlite3_column_int(stmt, 0);
arraySize--;
(*count)++;
}
} while (!sdb_done(sqlerr, &retry) && (arraySize > 0));
/* we only have some of the objects, there is probably more,
* set the sqlerr to an OK value so we return CKR_OK */ if (sqlerr == SQLITE_ROW && arraySize == 0) {
sqlerr = SQLITE_DONE;
}
UNLOCK_SQLITE()
// NB: indices in sqlite3_bind_int are 1-indexed
sqlerr = sqlite3_bind_int(stmt, 1, object_id); if (sqlerr != SQLITE_OK) { goto loser;
}
do {
sqlerr = sqlite3_step(stmt); if (sqlerr == SQLITE_BUSY) {
PR_Sleep(SDB_BUSY_RETRY_TIME);
} if (sqlerr == SQLITE_ROW) {
PORT_Assert(!found); for (i = 0; i < count; i++) { unsignedint blobSize; constchar *blobData;
// NB: indices in sqlite_column_{bytes,blob} are 0-indexed
blobSize = sqlite3_column_bytes(stmt, i);
blobData = sqlite3_column_blob(stmt, i); if (blobData == NULL) { /* PKCS 11 requires that get attributes process all the *attributesinthetemplate,markingtheattributeswith
* issues with -1. Mark the error but continue */ template[i].ulValueLen = -1;
error = CKR_ATTRIBUTE_TYPE_INVALID; continue;
} /* If the blob equals our explicit NULL value, then the
* attribute is a NULL. */ if ((blobSize == SQLITE_EXPLICIT_NULL_LEN) &&
(PORT_Memcmp(blobData, SQLITE_EXPLICIT_NULL,
SQLITE_EXPLICIT_NULL_LEN) == 0)) {
blobSize = 0;
} if (template[i].pValue) { if (template[i].ulValueLen < blobSize) { /* like CKR_ATTRIBUTE_TYPE_INVALID, continue processing */ template[i].ulValueLen = -1;
error = CKR_BUFFER_TOO_SMALL; continue;
}
PORT_Memcpy(template[i].pValue, blobData, blobSize);
} template[i].ulValueLen = blobSize;
}
found = 1;
}
} while (!sdb_done(sqlerr, &retry));
if (!invalidExists) {
validTemplate = template;
validCount = count;
} else { /* Create a new template containing only the valid subset of
* input |template|, and query with that. */
validCount = tmplIdx;
validTemplate = malloc(sizeof(CK_ATTRIBUTE) * count); if (!validTemplate) { return CKR_HOST_MEMORY;
} /* Copy in what we already know is valid. */ for (i = 0; i < validCount; i++) {
validTemplate[i] = template[i];
}
/* tmplIdx was left at the index of the first invalid *attribute,whichhasbeenhandled.Weonlyneedto
* deal with the remainder. */
tmplIdx++; for (; tmplIdx < count; tmplIdx++) { if (sdb_attributeExists(sdb, template[tmplIdx].type)) {
validTemplate[validCount++] = template[tmplIdx];
} else { template[tmplIdx].ulValueLen = -1;
}
}
}
/* If an invalid attribute was removed above, let *thecallerknow.Anyothererrorfromtheactual
* query should propogate. */
crv = (crv2 == CKR_OK) ? crv : crv2;
}
if (invalidExists) { /* Copy out valid lengths. */
tmplIdx = 0; for (resIdx = 0; resIdx < validCount; resIdx++) { for (; tmplIdx < count; tmplIdx++) { if (template[tmplIdx].type != validTemplate[resIdx].type) { continue;
} template[tmplIdx].ulValueLen = validTemplate[resIdx].ulValueLen;
tmplIdx++; break;
}
}
free(validTemplate);
}
next_obj = (CK_OBJECT_HANDLE)(time & 0x3fffffffL);
}
candidate = next_obj++; /* detect that we've looped through all the handles... */ for (count = 0; count < 0x40000000; count++, candidate = next_obj++) { /* mask off excess bits */
candidate &= 0x3fffffff; /* if we hit zero, go to the next entry */ if (candidate == CK_INVALID_HANDLE) { continue;
} /* make sure we aren't already using */ if (!sdb_objectExists(sdb, candidate)) { /* this one is free */ return candidate;
}
}
/* no handle is free, fail */ return CK_INVALID_HANDLE;
}
id = sdb_getObjectId(sdb); if (id == CK_INVALID_HANDLE) { return CKR_DEVICE_MEMORY; /* basically we ran out of resources */
}
*object = id; return CKR_OK;
}
if ((sdb->sdb_flags & SDB_RDONLY) != 0) { return CKR_TOKEN_WRITE_PROTECTED;
}
LOCK_SQLITE()
/* get a new version that we will use for the entire transaction */
sqlerr = sdb_openDB(sdb_p->sqlDBName, &sqlDB, SDB_RDWR); if (sqlerr != SQLITE_OK) { goto loser;
}
/* we are starting a new transaction, *andifwesucceeded,thensavethisdatabasefortherestof
* our transaction */ if (error == CKR_OK) { /* we hold a 'BEGIN TRANSACTION' and a sdb_p->lock. At this point
* sdb_p->sqlXactDB MUST be null */
PR_EnterMonitor(sdb_p->dbMon);
PORT_Assert(sdb_p->sqlXactDB == NULL);
sdb_p->sqlXactDB = sqlDB;
sdb_p->sqlXactThread = PR_GetCurrentThread();
PR_ExitMonitor(sdb_p->dbMon);
} else { /* we failed to start our transaction,
* free any databases we opened. */ if (sqlDB) {
sqlite3_close(sqlDB);
}
}
if ((sdb->sdb_flags & SDB_RDONLY) != 0) { return CKR_TOKEN_WRITE_PROTECTED;
}
/* We must have a transation database, or we shouldn't have arrived here */
PR_EnterMonitor(sdb_p->dbMon);
PORT_Assert(sdb_p->sqlXactDB); if (sdb_p->sqlXactDB == NULL) {
PR_ExitMonitor(sdb_p->dbMon); return CKR_GENERAL_ERROR; /* shouldn't happen */
}
PORT_Assert(sdb_p->sqlXactThread == PR_GetCurrentThread()); if (sdb_p->sqlXactThread != PR_GetCurrentThread()) {
PR_ExitMonitor(sdb_p->dbMon); return CKR_GENERAL_ERROR; /* shouldn't happen */
}
sqlDB = sdb_p->sqlXactDB;
sdb_p->sqlXactDB = NULL; /* no one else can get to this DB,
* safe to unlock */
sdb_p->sqlXactThread = NULL;
PR_ExitMonitor(sdb_p->dbMon);
do {
sqlerr = sqlite3_step(stmt); if (sqlerr == SQLITE_BUSY) {
PR_Sleep(SDB_BUSY_RETRY_TIME);
}
} while (!sdb_done(sqlerr, &retry));
/* Pending BEGIN TRANSACTIONS Can move forward at this point. */
if (stmt) {
sqlite3_reset(stmt);
sqlite3_finalize(stmt);
}
/* we we have a cached DB image, update it as well */ if (sdb_p->cacheTable) {
PR_EnterMonitor(sdb_p->dbMon);
sdb_updateCache(sdb_p);
PR_ExitMonitor(sdb_p->dbMon);
}
error = sdb_mapSQLError(sdb_p->type, sqlerr);
/* We just finished a transaction.
* Free the database, and remove it from the list */
sqlite3_close(sqlDB);
void
sdb_SetForkState(PRBool forked)
{ /* XXXright now this is a no-op. The global fork state in the softokn3 *sharedlibraryisalreadytakencareofatthePKCS#11level. *Ifandwhenweaddforkstatetothesqlitesharedlibraryandextern
* interface, we will need to set it and reset it from here */
}
columnCount = sqlite3_column_count(stmt); /* columns include the first column, which is id, which is not *andattribute.checktomakesurewehaveatleastasmanyattributes *asthereareidinoutlist.Thisassumesweneveraddsome *attributesinsomeNSSversionandnotothers,whichisgenerally
* true. */ if (columnCount >= sftkdb_known_attributes_size + 1) {
sqlite3_finalize(stmt); return CKR_OK;
} /* we have more attributes than in the database, so we know things
* are missing, find what was missing */ for (size_t i = 0; i < sftkdb_known_attributes_size; i++) { char *typeString = sqlite3_mprintf("a%lx", sftkdb_known_attributes[i]);
PRBool found = PR_FALSE; /* this one index is important, we skip the first column (id), since *itwillnevermatch,startingatzeroisn'tabug,
* just inefficient */ for (int j = 1; j < columnCount; j++) { constchar *columnName = sqlite3_column_name(stmt, j); if (columnName == NULL) {
sqlite3_free(typeString);
sqlite3_finalize(stmt); /* if we couldnt' get the colmun name, it's only because
* we couldn't get the memory */ return CKR_HOST_MEMORY;
} if (PORT_Strcmp(typeString, columnName) == 0) { /* we found this one, no need to add it */
found = PR_TRUE; break;
}
} if (found) {
sqlite3_free(typeString); continue;
} /* we didn't find the attribute, so now add it */
error = sdb_add_column(sqlDB, table, type, typeString); if (error != CKR_OK) {
sqlite3_free(typeString);
sqlite3_finalize(stmt); return error;
}
sqlite3_free(typeString);
}
sqlite3_finalize(stmt); return CKR_OK;
}
/* *initializeasingledatabase
*/ staticconstchar INIT_CMD[] = "CREATE TABLE %s (id PRIMARY KEY UNIQUE ON CONFLICT ABORT%s)";
newStr = sqlite3_mprintf(CREATE_ID_INDEX_CMD, table); if (newStr == NULL) {
error = CKR_HOST_MEMORY; goto loser;
}
sqlerr = sqlite3_exec(sqlDB, newStr, NULL, 0, NULL);
sqlite3_free(newStr); if (sqlerr != SQLITE_OK) {
error = sdb_mapSQLError(type, sqlerr); goto loser;
}
} elseif (flags != SDB_RDONLY) { /* check to see if we need to update the scheme, only need to *dothisifweopenr/w,sincethat'stheonlycasewhere
* it's a problem if the attribute colmumn are missing */
error = sdb_update_column(sqlDB, table, type); if (error != CKR_OK) { goto loser;
}
}
/* access to network filesystems are significantly slower than local ones *fordatabaseoperations.Inthosecasesweneedtocreateacachedcopy *ofthedatabaseinatemporarylocationonthelocaldisk.SQLITE *alreadyprovidesawaytocreateatemporarytableandinitializeit,
* so we use it for the cache (see sdb_buildCache for how it's done).*/
/* Variables enableCache, checkFSType, measureSpeed are PR_FALSE by default, *whichistheexpectedbehaviorforNSS_SDB_USE_CACHE="no".
* We don't need to check for "no" here. */ if (!env) { /* By default, with no variable set, we avoid expensive measuring for *mostFStypes.WestartwithinexpensiveFStypechecking,and
* might perform measuring for some types. */
checkFSType = PR_TRUE;
} elseif (PORT_Strcasecmp(env, "yes") == 0) {
enableCache = PR_TRUE;
} elseif (PORT_Strcasecmp(env, "no") != 0) { /* not "no" => "auto" */
measureSpeed = PR_TRUE;
}
if (checkFSType) { #ifdefined(LINUX) && !defined(ANDROID) struct statfs statfs_s; if (statfs(dbname, &statfs_s) == 0) { switch (statfs_s.f_type) { case SMB_SUPER_MAGIC: case0xff534d42: /* CIFS_MAGIC_NUMBER */ case NFS_SUPER_MAGIC: /* We assume these are slow. */
enableCache = PR_TRUE; break; case CODA_SUPER_MAGIC: case0x65735546: /* FUSE_SUPER_MAGIC */ case NCP_SUPER_MAGIC: /* It's uncertain if this FS is fast or slow. *Itseemsreasonabletoperformslowmeasuringforusers
* with questionable FS speed. */
measureSpeed = PR_TRUE; break; case AFS_SUPER_MAGIC: /* Already implements caching. */ default: break;
}
} #endif
}
if (measureSpeed) { char *tempDir = NULL;
PRUint32 tempOps = 0; /* *UsePR_Accesstodeterminehowexpensiveit *istocheckfortheexistanceofalocalfilecomparedtothesame *checkinthetempdirectory.Ifthetempdirectoryisfaster,cache
* the database there. */
tempDir = sdb_getTempDir(sqlDB); if (tempDir) {
tempOps = sdb_measureAccess(tempDir);
PORT_Free(tempDir);
/* There is a cost to continually copying the database.
* Account for that cost with the arbitrary factor of 10 */
enableCache = (PRBool)(tempOps > accessOps * 10);
}
}
if (enableCache) { /* try to set the temp store to memory.*/
sqlite3_exec(sqlDB, "PRAGMA temp_store=MEMORY", NULL, 0, NULL); /* Failure to set the temp store to memory is not fatal,
* ignore the error */
cacheTable = sqlite3_mprintf("%sCache", table); if (cacheTable == NULL) {
error = CKR_HOST_MEMORY; goto loser;
} /* build the cache table */
error = sdb_buildCache(sqlDB, type, cacheTable, table); if (error != CKR_OK) { goto loser;
} /* initialize the last cache build time */
now = PR_IntervalNow();
}
/* Cache the attributes that are held in the table, so we can later check *thatqueriedattributesactuallyexist.Wedon'tassumetheschema
* to be exactly |sftkdb_known_attributes|, as it may change over time. */
sdb_p->schemaAttrs = NULL; if (!PORT_Strcmp("nssPublic", table) ||
!PORT_Strcmp("nssPrivate", table)) {
sqlite3_stmt *stmt = NULL; int retry = 0; unsignedint backedAttrs = 0;
/* Can't bind parameters to a PRAGMA. */
queryStr = sqlite3_mprintf("PRAGMA table_info(%s);", table); if (queryStr == NULL) {
error = CKR_HOST_MEMORY; goto loser;
}
sqlerr = sqlite3_prepare_v2(sqlDB, queryStr, -1, &stmt, NULL);
sqlite3_free(queryStr);
queryStr = NULL; if (sqlerr != SQLITE_OK) { goto loser;
} unsignedint schemaAttrsCapacity = sftkdb_known_attributes_size;
sdb_p->schemaAttrs = malloc(schemaAttrsCapacity * sizeof(CK_ATTRIBUTE_TYPE)); if (!sdb_p->schemaAttrs) {
error = CKR_HOST_MEMORY; goto loser;
} do {
sqlerr = sqlite3_step(stmt); if (sqlerr == SQLITE_BUSY) {
PR_Sleep(SDB_BUSY_RETRY_TIME);
} if (sqlerr == SQLITE_ROW) { if (backedAttrs == schemaAttrsCapacity) {
schemaAttrsCapacity += sftkdb_known_attributes_size;
sdb_p->schemaAttrs = realloc(sdb_p->schemaAttrs,
schemaAttrsCapacity * sizeof(CK_ATTRIBUTE_TYPE)); if (!sdb_p->schemaAttrs) {
error = CKR_HOST_MEMORY; goto loser;
}
} /* Record the ULONG attribute value. */ char *val = (char *)sqlite3_column_text(stmt, 1); if (val && val[0] == 'a') {
CK_ATTRIBUTE_TYPE attr = strtoul(&val[1], NULL, 16);
sdb_p->schemaAttrs[backedAttrs++] = attr;
}
}
} while (!sdb_done(sqlerr, &retry));
if (sqlerr != SQLITE_DONE) { goto loser;
}
sqlerr = sqlite3_reset(stmt); if (sqlerr != SQLITE_OK) { goto loser;
}
sqlerr = sqlite3_finalize(stmt); if (sqlerr != SQLITE_OK) { goto loser;
}
sdb_p->numSchemaAttrs = backedAttrs;
/* Sort these once so we can shortcut invalid attribute searches. */
qsort(sdb_p->schemaAttrs, sdb_p->numSchemaAttrs, sizeof(CK_ATTRIBUTE_TYPE), sdb_attributeComparator);
}
/* invariant fields */
sdb_p->sqlDBName = PORT_Strdup(dbname);
sdb_p->type = type;
sdb_p->table = table;
sdb_p->cacheTable = cacheTable;
sdb_p->lastUpdateTime = now; /* set the cache delay time. This is how long we will wait before we
* decide the existing cache is stale. Currently set to 10 sec */
sdb_p->updateInterval = PR_SecondsToInterval(10);
sdb_p->dbMon = PR_NewMonitor(); /* these fields are protected by the lock */
sdb_p->sqlXactDB = NULL;
sdb_p->sqlXactThread = NULL;
sdb->private = sdb_p;
sdb->version = 1;
sdb->sdb_flags = inFlags | SDB_HAS_META;
sdb->app_private = NULL;
sdb->sdb_FindObjectsInit = sdb_FindObjectsInit;
sdb->sdb_FindObjects = sdb_FindObjects;
sdb->sdb_FindObjectsFinal = sdb_FindObjectsFinal;
sdb->sdb_GetAttributeValue = sdb_GetAttributeValue;
sdb->sdb_SetAttributeValue = sdb_SetAttributeValue;
sdb->sdb_CreateObject = sdb_CreateObject;
sdb->sdb_DestroyObject = sdb_DestroyObject;
sdb->sdb_GetMetaData = sdb_GetMetaData;
sdb->sdb_PutMetaData = sdb_PutMetaData;
sdb->sdb_DestroyMetaData = sdb_DestroyMetaData;
sdb->sdb_Begin = sdb_Begin;
sdb->sdb_Commit = sdb_Commit;
sdb->sdb_Abort = sdb_Abort;
sdb->sdb_Reset = sdb_Reset;
sdb->sdb_Close = sdb_Close;
sdb->sdb_SetForkState = sdb_SetForkState;
sdb->sdb_GetNewObjectID = sdb_GetNewObjectID;
/* how long does it take to test for a non-existant file in our working
* directory? Allows us to test if we may be on a network file system */
accessOps = 1;
{ char *env;
env = PR_GetEnvSecure("NSS_SDB_USE_CACHE"); /* If the environment variable is undefined or set to yes or no, *sdb_init()willignorethevalueofaccessOps,andwecanskipthe
* measuring.*/ if (env && PORT_Strcasecmp(env, "no") != 0 &&
PORT_Strcasecmp(env, "yes") != 0) {
accessOps = sdb_measureAccess(directory);
}
}
loser: if (cert) {
sqlite3_free(cert);
} if (key) {
sqlite3_free(key);
}
if (error != CKR_OK) { /* currently redundant, but could be necessary if more code is added
* just before loser */ if (keydb && *keydb) {
sdb_Close(*keydb);
} if (certdb && *certdb) {
sdb_Close(*certdb);
}
}
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.98Bemerkung:
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.