When doing cert traversals, create the certs in full as they are found. This avoids thrashing the softoken's object cache when the database has a large number of certs.
authorian.mcgreer%sun.com
Fri, 26 Apr 2002 14:34:05 +0000
changeset 3068 63d0bfc79acf52d0ae7cb421d7b8d822c3bc5f46
parent 3067 5533199693be2578d81a54f43f8fb51a1939e39d
child 3072 5f36d5505c66fc24b7b427c93e8000c29d9b379c
child 3077 df3fa49730ec1f16356fd5217a9346cd6cd87276
push idunknown
push userunknown
push dateunknown
When doing cert traversals, create the certs in full as they are found. This avoids thrashing the softoken's object cache when the database has a large number of certs.
security/nss/lib/dev/dev.h
security/nss/lib/dev/devtoken.c
security/nss/lib/pki/pkibase.c
security/nss/lib/pki/pkim.h
security/nss/lib/pki/trustdomain.c
--- a/security/nss/lib/dev/dev.h
+++ b/security/nss/lib/dev/dev.h
@@ -947,13 +947,23 @@ nssToken_GetTrustOrder
 );
 
 NSS_EXTERN PRStatus
 nssToken_NofifyCertsNotVisible
 (
   NSSToken *tok
 );
 
+NSS_EXTERN PRStatus
+nssToken_TraverseCertificates
+(
+  NSSToken *token,
+  nssSession *sessionOpt,
+  nssTokenSearchType searchType,
+  PRStatus (* callback)(nssCryptokiObject *instance, void *arg),
+  void *arg
+);
+
 #endif
 
 PR_END_EXTERN_C
 
 #endif /* DEV_H */
--- a/security/nss/lib/dev/devtoken.c
+++ b/security/nss/lib/dev/devtoken.c
@@ -1542,8 +1542,118 @@ NSS_IMPLEMENT PRBool
 nssToken_IsPresent
 (
   NSSToken *token
 )
 {
     return nssSlot_IsTokenPresent(token->slot);
 }
 
+/* Sigh.  The methods to find objects declared above cause problems with
+ * the low-level object cache in the softoken -- the objects are found in 
+ * toto, then one wave of GetAttributes is done, then another.  Having a 
+ * large number of objects causes the cache to be thrashed, as the objects 
+ * are gone before there's any chance to ask for their attributes.
+ * So, for now, bringing back traversal methods for certs.  This way all of 
+ * the cert's attributes can be grabbed immediately after finding it,
+ * increasing the likelihood that the cache takes care of it.
+ */
+NSS_IMPLEMENT PRStatus
+nssToken_TraverseCertificates
+(
+  NSSToken *token,
+  nssSession *sessionOpt,
+  nssTokenSearchType searchType,
+  PRStatus (* callback)(nssCryptokiObject *instance, void *arg),
+  void *arg
+)
+{
+    CK_RV ckrv;
+    CK_ULONG count;
+    CK_OBJECT_HANDLE *objectHandles;
+    CK_ATTRIBUTE_PTR attr;
+    CK_ATTRIBUTE cert_template[2];
+    CK_ULONG ctsize;
+    NSSArena *arena;
+    PRStatus status;
+    PRUint32 arraySize, numHandles;
+    nssCryptokiObject **objects;
+    void *epv = nssToken_GetCryptokiEPV(token);
+    nssSession *session = (sessionOpt) ? sessionOpt : token->defaultSession;
+
+    /* template for all certs */
+    NSS_CK_TEMPLATE_START(cert_template, attr, ctsize);
+    if (searchType == nssTokenSearchType_SessionOnly) {
+	NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_false);
+    } else if (searchType == nssTokenSearchType_TokenOnly ||
+               searchType == nssTokenSearchType_TokenForced) {
+	NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_true);
+    }
+    NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_CLASS, &g_ck_class_cert);
+    NSS_CK_TEMPLATE_FINISH(cert_template, attr, ctsize);
+
+    /* the arena is only for the array of object handles */
+    arena = nssArena_Create();
+    if (!arena) {
+	return PR_FAILURE;
+    }
+    arraySize = OBJECT_STACK_SIZE;
+    numHandles = 0;
+    objectHandles = nss_ZNEWARRAY(arena, CK_OBJECT_HANDLE, arraySize);
+    if (!objectHandles) {
+	goto loser;
+    }
+    nssSession_EnterMonitor(session); /* ==== session lock === */
+    /* Initialize the find with the template */
+    ckrv = CKAPI(epv)->C_FindObjectsInit(session->handle, 
+                                         cert_template, ctsize);
+    if (ckrv != CKR_OK) {
+	nssSession_ExitMonitor(session);
+	goto loser;
+    }
+    while (PR_TRUE) {
+	/* Issue the find for up to arraySize - numHandles objects */
+	ckrv = CKAPI(epv)->C_FindObjects(session->handle, 
+	                                 objectHandles + numHandles, 
+	                                 arraySize - numHandles, 
+	                                 &count);
+	if (ckrv != CKR_OK) {
+	    nssSession_ExitMonitor(session);
+	    goto loser;
+	}
+	/* bump the number of found objects */
+	numHandles += count;
+	if (numHandles < arraySize) {
+	    break;
+	}
+	/* the array is filled, double it and continue */
+	arraySize *= 2;
+	objectHandles = nss_ZREALLOCARRAY(objectHandles, 
+	                                  CK_OBJECT_HANDLE, 
+	                                  arraySize);
+	if (!objectHandles) {
+	    nssSession_ExitMonitor(session);
+	    goto loser;
+	}
+    }
+    ckrv = CKAPI(epv)->C_FindObjectsFinal(session->handle);
+    nssSession_ExitMonitor(session); /* ==== end session lock === */
+    if (ckrv != CKR_OK) {
+	goto loser;
+    }
+    if (numHandles > 0) {
+	objects = create_objects_from_handles(token, session,
+	                                      objectHandles, numHandles);
+	if (objects) {
+	    nssCryptokiObject **op;
+	    for (op = objects; *op; op++) {
+		status = (*callback)(*op, arg);
+	    }
+	    nss_ZFreeIf(objects);
+	}
+    }
+    nssArena_Destroy(arena);
+    return PR_SUCCESS;
+loser:
+    nssArena_Destroy(arena);
+    return PR_FAILURE;
+}
+
--- a/security/nss/lib/pki/pkibase.c
+++ b/security/nss/lib/pki/pkibase.c
@@ -711,17 +711,17 @@ find_object_in_collection
 	if (i == MAX_ITEMS_FOR_UID) {
 	    return node;
 	}
 	link = PR_NEXT_LINK(link);
     }
     return (pkiObjectCollectionNode *)NULL;
 }
 
-static PRStatus
+static pkiObjectCollectionNode *
 add_object_instance
 (
   nssPKIObjectCollection *collection,
   nssCryptokiObject *instance
 )
 {
     PRUint32 i;
     PRStatus status;
@@ -735,17 +735,17 @@ add_object_instance
      * instance is already in the collection, and we have nothing to do.
      */
     node = find_instance_in_collection(collection, instance);
     if (node) {
 	/* The collection is assumed to take over the instance.  Since we
 	 * are not using it, it must be destroyed.
 	 */
 	nssCryptokiObject_Destroy(instance);
-	return PR_SUCCESS;
+	return node;
     }
     mark = nssArena_Mark(collection->arena);
     if (!mark) {
 	goto loser;
     }
     status = (*collection->getUIDFromInstance)(instance, uid, 
                                                collection->arena);
     if (status != PR_SUCCESS) {
@@ -775,42 +775,43 @@ add_object_instance
 	}
 	node->haveObject = PR_FALSE;
 	PR_INIT_CLIST(&node->link);
 	PR_INSERT_BEFORE(&node->link, &collection->head);
 	collection->size++;
 	status = PR_SUCCESS;
     }
     nssArena_Unmark(collection->arena, mark);
-    return status;
+    return node;
 loser:
     if (mark) {
 	nssArena_Release(collection->arena, mark);
     }
     nssCryptokiObject_Destroy(instance);
-    return PR_FAILURE;
+    return (pkiObjectCollectionNode *)NULL;
 }
 
 NSS_IMPLEMENT PRStatus
 nssPKIObjectCollection_AddInstances
 (
   nssPKIObjectCollection *collection,
   nssCryptokiObject **instances,
   PRUint32 numInstances
 )
 {
     PRStatus status = PR_SUCCESS;
     PRUint32 i = 0;
+    pkiObjectCollectionNode *node;
     if (instances) {
 	for (; *instances; instances++, i++) {
 	    if (numInstances > 0 && i == numInstances) {
 		break;
 	    }
-	    status = add_object_instance(collection, *instances);
-	    if (status != PR_SUCCESS) {
+	    node = add_object_instance(collection, *instances);
+	    if (node == NULL) {
 		goto loser;
 	    }
 	}
     }
     return status;
 loser:
     /* free the remaining instances */
     for (; *instances; instances++, i++) {
@@ -886,16 +887,45 @@ nssPKIObjectCollection_Traverse
 	                                     callback->arg);
 	    break;
 	}
 	link = PR_NEXT_LINK(link);
     }
     return PR_SUCCESS;
 }
 
+NSS_IMPLEMENT PRStatus
+nssPKIObjectCollection_AddInstanceAsObject
+(
+  nssPKIObjectCollection *collection,
+  nssCryptokiObject *instance
+)
+{
+    pkiObjectCollectionNode *node;
+    node = add_object_instance(collection, instance);
+    if (node == NULL) {
+	return PR_FAILURE;
+    }
+    if (!node->haveObject) {
+	node->object = (*collection->createObject)(node->object);
+	node->haveObject = PR_TRUE;
+    }
+#ifdef NSS_3_4_CODE
+    else {
+	/* The instance was added to a pre-existing node.  This
+	 * function is *only* being used for certificates, and having
+	 * multiple instances of certs in 3.X requires updating the
+	 * CERTCertificate.
+	 */
+	STAN_ForceCERTCertificateUpdate((NSSCertificate *)node->object);
+    }
+#endif
+    return PR_SUCCESS;
+}
+
 /*
  * Certificate collections
  */
 
 static void
 cert_destroyObject(nssPKIObject *o)
 {
     NSSCertificate *c = (NSSCertificate *)o;
--- a/security/nss/lib/pki/pkim.h
+++ b/security/nss/lib/pki/pkim.h
@@ -491,16 +491,28 @@ nssPKIObjectCollection_AddInstances
  */
 NSS_EXTERN PRStatus
 nssPKIObjectCollection_Traverse
 (
   nssPKIObjectCollection *collection,
   nssPKIObjectCallback *callback
 );
 
+/* This function is being added for NSS 3.5.  It corresponds to the function
+ * nssToken_TraverseCertificates.  The idea is to use the collection during
+ * a traversal, creating certs each time a new instance is added for which
+ * a cert does not already exist.
+ */
+NSS_EXTERN PRStatus
+nssPKIObjectCollection_AddInstanceAsObject
+(
+  nssPKIObjectCollection *collection,
+  nssCryptokiObject *instance
+);
+
 /* nssPKIObjectCollection_GetCertificates
  *
  * Get all of the certificates in the collection. 
  */
 NSS_EXTERN NSSCertificate **
 nssPKIObjectCollection_GetCertificates
 (
   nssPKIObjectCollection *collection,
--- a/security/nss/lib/pki/trustdomain.c
+++ b/security/nss/lib/pki/trustdomain.c
@@ -1030,16 +1030,23 @@ NSSTrustDomain_FindUserCertificatesForEm
   PRUint32 rvLimit, /* zero for no limit */
   NSSArena *arenaOpt
 )
 {
     nss_SetError(NSS_ERROR_NOT_FOUND);
     return NULL;
 }
 
+static PRStatus
+collector(nssCryptokiObject *instance, void *arg)
+{
+    nssPKIObjectCollection *collection = (nssPKIObjectCollection *)arg;
+    return nssPKIObjectCollection_AddInstanceAsObject(collection, instance);
+}
+
 NSS_IMPLEMENT PRStatus *
 NSSTrustDomain_TraverseCertificates
 (
   NSSTrustDomain *td,
   PRStatus (*callback)(NSSCertificate *c, void *arg),
   void *arg
 )
 {
@@ -1047,16 +1054,17 @@ NSSTrustDomain_TraverseCertificates
     NSSToken *token = NULL;
     NSSSlot **slots = NULL;
     NSSSlot **slotp;
     nssPKIObjectCollection *collection = NULL;
     nssPKIObjectCallback pkiCallback;
     nssUpdateLevel updateLevel;
     NSSCertificate **cached = NULL;
     nssList *certList;
+
     certList = nssList_Create(NULL, PR_FALSE);
     if (!certList) return NULL;
     (void *)nssTrustDomain_GetCertsFromCache(td, certList);
     cached = get_certs_from_list(certList);
     collection = nssCertificateCollection_Create(td, cached);
     nssCertificateArray_Destroy(cached);
     nssList_Destroy(certList);
     if (!collection) {
@@ -1068,40 +1076,33 @@ NSSTrustDomain_TraverseCertificates
 	goto loser;
     }
     /* iterate over the slots */
     for (slotp = slots; *slotp; slotp++) {
 	/* get the token for the slot, if present */
 	token = nssSlot_GetToken(*slotp);
 	if (token) {
 	    nssSession *session;
-	    nssCryptokiObject **instances;
 	    nssTokenSearchType tokenOnly = nssTokenSearchType_TokenOnly;
 	    /* get a session for the token */
 	    session = nssTrustDomain_GetSessionForToken(td, token);
 	    if (!session) {
 		nssToken_Destroy(token);
 		goto loser;
 	    }
 	    /* perform the traversal */
-	    instances = nssToken_FindCertificates(token,
-	                                          session,
-	                                          tokenOnly,
-	                                          0, &status);
+	    status = nssToken_TraverseCertificates(token,
+	                                           session,
+	                                           tokenOnly,
+	                                           collector,
+	                                           collection);
 	    nssToken_Destroy(token);
 	    if (status != PR_SUCCESS) {
 		goto loser;
 	    }
-	    /* add the found certificates to the collection */
-	    status = nssPKIObjectCollection_AddInstances(collection, 
-	                                                 instances, 0);
-	    nss_ZFreeIf(instances);
-	    if (status != PR_SUCCESS) {
-		goto loser;
-	    }
 	}
     }
 
     /* Traverse the collection */
     pkiCallback.func.cert = callback;
     pkiCallback.arg = arg;
     status = nssPKIObjectCollection_Traverse(collection, &pkiCallback);
     /* clean up */