gecko-dev/security/nss/lib/base/error.c

270 строки
6.4 KiB
C

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
* error.c
*
* This file contains the code implementing the per-thread error
* stacks upon which most NSS routines report their errors.
*/
#ifndef BASE_H
#include "base.h"
#endif /* BASE_H */
#include <limits.h> /* for UINT_MAX */
#include <string.h> /* for memmove */
#define NSS_MAX_ERROR_STACK_COUNT 16 /* error codes */
/*
* The stack itself has a header, and a sequence of integers.
* The header records the amount of space (as measured in stack
* slots) already allocated for the stack, and the count of the
* number of records currently being used.
*/
struct stack_header_str {
PRUint16 space;
PRUint16 count;
};
struct error_stack_str {
struct stack_header_str header;
PRInt32 stack[1];
};
typedef struct error_stack_str error_stack;
/*
* error_stack_index
*
* Thread-private data must be indexed. This is that index.
* See PR_NewThreadPrivateIndex for more information.
*
* Thread-private data indexes are in the range [0, 127].
*/
#define INVALID_TPD_INDEX UINT_MAX
static PRUintn error_stack_index = INVALID_TPD_INDEX;
/*
* call_once
*
* The thread-private index must be obtained (once!) at runtime.
* This block is used for that one-time call.
*/
static PRCallOnceType error_call_once;
/*
* error_once_function
*
* This is the once-called callback.
*/
static PRStatus
error_once_function(void)
{
return PR_NewThreadPrivateIndex(&error_stack_index, PR_Free);
}
/*
* error_get_my_stack
*
* This routine returns the calling thread's error stack, creating
* it if necessary. It may return NULL upon error, which implicitly
* means that it ran out of memory.
*/
static error_stack *
error_get_my_stack(void)
{
PRStatus st;
error_stack *rv;
PRUintn new_size;
PRUint32 new_bytes;
error_stack *new_stack;
if (INVALID_TPD_INDEX == error_stack_index) {
st = PR_CallOnce(&error_call_once, error_once_function);
if (PR_SUCCESS != st) {
return (error_stack *)NULL;
}
}
rv = (error_stack *)PR_GetThreadPrivate(error_stack_index);
if ((error_stack *)NULL == rv) {
/* Doesn't exist; create one */
new_size = 16;
} else if (rv->header.count == rv->header.space &&
rv->header.count < NSS_MAX_ERROR_STACK_COUNT) {
/* Too small, expand it */
new_size = PR_MIN(rv->header.space * 2, NSS_MAX_ERROR_STACK_COUNT);
} else {
/* Okay, return it */
return rv;
}
new_bytes = (new_size * sizeof(PRInt32)) + sizeof(error_stack);
/* Use NSPR's calloc/realloc, not NSS's, to avoid loops! */
new_stack = PR_Calloc(1, new_bytes);
if ((error_stack *)NULL != new_stack) {
if ((error_stack *)NULL != rv) {
(void)nsslibc_memcpy(new_stack, rv, rv->header.space);
}
new_stack->header.space = new_size;
}
/* Set the value, whether or not the allocation worked */
PR_SetThreadPrivate(error_stack_index, new_stack);
return new_stack;
}
/*
* The error stack
*
* The public methods relating to the error stack are:
*
* NSS_GetError
* NSS_GetErrorStack
*
* The nonpublic methods relating to the error stack are:
*
* nss_SetError
* nss_ClearErrorStack
*
*/
/*
* NSS_GetError
*
* This routine returns the highest-level (most general) error set
* by the most recent NSS library routine called by the same thread
* calling this routine.
*
* This routine cannot fail. However, it may return zero, which
* indicates that the previous NSS library call did not set an error.
*
* Return value:
* 0 if no error has been set
* A nonzero error number
*/
NSS_IMPLEMENT PRInt32
NSS_GetError(void)
{
error_stack *es = error_get_my_stack();
if ((error_stack *)NULL == es) {
return NSS_ERROR_NO_MEMORY; /* Good guess! */
}
if (0 == es->header.count) {
return 0;
}
return es->stack[es->header.count - 1];
}
/*
* NSS_GetErrorStack
*
* This routine returns a pointer to an array of integers, containing
* the entire sequence or "stack" of errors set by the most recent NSS
* library routine called by the same thread calling this routine.
* NOTE: the caller DOES NOT OWN the memory pointed to by the return
* value. The pointer will remain valid until the calling thread
* calls another NSS routine. The lowest-level (most specific) error
* is first in the array, and the highest-level is last. The array is
* zero-terminated. This routine may return NULL upon error; this
* indicates a low-memory situation.
*
* Return value:
* NULL upon error, which is an implied NSS_ERROR_NO_MEMORY
* A NON-caller-owned pointer to an array of integers
*/
NSS_IMPLEMENT PRInt32 *
NSS_GetErrorStack(void)
{
error_stack *es = error_get_my_stack();
if ((error_stack *)NULL == es) {
return (PRInt32 *)NULL;
}
/* Make sure it's terminated */
es->stack[es->header.count] = 0;
return es->stack;
}
/*
* nss_SetError
*
* This routine places a new error code on the top of the calling
* thread's error stack. Calling this routine wiht an error code
* of zero will clear the error stack.
*/
NSS_IMPLEMENT void
nss_SetError(PRUint32 error)
{
error_stack *es;
if (0 == error) {
nss_ClearErrorStack();
return;
}
es = error_get_my_stack();
if ((error_stack *)NULL == es) {
/* Oh, well. */
return;
}
if (es->header.count < es->header.space) {
es->stack[es->header.count++] = error;
} else {
memmove(es->stack, es->stack + 1,
(es->header.space - 1) * (sizeof es->stack[0]));
es->stack[es->header.space - 1] = error;
}
return;
}
/*
* nss_ClearErrorStack
*
* This routine clears the calling thread's error stack.
*/
NSS_IMPLEMENT void
nss_ClearErrorStack(void)
{
error_stack *es = error_get_my_stack();
if ((error_stack *)NULL == es) {
/* Oh, well. */
return;
}
es->header.count = 0;
es->stack[0] = 0;
return;
}
/*
* nss_DestroyErrorStack
*
* This routine frees the calling thread's error stack.
*/
NSS_IMPLEMENT void
nss_DestroyErrorStack(void)
{
if (INVALID_TPD_INDEX != error_stack_index) {
PR_SetThreadPrivate(error_stack_index, NULL);
}
return;
}